读书人

网络编程之 Socket的方式(三) - “Win

发布时间: 2013-11-01 14:43:02 作者: rapoo

网络编程之 Socket的模式(三) --- “Window网络I/O模型”
1. Window下网络I/O模型 Window系统提供了另外一些网络模型,除了兼容Linux上的阻塞式I/O模型、非阻塞式I/O模型、I/O复用模型(只兼容select)外,还包括了WSAAsyncSelect模型、WSAEventSelect模型、Overlapped I/O 事件通知模型、Overlapped I/O 完成例程模型、IOCP模型。对于与Linux类似的网络模型,就不在这里叙述了。只讨论一些其特有的形式。


1.1 WSAAsyncSelect模型 Windows上最有特色的就是其消息机制了,所有的消息传送都依赖于窗口。通过消息机制,Windows做到了用单线程来同时处理UI和I/O数据。本质上,Windows的消息机制是一个轮询机制,而用户向窗口注册消息并关联对应的消息函数的过程可以理解成一个向Window系统设置回调函数的过程。当Windows收到某个消息,便会调用相应的用户处理函数。WSAAsyncSelect函数就是在这个背景下的一个产物,它把Socket和窗口关联了起来,非常适合于Windows编程使用。

WSAAsyncSelect函数原型如下:

int main() {     //初始化库    WSADATA wsaData;     WSAStartup(MAKEWORD(2,2),&wsaData);    //创建套接字    SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);     //设置套接口s"非阻塞模式"     u_long u1=1;     ioctlsocket(s,FIONBIO,(u_long*)&u1);     //绑定本地地址     struct sockaddr_in Sadd;     Sadd.sin_family=AF_INET;     Sadd.sin_port=htons(1928);     Sadd.sin_addr.S_un.S_addr=inet_addr("192.168.1.1");     bind(s,(sockaddr*)&Sadd,sizeof(Sadd));    //监听    listen(s,5);    //创建事件对象    WSAEVENT NewEvent=WSACreateEvent();     //网络事件注册    WSAEventSelect(s,NewEvent,FD_ACCEPT|FD_CLOSE);     //准备工作     int t=1;     WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];     SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];     int n=0;     eventArray[n]=NewEvent;     sockArray[n]=s;     n++;     //循环处理     while (true)     {         //等待事件对象        int nIndex=WSAWaitForMultipleEvents(n,eventArray,FALSE,40000,FALSE);         if (nIndex==WSA_WAIT_FAILED)        {             //调用失败            break;         }         else if (nIndex==WSA_WAIT_TIMEOUT)        {            //超时            break;         }         else         {            //网络事件触发事件对象句柄的工作状态            WSANETWORKEVENTS event;//该结构记录网络事件和对应出错代码             //网络事件查询            WSAEnumNetworkEvents(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,&event);             WSAResetEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]);             if ((event.lNetworkEvents&FD_ACCEPT)!=0)            {                 //处理FD_ACCEPT通知消息                 if (event.iErrorCode[FD_ACCEPT_BIT]==0)                 {                     if (n>WSA_MAXIMUM_WAIT_EVENTS)                     {                         //连接超上限                        break;                     }                     SOCKET sNew=accept(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,NULL);                     NewEvent=WSACreateEvent();                     WSAEventSelect(sNew,NewEvent,FD_READ|FD_CLOSE);                     eventArray[n]=NewEvent;                     sockArray[n]=sNew;                     n++;                 }             }             else if (event.lNetworkEvents&FD_READ)            {                 //处理FD_READ通知消息                 if (event.iErrorCode[FD_READ_BIT]==0)                 {                     char buf[256];                     memset(buf,0,256);                     int nRecv=recv(sockArray[nIndex-WSA_WAIT_EVENT_0],buf,sizeof(buf),0);                 }             }             else if (event.lNetworkEvents&FD_CLOSE)            {                //处理FD_CLOSE通知消息                if (event.iErrorCode[FD_CLOSE_BIT]==0)                 {                     closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]);                     WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]);                 }                 else                 {                     if (event.iErrorCode[FD_CLOSE_BIT]==10053)                     {                         closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]);                         WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]);                     }                 }                 for (int j=nIndex-WSA_WAIT_EVENT_0;j<n-1;j++)                 {                     sockArray[j]=sockArray[j+1];                     eventArray[j]=eventArray[j+1];                 }                 n--;             }         }    }    //关闭socket    closesocket(s);    //卸载库     WSACleanup();     return 0; } 

流程如下:
1、定义一个socket数组和event数组
2、调用WSAEventSelect为每个socket操作关联一个event对象
3、调用WSAWaitForMultipleEvents函数等待事件的触发
4、调用WSAEnumNetworkEvents函数查看是哪个一个事件,根据事件找到相应的socket,然后进行相应的处理:需要将event重置为无信号状态。
5、循环步骤3和4,直到服务器退出。

WSAEventSelec模型可以在没有窗口的条件下使用,但受到WSAWaitForMultipleEvents限制,只能接收64个socket的限制。如果和多线程配合使用,能够使接收数目增加,但由于核心对象的增加,性能会下降,故不推荐在服务器端使用。


(版权所有,转载时请注明作者和出处http://blog.csdn.net/arau_sh/article/details/13769753)

读书人网 >编程

热点推荐