非阻塞recvfrom的设置

 

       我想用 UDP 阻塞模式给硬件设备发包,然后收包。因为网络的问题,经常丢包,也就是发了之后没有响应。这样的话, recvfrom 会一直停在那里,死机了一样。           能不能设成超时自动返回,或者其它什么解决办法,谢谢!            我不想用非阻塞 模式 ,据说比较耗资源。
 
// 连接超时   
          //--------------------------------------------------------------------------  
          // 设置为非阻塞方式连接   
          unsigned   long   ul   =   1;  
          int   ret   =   ioctlsocket(m_sSocket,   FIONBIO,   (unsigned   long*)&ul);  
          if(ret   ==   SOCKET_ERROR)  
          {  
                  err   =   WSAGetLastError();  
      closesocket(m_sSocket);  
      m_sSocket   =   NULL;  
      return   FALSE;  
          }  
   
 Struct timeval   timeout   ;     //   超时结构   
          fd_set  r;                                              
   
          FD_ZERO(&r);  
          FD_SET(m_sSocket,   &r);  
          timeout.tv_sec   =   iTimeout;       //   连接超时设置   
          timeout.tv_usec   =0;  
   
          connect(m_sSocket,(LPSOCKADDR)&server,sizeof(SOCKADDR));  
          ret   =   select(0,   0,   &r,   0,   &timeout);      
          if   (   ret   <=   0   )  
          {  
      err   =   WSAGetLastError();  
      closesocket(m_sSocket);  
      m_sSocket   =   NULL;  
      return   FALSE;  
          }  
   
    // 设回阻塞模式   
          ul   =   0   ;  
          ret   =   ioctlsocket(m_sSocket,   FIONBIO,   (unsigned   long*)&ul);  
          //--------------------------------------------------------------------------  
   
   
                // 接收超时   
          //-------------------------------------------  
          // 接收超时设置   
          struct                   timeval   outtime   ;     //   超时结构   
   
          FD_SET   fdr   =   {1,   m_sSocket};    
          outtime.tv_sec   =   timeout;      
          outtime.tv_usec   =0;  
          int   nSelectRet;  
          //------------------------------------------------------------  
          // 网络只认单字节串,而 EVC 里多字节 ; 发送的 UNICODE 串转换成单字节串   
          UINT   nLen   =   len   *   2;  
          char   *pByte   =   new   char[nLen+1];  
          memset(pByte,   0,   nLen+1);  
          WideCharToMultiByte(CP_ACP,   NULL,   buf,   wcslen(buf),pByte,   nLen,   NULL,   NULL);  
          int   nRet;  
   
          nSelectRet=::select(0,   &fdr,   NULL,   NULL,   &outtime);   // 检查可读状态     
          if(SOCKET_ERROR==nSelectRet)    
          {  
    err   =   WSAGetLastError();  
    closesocket(m_sSocket);  
    m_sSocket   =   NULL;  
    return   -1;  
          }  
          if(nSelectRet==0)   // 超时发生,无可读数据     
          {  
    AfxMessageBox(L" 接收超时 ");  
    err   =   WSAGetLastError();  
    closesocket(m_sSocket);  
    m_sSocket   =   NULL;  
    return   -1;  
          }  
          else    
          {  
    // 接收数据   
    nRet   =   recv(m_sSocket,   pByte,   nLen,   0);  
    if(nRet   ==   SOCKET_ERROR)  
    {  
      err   =   WSAGetLastError();  
    }  
          }  
   
          //-------------------------------------------  
   
          MultiByteToWideChar(CP_ACP,   NULL,   pByte,   nLen,   buf,   len);  
          delete   []   pByte;  
          pByte   =   NULL;  
 
不知道大家有没有遇到过这种情况,当 socket 进行 TCP 连接的时候(也就是调用 connect 时),一旦网络不通,或者是 ip 地址无效,就可能使整个线程阻塞。一般为 30 秒(我测的是 20 秒)。如果设置为非阻塞模式,能很好的解决这个问题,我们可以这样来设置非阻塞模式:调用 ioctlsocket 函数: unsigned long flag=1; if (ioctlsocket(sock,FIONBIO,&flag)!=0) { closesocket(sock); return false; } 以下是对 ioctlsocket 函数的相关解释: int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp); s :一个标识套接口的描述字。 cmd :对套接口 s 的操作命令。 argp :指向 cmd 命令所带参数的指针。 注释: 本函数可用于任一状态的任一套接口。它用于获取与套接口相关的操作参数,而与具体协议或通讯子系统无关。支持下列命令: FIONBIO :允许或禁止套接口 s 的非阻塞模式。 argp 指向一个无符号长整型。如允许非阻塞模式则非零,如禁止非阻塞模式则为零。当创建一个套接口时,它就处于阻塞模式(也就是说非阻塞模式被禁止)。这与 BSD 套接口是一致的。 WSAAsynSelect() 函数将套接口自动设置为非阻塞模式。如果已对一个套接口进行了 WSAAsynSelect() 操作,则任何用 ioctlsocket() 来把套接口重新设置成阻塞模式的试图将以 WSAEINVAL 失败。为了把套接口重新设置成阻塞模式,应用程序必须首先用 WSAAsynSelect() 调用( IEvent 参数置为 0 )来禁至 WSAAsynSelect() FIONREAD :确定套接口 s 自动读入的数据量。 argp 指向一个无符号长整型,其中存有 ioctlsocket() 的返回值。如果 s SOCKET_STREAM 类型,则 FIONREAD 返回在一次 recv() 中所接收的所有数据量。这通常与套接口中排队的数据总量相同。如果 S SOCK_DGRAM 型,则 FIONREAD 返回套接口上排队的第一个数据报大小。 SIOCATMARK :确实是否所有的带外数据都已被读入。这个命令仅适用于 SOCK_STREAM 类型的套接口,且该套接口已被设置为可以在线接收带外数据( SO_OOBINLINE )。如无带外数据等待读入,则该操作返回 TRUE 真。否则的话返回 FALSE 假,下一个 recv() recvfrom() 操作将检索 标记 前一些或所有数据。应用程序可用 SIOCATMARK 操作来确定是否有数据剩下。如果在 紧急 (带外)数据前有常规数据,则按序接收这些数据(请注意, recv() recvfrom() 操作不会在一次调用中混淆常规数据与带外数据)。 argp 指向一个 BOOL 型数, ioctlsocket() 在其中存入返回值。 此时已经设置非阻塞模式,但是并没有设置 connect 的连接时间,我们可以通过调用 select 语句来实现这个功能。以下代码设定了是连接时间为 5 秒,如果还未能连上,则直接返回。 struct timeval timeout ; fd_set r; int ret; connect( sock, (LPSOCKADDR)sockAddr, sockAddr.Size()); FD_ZERO(&r); FD_SET(sock,&r); timeout.tv_sec = 5; timeout.tv_usec =0; ret = select(0,0,&r,0,&timeout); if ( ret <= 0 ) { closesocket(sock); return false; } 以下是对 select 函数的解释: int select ( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout ); 第一个参数 nfds 沒有用,仅仅为与伯克利 Socket 兼容而提供。 readfds 指定一個 Socket 数组(应该是一个,但这里主要是表现为一个 Socket 数组), select 检查该数组中的所有 Socket 。如果成功返回,则 readfds 中存放的是符合 可读性 条件的数组成员(如缓冲区中有可读的数据)。 writefds 指定一个 Socket 数组, select 检查该数组中的所有 Socket 。如果成功返回,则 writefds 中存放的是符合 可写性 条件的数组成员(如连接成功)。 exceptfds 指定一个 Socket 数组, select 检查该数组中的所有 Socket 。如果成功返回,则 cxceptfds 中存放的是符合 有异常 条件的数组成员(如连接接失败)。 timeout 指定 select 执行的最长时间,如果在 timeout 限定的时间内, readfds writefds exceptfds 中指定的 Socket 沒有一个符合要求,就返回 0 如果对 Connect 进行非阻塞调用,则可读意味着已经成功连接,连接不成功则不可读。所以通过这样的设定,我们就能够实现对 connect 连接时间的修改。但是,应该注意,这样的设置并不能保证在限定时间内连接不上就说明网络不通。比如我们设的时间是 5 秒,但是由于种种原因,可能第 6 秒就能连接上,但是函数在 5 秒后就返回了。
 
非阻塞 recvfrom 的设置
 int iMode = 1; //0 :阻塞
 ioctlsocket(socketc,FIONBIO, (u_long FAR*) &iMode);// 非阻塞设置
 
 rs=recvfrom(socketc,rbuf,sizeof(rbuf),0,(SOCKADDR*)&addr,&len);
 
int ioctlsocket (
 SOCKET s,        
 long cmd,        
 u_long FAR* argp 
);
 
 
s
[in] A descriptor identifying a socket.
cmd
[in] The command to perform on the socket s.
argp
[in/out] A pointer to a parameter for cmd.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值