c++使用socket实现C/S端文件的下载传输
首先是服务器端,大致说下流程:服务器创建线程去处理应答accept(),当接受到客户端连接请求时,首先获取要发送的指定的文件数据总大小给客户端,接着就是循环读取要发送的文件数据流向客户端发送文件数据,每次都判断循环读取到的数据实际大小,当实际读取到的数据总大小为0时,表示文件发送结束。下面是服务器server端实现:
声明部分:
public:
afx_msg void OnBnClickedButton1();
public:
BOOL InitSocket(); //初始化并创建套接字
static DWORD WINAPI ThreadProc(LPVOID lpParameter); //创建线程去执行服务器accept()
实现部分:
void CSendFileServerDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
if (InitSocket())
{
GetDlgItem(IDC_EDIT1)->SetWindowText(_T("服务器开启监听。。。 \r\n"));
//创建线程
HANDLE hThread = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
//关闭该接收线程句柄,释放引用计数
CloseHandle(hThread);
}
}
BOOL CSendFileServerDlg::InitSocket()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return FALSE;
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return FALSE;
}
//创建套接字
//SOCKET m_socket=socket(AF_INET,SOCK_STREAM,0);
m_socket=socket(AF_INET,SOCK_STREAM,0);
if (m_socket == INVALID_SOCKET)
{
AfxMessageBox(_T("套接字创建失败!"));
return FALSE;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(8099);
err = bind(m_socket,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //绑定本地端口
if (err==SOCKET_ERROR)
{
closesocket(m_socket);
AfxMessageBox(_T("绑定失败!"));
return FALSE;
}
listen(m_socket,5);//开启监听
return TRUE;
}
DWORD WINAPI CSendFileServerDlg::ThreadProc(LPVOID lpParameter)
{
SOCKADDR_IN addrClient;
int len = sizeof(SOCKADDR);
while (true)
{
SOCKET sockConn=accept(m_socket,(SOCKADDR*)&addrClient,&len);
CString filename = _T("E:\\test.zip");
HANDLE hFile;
unsigned long long file_size = 0;
char Buffer[1024];
DWORD dwNumberOfBytesRead;
hFile = CreateFile(filename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
file_size = GetFileSize(hFile,NULL);
send(sockConn,(char*)&file_size,sizeof(unsigned long long)+1,NULL);
do
{
::ReadFile(hFile,Buffer,sizeof(Buffer),&dwNumberOfBytesRead,NULL);
::send(sockConn,Buffer,dwNumberOfBytesRead,0);
} while (dwNumberOfBytesRead);
CloseHandle(hFile);
}
return 0;
}
如代码所述 每次发送单位是unsigned char[1024]大小(程序是char 应该为unsigned char[1024])所以就不存在网络字节序问题也不用考虑大端小端什么的。
服务器端暂时不支持多客户端并发访问,后续可能会加上。。。
-------------------------------------------
下面是客户端,同样也大致说下客户端流程,客户端增加手动填写Ip地址和端口号功能(端口号暂为8099)。以及下载传输文件数据进度条的显示,和下面简单的一些状态显示。客观端由填写的IP地址进行连接服务器操作,如果客户端连接服务器成功的话直接就会获取服务器端发送的要发送的文件数据的总大小,如果获取文件总大小>0 则会循环往指定的路径写数据啦。此处循环写文件结束标志,我是用每次实际写的累加如果累计值等于从服务器端获取的文件总大小的话表示下载文件数据成功,结束循环。大致是这样一个过程。代码实现:
客户端声明部分:
?
public:
afx_msg void OnBnClickedButton1();
BOOL InitSocket();
void ConnectServer();
void ConnectRecvFileData(DWORD ip,int port);
private:
CProgressCtrl *m_progress; //进度条
进度条在OnInitDialog()里初始化:
BOOL CRecvFileClientDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_progress = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1);
补充:软件开发 , C++ ,