首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VC/MFC >

socket传输文件的有关问题

2012-03-18 
socket传输文件的问题?最近刚开始学socket编程,看书加上网,也看了不少代码,照别人的模式弄了一个C/S结构的

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看作异常,捕获这个异常后结束传输线程,若考虑效率,可以考虑引入简单的断点续传。

热点排行