strTransfersSize.Format(\字节\
GetDlgItem(IDC_RECEIVE_SIZE)->SetWindowText(strTransfersSize);
int p=((double)dwCount)/((int)m_dwFileSize+1)*100+1; strTransfersSize.Format(\ strTransfersSize+=\
GetDlgItem(IDC_PECENT)->SetWindowText(strTransfersSize) //用户是否停止了接收 if(m_bIsStop) {
m_bIsStop=FALSE; break; } }
file.Close(); recSo.Close();
if(m_dwFileSize==dwCount)
AfxMessageBox(_T(\文件接收成功\ else
AfxMessageBox(_T(\文件接收失败\ m_ctrlProgress.SetPos(0); m_bIsTransmitting = FALSE; }
六、关键问题及其解决方法
1、在传输文件前、中、后能让程序响应各类控制消息
CSocket、CSocketFile、CArchive三个类的联动,完美地解决了这个问题,在传输文件的过程中,若一方有事件发生(暂停传输、关闭连接等等),则改变预先设定的状态值,SendMsg给CSocketFile,触发对方的虚函数OnReceive,从而解决通信问题。
集成后的互通消息代码很简单: void Serialize(CArchive& ar){ if(ar.IsStoring()){ ar << m_nType; ar << m_strFileName; ar << m_dwFileSize; } else { ar >> m_nType; ar >> m_strFileName; ar >> m_dwFileSize; } } 2、解决单线程传输阻塞的非“非阻塞”式方法 文件的传输时阻塞式的,所以在主线程中直接传输文件必然导致界面卡死,所以这里我用到了多线程技术,为传输文件和接受文件分别开设线程。 在使用AfxBeginThread实现多线程的同时,也增加了诸如发送超时、接收超时等判断(使用Windows定时器SetTimer响应OnTime来实现),这一定程度上增强了程序的鲁棒性
① _ListenThread
UINT _ListenThread(LPVOID lparam) { CSendFileDlg* pDlg=(CSendFileDlg*)lparam; //创建服务器端套接字 CSocket sockSrvr; if(!sockSrvr.Create(pDlg->m_wPort+PORT)) { pDlg->TransfersFailed(); ::MessageBox((HWND)lparam,
pDlg->GetError(GetLastError()), _T(\错误\MB_ICONHAND|MB_OK); return -1; } if(!sockSrvr.Listen()) //监听 { pDlg->TransfersFailed(); ::MessageBox((HWND)lparam,
pDlg->GetError(GetLastError()), _T(\错误\MB_ICONHAND|MB_OK); return -1; } //接收套接字已经创建向主对话框发送自定义消息,该消息发送一个消息给发送方,可以开始传数据 pDlg->SendMessage(WM_ACCEPT_TRANSFERS);
CSocket recSo; if(!sockSrvr.Accept(recSo)) { ::MessageBox((HWND)lparam,
pDlg->GetError(GetLastError()), _T(\错误\MB_ICONHAND|MB_OK); return -1; } sockSrvr.Close(); //调用主对话框类中的ReceiveFile成员函数进行文件的接受 pDlg->ReceiveFile(recSo); return 0; }
② _SendThread
UINT _SendThread(LPVOID lparam) { CSendFileDlg* pDlg=(CSendFileDlg*)lparam; //创建套接字 CSocket sockClient; if(!sockClient.Create()) { pDlg->TransfersFailed(); ::MessageBox((HWND)lparam,
pDlg->GetError(GetLastError()), _T(\错误\MB_ICONHAND|MB_OK); return -1; } CString strIPAddress; UINT nPort; //获取对方地址 pDlg->m_psockClient->GetPeerName(strIPAddress,nPort); //连接服务器 if(!sockClient.Connect(strIPAddress,pDlg->m_wPort+PORT)) { pDlg->TransfersFailed(); ::MessageBox((HWND)lparam,
pDlg->GetError(GetLastError()), _T(\错误\MB_ICONHAND|MB_OK); return -1;
} //调用主对话框类进行文件发送 pDlg->SendFile(sockClient); return 0; }
3、在文件传输前,提前让对方了解文件信息,以便对方判断是否接受文件。 由于本工程实现了“文件传输与两端消息响应”的明确划分,该功
能实现相当简单,只需要在发送端SelectFile之后向接收端发送一个“准备发送”的消息(该消息由于CMessage的封装,包含了文件的基本消息),该消息被接收后弹出对话框,由接收方来选择是否接受。
接收端由接收方选择对话框选项,根据MessageBox返回值IDOK或
是IDCANCLE,若为IDOK,则发送一个Accpet消息给发送方,否则发送一个REFUSE回去,均由发送方的OnReceive虚函数处理,大致过程如下:
七、设计结果
1、服务器客户端的选择(服务器客户端集成)

