zoukankan      html  css  js  c++  java
  • winsock select I/O模型初探

        windows对socket封装得有点异类,其典型的I/O模型有blocking,select,WSAAsyncSelect,WSAEventSelect,Overlapped,complete port,从本期开始逐步对各种I/O模型进行分析。

        select 模型是继承了berkeley的接口,在微软的MSDN中描述如下:

    The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

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

    nfds
    [in] Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.
    readfds
    [in, out] Optional pointer to a set of sockets to be checked for readability.
    writefds
    [in, out] Optional pointer to a set of sockets to be checked for writability.
    exceptfds
    [in, out] Optional pointer to a set of sockets to be checked for errors.
    timeout
    [in] Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

    显然select模型是一种轮询的机制,在处理数据之前需要将感兴趣的socket添加到相应的集合中。它的优点是能够在一个线程中监听多个端口,由于引入了时间的参数,不会导致某个socket在没有事件到来时,长时间处于阻塞状态,但缺点也是明显的:一个是效率上的,轮询需要遍历socket集合,消耗大量的cpu时间,二是并发量上,监听的端口数量有限,winsock2默认是64个套接字,当然可以通过重定义宏FD_SIZE来修改最大监听数目,但是系统的性能会受到影响(还未测试,后续会补上)。综上可以看出select模型在服务器端用途非常有限,可能是当时为了解决阻塞问题临时提供的一套解决方案吧。

        下面提供一套客户端调用select模型的代码:

    client端代码:  

      1 #include "stdafx.h"
      2 #include <winsock2.h>
      3 #pragma comment(lib,"WS2_32.lib")
      4 
      5 #define DEF_INVALID_SOCKET 0XFFFFFF
      6 #define DEF_MAX_CONNECT 10  
      7 #define FALSE 0
      8 #define TRUE  1
      9 
     10 BOOL CreateContext()
     11 {
     12     WSADATA wsaData;
     13 
     14     if (WSAStartup(MAKEWORD(2,1),&wsaData)) //call Windows Sockets DLL
     15     {
     16         printf("Winsock can't initiate,error code: %d !\n",WSAGetLastError ());
     17         WSACleanup();
     18         return FALSE;
     19     }
     20 
     21     return TRUE;
     22 }
     23 
     24 int main(int argc, char* argv[])
     25 {
     26     printf("Hello World!\n");
     27     SOCKET lszSocket[DEF_MAX_CONNECT] = {0XFFFFFF};
     28     printf("my client program! \n");
     29 
     30     if(!CreateContext())
     31     {
     32         return 0;
     33     }
     34 
     35     for(int i = 0;i < DEF_MAX_CONNECT;i++)
     36     {
     37          lszSocket[i] = socket(AF_INET,SOCK_STREAM,0);
     38          if( 0 > lszSocket[i])
     39          {
     40             printf("create socket error! The %d-th£¬error code: %d \n",i+1,WSAGetLastError ());            
     41             return 1;
     42          }
     43     }
     44     
     45     //bind socket
     46     for( i = 0;i < DEF_MAX_CONNECT;i++)
     47     {
     48         sockaddr_in lstLocalAddr;
     49         lstLocalAddr.sin_family = AF_INET;
     50         lstLocalAddr.sin_port = htons(0);
     51         lstLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
     52         if(SOCKET_ERROR == bind(lszSocket[i],(SOCKADDR*)&lstLocalAddr,sizeof(lstLocalAddr)))
     53         {
     54              printf("bind socket error! The %d-th£¬error code: %d \n",i+1,WSAGetLastError());
     55 
     56              return 1;
     57         }
     58         else
     59         {
     60             sockaddr_in lstServerAddr;
     61             lstServerAddr.sin_family = AF_INET;
     62             lstServerAddr.sin_port = htons(5700);
     63             lstServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");            
     64             if(0 > connect(lszSocket[i],(sockaddr*)&lstServerAddr,sizeof(lstServerAddr)))
     65             {
     66                 printf("connect socket error!The %d-th£¬error code: %d  \n",i+1,WSAGetLastError());
     67             }
     68         }
     69     }
     70    
     71     int liRet = 0;
     72     fd_set lstReadFd;
     73     timeval lstTime;
     74     lstTime.tv_sec = 0;
     75     lstTime.tv_usec = 10;
     76     while(1)
     77     {
     78         FD_ZERO(&lstReadFd);
     79         for(int i = 0;i < DEF_MAX_CONNECT;i++)
     80         {
     81             FD_SET(lszSocket[i],&lstReadFd);
     82         }
     83 
     84         liRet = select(DEF_MAX_CONNECT +1,&lstReadFd,NULL,NULL,&lstTime);
     85         switch(liRet)
     86         {
     87         case -1:
     88             printf("select socket error! error code: %d \n",WSAGetLastError());
     89             return 1;
     90             break;
     91         case 0:
     92             break;
     93         default:
     94             for( i = 0;i < DEF_MAX_CONNECT;i++)
     95             {                
     96                  if(FD_ISSET(lszSocket[i],&lstReadFd))
     97                  {
     98                      char lszRecvBuff[512] = {0};
     99                     int liRcvLen = recv(lszSocket[i],lszRecvBuff,512,0);
    100                     if(0 > liRcvLen)
    101                     {
    102                         printf(" %d socket,receive failed! \n",i+ 1);
    103                     }
    104                     printf("%d socket receive : %s \n", i+ 1, lszRecvBuff);
    105                  }
    106             }
    107         }     
    108     }
    109 
    110     for( i = 0;i < DEF_MAX_CONNECT;i++)
    111     {
    112         closesocket(lszSocket[i]);
    113     }
    114 
    115     //free resource
    116     WSACleanup();
    117     return 1;
    118 }

    服务端的代码相对简单,粘贴如下:

    #include "stdafx.h"
    #include <winsock2.h>
    #pragma comment(lib,"WS2_32.lib")
    
    int main(int argc, char* argv[])
    {
    
    	printf("Hello World!\n");
    	WSADATA wsaData;
    	if (WSAStartup(MAKEWORD(2,1),&wsaData))
    	{
    		printf("Winsock can't initiate, error code: %d !\n",WSAGetLastError());
    		WSACleanup();
    		return 1;
    	}
    	SOCKET lServerSocket = socket(AF_INET,SOCK_STREAM,0);
    	sockaddr_in lstAddr;
    	lstAddr.sin_family = AF_INET;
    	lstAddr.sin_port = htons(5700);
    	lstAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	if(0 > bind(lServerSocket,(sockaddr*) &lstAddr,sizeof(lstAddr)))
    	{
            printf("bind socket error ,code : %d \n",WSAGetLastError());
    	}
    
    	if( 0 != listen(lServerSocket,1000))
    	{
            printf("listen socket error ,code : %d \n",WSAGetLastError());
    	}
    	while(1)
    	{
    		sockaddr_in lClientAddr;
    		printf("Begin accept! \n");
    		char lszSndBuff[1024];
    		SOCKET lRecvSocket;
    		int liLen = sizeof(lClientAddr);
    		if(INVALID_SOCKET  == (lRecvSocket = accept(lServerSocket,(sockaddr*)&lClientAddr,&liLen)))
    		{
    			printf("receive error : %d \n",WSAGetLastError());
    		}
    		printf("client addr: %s ,port: %d  \n",inet_ntoa(lClientAddr.sin_addr),ntohs(lClientAddr.sin_port));
    		sprintf(lszSndBuff,"hello, %d \n",ntohs(lClientAddr.sin_port));
    		int liSndLen = send(lRecvSocket,lszSndBuff,strlen(lszSndBuff),0);
    		if(liSndLen <= 0)
    		{
    			printf("snd %d hello failed ! code: %d \n",ntohs(lClientAddr.sin_port),WSAGetLastError());
    		}
    		Sleep(300);
    	}
    	return 0;
    }
    

      




  • 相关阅读:
    fs.mkdir
    Node Buffer 利用 slice + indexOf 生成 split 方法
    class 类
    Proxy + Reflect 实现 响应的数据变化
    ivew 封装删除 对话框
    php调用js变量
    JS调用PHP 和 PHP调用JS的方法举例
    curl远程传输工具
    php 正则只保留 汉字 字母 数字
    php 发送与接收流文件
  • 原文地址:https://www.cnblogs.com/huanglifeng/p/3091971.html
Copyright © 2011-2022 走看看