socket传输文件的问题?
最近刚开始学socket编程,看书加上网,也看了不少代码,
照别人的模式弄了一个C/S结构的,TCP协议的
服务器端采用的是异步非阻塞WSAAsyncSelect(),客户端用的是阻塞模式,
结果发文件的时候总是服务器端迅速显示发送完毕,而客户端却只接收了一部分数据
后来在服务器端加了个Sleep(nt),当nt值稍大的时候,传输可以成功。send和recv代码部分:
- C/C++ code
////服务器端void CServerDlg::OnSend(){ if( !( bFileIsOpen = sourceFile.Open( sendfname,CFile::modeRead | CFile::typeBinary, &fe ) ) ) { AfxMessageBox(_T("ERR")); CleanUp(); } //首先传递将要被发送文件的长度给接收端 fileLength = sourceFile.GetLength(); fileLength = htonl( fileLength );//似乎没有必要,只要两边都不转换也可以吧? int nLeftToSend = sizeof( fileLength ); int nSent=0; do { BYTE* bp = (BYTE*)(&fileLength) + nSent; int ns = send(socketsend, (const char*)bp, nLeftToSend,0 ); if ( ns == SOCKET_ERROR ) { AfxMessageBox(_T("ERR")); CleanUp(); } nLeftToSend - = ns; nSent + = ns; } while ( nLeftToSend>0 ); //开始发送文件数据 char* sendData = new char[1024]; nLeftToSend = sourceFile.GetLength(); do { int sendThisTime, doneSoFar, buffOffset; sendThisTime = sourceFile.Read( sendData, 1024); buffOffset = 0; do { doneSoFar = send(socketsend,(const char*) (sendData + buffOffset),sendThisTime,0 ); if ( doneSoFar == SOCKET_ERROR ) { AfxMessageBox(_T("ERR")); CleanUp(); } buffOffset += doneSoFar; sendThisTime -= doneSoFar; nLeftToSend -= doneSoFar; } while ( sendThisTime > 0 ); Sleep(10);//这是后来我自己加的,有时能起作用 } while ( nLeftToSend > 0 ); ::PostMessage(hwnd,UM_PRGSOVER,0,0);//向主窗口发送消息发送完毕 \ CleanUp();}////客户端void CClientDlg::OnReceive(){ //文件已建立好 //首先接收文件的长度信息 nLeftToReceive = sizeof( dataLength ); nReceived = 0; do { BYTE* bp = (BYTE*)(&dataLength) + nReceived; int nr = recv(sockConn, (char*)bp, nLeftToReceive,0); if ( nr == SOCKET_ERROR || nr == 0 ) { AfxMessageBox(_T("ERR")); CleanUp(); } nLeftToReceive - = nr; nReceived + = nr; } while ( nLeftToReceive > 0 ); dataLength = ntohl( dataLength ); //将文件的长度信息转化为本地字节序,服务器端是相反转换 //准备开始接收文件 recdData = new byte[RECV_BUFFER_SIZE]; nLeftToReceive = dataLength; do { int iiGet, iiRecd; iiGet = (nLeftToReceive<RECV_BUFFER_SIZE) ? nLeftToReceive : RECV_BUFFER_SIZE ; nr = recv(sockConn, (char*)recdData, iiGet,0 ); if ( br == SOCKET_ERROR || nr == 0 ) { AfxMessageBox(_T("ERR")); CleanUp(); } destFile.Write( recdData, nr ); nLeftToReceive - = nr; } while ( nLeftToReceive > 0 ); ::PostMessage(hwnd,UM_PRGROVER,0,0); //向主窗口发送消息 CleanUp(); }现在请教这么几个问题:
1、收发文件时,同时兼顾速度和成功率,大家一般是怎么处理的?客户端和服务器端采用什么模式较好?
2、还有,我在客户端也想用异步非阻塞模式,可是connect的时候总显示连不上服务器,
但是服务器端有反应,是怎么回事?
3、socket传输到底是什么机制?服务器端执行一次send(),客户端端是不是一定能受到FD_READ消息?
我头都昏了,请大家指教!
[解决办法]
第一个问题:对于阻塞模式的套接字,我采用应答模式,就是客户端(服务器)在收到数据后,给服务器(客户端)发一个确认,客户端(服务器)再继续发送数据。
第二个问题:你估计是没有好好看MSDN的说明,在MSDN里有说明,就是采用异步模式的套接字,在连接的时候一般都会由于函数要马上返回,而与对方的连接没有来得及完成,所以会产生一个WSAEWOULDBLOCK错误。原文如下:
With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, WSAConnect will return SOCKET_ERROR and WSAGetLastError will return WSAEWOULDBLOCK.
第三个问题:FD_READ消息是在前一次的数据被接收(recv)之后才会产生的。
[解决办法]
补充:
第二个问题:当你Connect返回错误了,可以用WSAGetLastError判断错误是不是WSAEWOULDBLOCK,如果是的话,那么连接并不是失败了,这可能再过一会就连接上了,如果不是,那么就是连接失败了。在MSDN里源码,你可以参考
[解决办法]
你这样的丢包,一般都是丢在处理上的,也就是数据发来了,你没有写进去。和模型无关。
[解决办法]
建议参考下飞鸽的代码
[解决办法]
select 适用客户端 或者小型的聊天室之类的 (只支持64个连接 当然是有技术可以突破64的)
WSAAsyncselect 同上 但是基于消息机制实现 可以用win32或者mfc实现
WSAEventSelect 同上 基于windows的事件机制实现
OverLapped IO 基于事件通知 没有连接限制 服务器可以用此模型
Completion Port windows系统 高性能的服务器模型首选 也最复杂
........
[解决办法]
根据自己的实践哈(双端都使用1.1阻塞套接字)。发送方和接收方应协商一个相同的发送/接收字节数,例如都是4096或1024,关键的地方是保证发送方一定能将目标字节数完全发送、接收方一定能完整接收目标字节,具体方法就是使用循环,如果发送/接收的字节数不到目标字节数,那么就持续发送/接收。另外就是,如果传输过程中有SOCKET_ERROR(貌似这个返回值很恶心,不同于传输超时),这时不宜继续传输,稳妥点的做法是将SOCKET_ERROR看作异常,捕获这个异常后结束传输线程,若考虑效率,可以考虑引入简单的断点续传。