一个可靠的发送和接受数据函数

 在广域网条件下,客户端与服务器打交道,如果仅仅是简单的send-recv方式,那么潜在的bug是无法避免的,尤其是在一个指令序列不断交互,问题更大了。

例如,你预期的情况是这样的:

C->S: Hello,server.  \r\n

S->C:Hello,client \r\n

C->S:Tell me your name \r\n

S->C:My name is Jimmy \r\n

而实际却得到这样的结果:

C->S: Hello,server \r\n

S->C:Hello,cli

C->S:Tell me your name \r\n

S->C:eint  \r\n My name is Jimmy \r\n

为此,必须充分考虑广域网的阻塞,延迟等非常规情况出现。

  1. #include <iostream>  
  2. #include <assert.h>  
  3. #include <winsock2.h>  
  4.   
  5. #pragma comment(lib, "ws2_32.lib")  
  6.   
  7. using namespace std;  
  8.   
  9. class CMySocket  
  10. {  
  11. public:  
  12.     CMySocket(){};  
  13.     ~CMySocket(){};  
  14.     bool  SendData();  
  15.     bool  ReceiveData();  
  16. private:  
  17.     SOCKET  m_socket;  
  18.     char*   m_dataBuf;  
  19.     int     m_dataLen;  
  20.     int     m_waitSeconds;  
  21. };  
  22.   
  23.   
  24. bool CMySocket::SendData()  
  25. {  
  26.     int idx = 0,res;  
  27.     int nLeft=m_dataLen;  
  28.   
  29.     if(m_dataLen==0){  
  30.         return false;  
  31.     }  
  32.   
  33.     fd_set  fdwrite;  
  34.     timeval time;  
  35.   
  36.     time.tv_sec = m_waitSeconds;  
  37.     time.tv_usec = 0;  
  38.   
  39.     while(1)  
  40.     {  
  41.         FD_ZERO(&fdwrite);  
  42.   
  43.         FD_SET(m_socket,&fdwrite);  
  44.   
  45.         if((res = select(m_socket+1,NULL,&fdwrite,NULL,&time)) == SOCKET_ERROR)  
  46.         {  
  47.             FD_CLR(m_socket,&fdwrite);  
  48.             return false;  
  49.         }  
  50.   
  51.         if(!res)//timeout  
  52.         {  
  53.             FD_CLR(m_socket,&fdwrite);  
  54.             return false;  
  55.         }  
  56.   
  57.         if(res && FD_ISSET(m_socket,&fdwrite))  
  58.         {  
  59.             if(nLeft > 0)  
  60.             {  
  61.                 if((res = send(m_socket,&m_dataBuf[idx],nLeft,0)) == SOCKET_ERROR)  
  62.                 {  
  63.                     FD_CLR(m_socket,&fdwrite);  
  64.                 }  
  65.                 if(!res)  
  66.                     break;  
  67.                 nLeft -= res;  
  68.                 idx += res;  
  69.             }  
  70.             else  
  71.                 break;  
  72.         }  
  73.     }  
  74.   
  75.     FD_CLR(m_socket,&fdwrite);  
  76.   
  77.     return true;  
  78. }  
  79.   
  80.   
  81. bool CMySocket::ReceiveData()  
  82. {  
  83.     int res,i = 0;  
  84.     fd_set fdread;  
  85.     timeval time;  
  86.   
  87.     time.tv_sec = m_waitSeconds;  
  88.     time.tv_usec = 0;  
  89.   
  90.     assert(m_dataBuf);  
  91.       
  92.     if(m_dataBuf == NULL)  
  93.         return false;  
  94.   
  95.     while(1)  
  96.     {  
  97.         FD_ZERO(&fdread);  
  98.   
  99.         FD_SET(m_socket,&fdread);  
  100.   
  101.         if((res = select(m_socket+1, &fdread, NULL, NULL, &time)) == SOCKET_ERROR)  
  102.         {  
  103.             FD_CLR(m_socket,&fdread);  
  104.             return false;  
  105.         }  
  106.   
  107.         if(!res)//timeout  
  108.         {  
  109.             FD_CLR(m_socket,&fdread);  
  110.             return false;  
  111.         }  
  112.   
  113.         if(res && FD_ISSET(m_socket,&fdread))  
  114.         {  
  115.             if(i >= m_dataLen)  
  116.             {  
  117.                 FD_CLR(m_socket,&fdread);  
  118.                 return false;//lack of memory  
  119.             }  
  120.             if(recv(m_socket,&m_dataBuf[i++],1,0) == SOCKET_ERROR)  
  121.             {  
  122.                 FD_CLR(m_socket,&fdread);  
  123.                 return false;  
  124.             }  
  125.             if(m_dataBuf[i-1]=='\n')  
  126.             {  
  127.                 m_dataBuf[i] = '\0';  
  128.                 break;  
  129.             }  
  130.         }  
  131.     }  
  132.   
  133.     FD_CLR(m_socket,&fdread);  
  134.     return true;  
  135. }  


注解:

select函数用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。用fd_set结构来表示一组等待检查的套接口。

原型

int select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
);

nfds:本参数忽略,仅起到兼容作用。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。

   FD_CLR(s,*set):从集合set中删除描述字s。
   FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。
   FD_SET(s,*set):向集合添加描述字s。
   FD_ZERO(*set):将set初始化为空集NULL。

 

返回值:


   select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

错误代码:
   WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
   WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
   WSAEINVAL:超时时间值非法。
   WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
   WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
   WSAENOTSOCK:描述字集合中包含有非套接口的元素。


附带别人的一篇文章:

  1. /* 
  2.  
  3. Socket中如何设置连接超时【转】 
  4.  
  5. http://fcxz.blogbus.com/logs/1564481.html 
  6.  
  7.  
  8. 设置connect的超时很简单,CSDN上也有人提到过使用select,但却没有一个令人满意与完整的答案。 
  9. 偶所讲的也正是select函数,此函数集成在winsock1.1中, 
  10. 简单点讲,"作用使那些想避免在套接字调用过程中被锁定的应用程序,采取一种有序的方式,同时对多个套接字进行管理"(《Windows网络编程技术》原话)。 
  11. 使用方法与解释请见《Windows网络编程技术》。 
  12.    
  13. 在使用此函数前,需先将socket设置为非锁定模式,这样,在connect时,才会立马跳过, 
  14. 同时,通常也会产生一个WSAEWOULDBLOCK错误,这个错误没关系。再执行select则是真正的超时。 
  15.  
  16. */  
  17.   
  18. void main()  
  19. {  
  20.   
  21.     WSADATA wsd;  
  22.     SOCKET cClient;  
  23.     int ret;  
  24.     struct sockaddr_in server;  
  25.     hostent *host=NULL;  
  26.   
  27.     if(WSAStartup(MAKEWORD(2,0),&wsd)){return 0;}  
  28.     cClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);  
  29.       
  30.     if(cClient==INVALID_SOCKET){  
  31.         return 0;  
  32.     }  
  33.   
  34.     //set Recv and Send time out  
  35.     int TimeOut=6000; //设置发送超时6秒  
  36.       
  37.     if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){  
  38.         return 0;  
  39.     }  
  40.   
  41.     TimeOut=6000;//设置接收超时6秒  
  42.     if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){  
  43.         return 0;  
  44.     }  
  45.   
  46.     //设置非阻塞方式连接  
  47.     unsigned long ul = 1;  
  48.     ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);  
  49.       
  50.     if(ret==SOCKET_ERROR)  
  51.         return 0;  
  52.   
  53.     //连接  
  54.     server.sin_family = AF_INET;  
  55.     server.sin_port = htons(25);  
  56.     server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);  
  57.     if(server.sin_addr.s_addr == INADDR_NONE){  
  58.         return 0;  
  59.     }  
  60.   
  61.     connect(cClient,(const struct sockaddr *)&server,sizeof(server));  
  62.   
  63.     //select模型,即设置超时  
  64.     struct timeval timeout ;  
  65.     fd_set r;  
  66.   
  67.     FD_ZERO(&r);  
  68.     FD_SET(cClient,&r);  
  69.     timeout.tv_sec = 15; //连接超时15秒  
  70.     timeout.tv_usec =0;  
  71.     ret = select(0, 0,&r, 0,&timeout);  
  72.     if ( ret<= 0 )  
  73.     {  
  74.         ::closesocket(cClient);  
  75.         return 0;  
  76.     }  
  77.   
  78.     //一般非锁定模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式  
  79.     unsigned long ul1= 0 ;  
  80.     ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);  
  81.     if(ret==SOCKET_ERROR){  
  82.         ::closesocket (cClient);  
  83.         return 0;  
  84.     }  
  85. }  
  86. http://blog.csdn.net/wangjiannuaa/article/details/6914162
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计,皆可应用在项目、毕业设计、课程设计、期末/期/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值