下面范例是一个关于非阻塞模式下的SOCKET设定处理---select模式。



使用的是UDP协议。



Client02首先启动,将本机的1207端口进行SOCKET绑定,并将该SOCKET模式设定为非阻塞模式,
此模式下不可直接调用recvfrom。



理由: 
阻塞模式下,如果直接调用recvfrom从指定的SOCKET读取数据,如果还没有接受到来自Client01的数据,函数recvfrom会一直等待,直到有数据可以读出为止。当然,也可以创建一个独立的线程来专门调用recvfrom,这样可以不影响主线程的处理。



非阻塞模式下,recvfrom函数会立即返回,如果此时没有数据到达,该函数调用必然失败,返回值为WSAEWOULDBLOCK,表明当前状态是非阻塞模式的调用,且没有数据到达。这时,通常的做法是,创建一个独立的线程调用recvfrom,判断返回值(如果是WSAEWOULDBLOCK)进行循环调用recvfrom,直到有数据到达为止,这样可以,但是开销大,不好。如果,用MSG_PEEK来查询,其实开销也很大,也不推荐。




或者用select模式进行处理。(select应该是专门用来对付非阻塞模式的一种办法)



select函数察看要求进行处理的SOCKET, 同时设有超时Value, 这个函数本身会等待(也就是本身其实是个阻塞的函数),直到有可用的SOCKET为止,然后,进行处理,再进行select等待,如此反复执行。





//       
 //         UDP Client 02       
 //         IP: 127.0.0.1       
 //         PORT: 1207       
 //       

       #define CONNECT_IP           "127.0.0.1"       
 #define CONNECT_PORT         1207       
 #define DEFAULT_BUFFER_LEN   256      

       char* getBlockMode(int nMode)       
 {       
     return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ;       
 }      


 int _tmain(int argc, _TCHAR* argv[])       
 {      

           printf(">>>>>UDP Client启动<<<<<<<\n");       
     WSAData         wsData;       
     SOCKET          sClient;       
     SOCKADDR_IN    addrSender;       
     SOCKADDR_IN    addrRecviver;       
     int            addrSenderLen = sizeof(addrSender);       
     char           sendBuff[DEFAULT_BUFFER_LEN] = {0};       
     char           recvBuff[DEFAULT_BUFFER_LEN] = {0};       
     DWORD       nMode = 0;       
     int             nError = -1;      

           printf("-启动SOCKET库\n");       
     WSAStartup( MAKEWORD(2,2), &wsData );      

           addrRecviver.sin_family = AF_INET;       
     addrRecviver.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );       
     addrRecviver.sin_port = htons( CONNECT_PORT );       
     printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrRecviver.sin_addr), ntohs(addrRecviver.sin_port));      


     printf("-创建SOCKET\n");       
     sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP );       
     if( sClient == INVALID_SOCKET )       
     {       
         printf("!!! socket failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }      

                 
     //阻塞/非阻塞模式设定       
           
     nMode = 1; //阻塞模式/非阻塞模式       
     nError = ioctlsocket( sClient, FIONBIO, &nMode );       
     if( nError!=0 )       
     {       
         printf("!!! ioctlsocket failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }       
     printf("-设定SOCKET模式: %s\n", getBlockMode(nMode));      

           printf("-绑定SOCKET用于接受数据\n");       
     nError = bind( sClient, (const sockaddr *)&addrRecviver, sizeof(addrRecviver) );       
     if( nError == SOCKET_ERROR )       
     {       
         printf("!!! bind failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }      


           
     //       
     // select 模型用于监视制定socket 非阻塞模式设定中       
     //       
           
     fd_set   fdRead;       
     struct   timeval tv = {5,0}; //{second, milisec}      

           while( true )       
     {       
         FD_ZERO( &fdRead );                   //首先清空fd集       
         FD_SET( sClient, &fdRead );         //加入要查看的SOCKET      

               printf("-select 监视中\n");       
         nError = select( 0, &fdRead, NULL, NULL, &tv );   //调用select进行SOCKET查看       
         ;;;;;if( nError == 0 )       
         {       
             printf("-!!! select 超时: %d sec\n", tv.tv_sec);       
             continue;       
         }       
         else if( nError < 0 )       
         {       
             printf("!!! select failed: %d\n", WSAGetLastError());       
             WSACleanup();       
             return -1;       
         }      

               printf("-select 查找可读的SOCKET\n");       
         if( FD_ISSET(sClient, &fdRead) )                        //查找可用的SOCKET,不可用的SOCKET会从fd集里删除掉       
         {//可读入数据了       
             printf("-找到一个可以读入的SOCKET\n");      

                   nError = recvfrom( sClient, recvBuff, sizeof(recvBuff), 0, (sockaddr *)&addrSender, &addrSenderLen );       
             if( nError == SOCKET_ERROR )       
             {       
                 printf("!!! recvfrom failed: %d\n", WSAGetLastError());       
                 closesocket( sClient );       
                 WSACleanup();       
                 return -1;       
             }       
             printf("-收到数据: %s [%s:%d]\n", recvBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));      

               }       
         else{       
             printf("-没有找到一个可以读入的SOCKET\n");       
         }      

           }//while(true)      





           printf("-Socket关闭\n");       
     closesocket( sClient );       
     WSACleanup();      

       return 0;       
 }




//       
 //         UDP Client 01       
 //         IP: 127.0.0.1       
 //         PORT: 1207       
 //       

       #define CONNECT_IP           "127.0.0.1"       
 #define CONNECT_PORT         1207       
 #define DEFAULT_BUFFER_LEN   256      

       char* getBlockMode(int nMode)       
 {       
     return nMode==0? "阻塞模式设定" : "非阻塞模式设定" ;       
 }      


 int _tmain(int argc, _TCHAR* argv[])       
 {      

           printf(">>>>>UDP Client启动<<<<<<<\n");       
     WSAData          wsData;       
     SOCKET             sClient;       
     SOCKADDR_IN    addrSender;       
     SOCKADDR_IN    addrRecvier;       
     int             addrRecvierLen = sizeof(addrRecvier);       
     char           sendBuff[DEFAULT_BUFFER_LEN] = {0};       
     char           recvBuff[DEFAULT_BUFFER_LEN] = {0};       
     DWORD       nMode = 0;       
     int               nError = -1;      

           printf("-启动SOCKET库\n");       
     WSAStartup( MAKEWORD(2,2), &wsData );      

           addrSender.sin_family = AF_INET;       
     addrSender.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );       
     addrSender.sin_port = htons( CONNECT_PORT );       
     printf("-设定地址等信息完了: %s:%d\n", inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));      


     printf("-创建SOCKET\n");       
     sClient = socket( AF_INET, SOCK_DGRAM , IPPROTO_UDP );       
     if( sClient == INVALID_SOCKET )       
     {       
         printf("!!! socket failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }      

                 
     //阻塞/非阻塞模式设定       
           
     nMode = 0; //阻塞模式/非阻塞模式       
     nError = ioctlsocket( sClient, FIONBIO, &nMode );       
     if( nError!=0 )       
     {       
         printf("!!! ioctlsocket failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }       
     printf("-设定SOCKET模式: %s\n", getBlockMode(nMode));      

           strcpy( sendBuff, "message from Client01" );       
     printf("-送信设定: %s [%s:%d]\n", sendBuff, inet_ntoa(addrSender.sin_addr), ntohs(addrSender.sin_port));       
     nError = sendto( sClient, sendBuff, sizeof(sendBuff), 0, (const sockaddr *)&addrSender, sizeof(addrSender) );       
     if( nError == SOCKET_ERROR )       
     {       
         printf("!!! sendto failed: %d\n", WSAGetLastError());       
         WSACleanup();       
         return -1;       
     }      




           printf("-Socket关闭\n");       
     closesocket( sClient );       
     WSACleanup();      

       return 0;       
 }