WindowsSocket五种模型.docx

上传人:b****7 文档编号:11424413 上传时间:2023-03-01 格式:DOCX 页数:28 大小:27.63KB
下载 相关 举报
WindowsSocket五种模型.docx_第1页
第1页 / 共28页
WindowsSocket五种模型.docx_第2页
第2页 / 共28页
WindowsSocket五种模型.docx_第3页
第3页 / 共28页
WindowsSocket五种模型.docx_第4页
第4页 / 共28页
WindowsSocket五种模型.docx_第5页
第5页 / 共28页
点击查看更多>>
下载资源
资源描述

WindowsSocket五种模型.docx

《WindowsSocket五种模型.docx》由会员分享,可在线阅读,更多相关《WindowsSocket五种模型.docx(28页珍藏版)》请在冰豆网上搜索。

WindowsSocket五种模型.docx

WindowsSocket五种模型

WindowsSocket五种I/O模型

如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。

Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(OverlappedI/O)和完成端口(CompletionPort)共五种I/O模型。

每一种模型均适用于一种特定的应用场景。

程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

我会以一个回应反射式服务器(与《Windows网络编程》第八章一样)来介绍这五种I/O模型。

我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同):

#include

#include

#defineSERVER_ADDRESS"137.117.2.148"

#definePORT          5150

#defineMSGSIZE       1024

#pragmacomment(lib,"ws2_32.lib")

intmain()

{

WSADATA    wsaData;

SOCKET     sClient;

SOCKADDR_INserver;

char       szMessage[MSGSIZE];

int        ret;

//InitializeWindowssocketlibrary

WSAStartup(0x0202,&wsaData);

//Createclientsocket

sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

//Connecttoserver

memset(&server,0,sizeof(SOCKADDR_IN));

server.sin_family=AF_INET;

server.sin_addr.S_un.S_addr=inet_addr(SERVER_ADDRESS);

server.sin_port=htons(PORT);

connect(sClient,(structsockaddr*)&server,sizeof(SOCKADDR_IN));

while(TRUE)

{

   printf("Send:

");

  gets(szMessage);

   //Sendmessage

   send(sClient,szMessage,strlen(szMessage),0);

   //Receivemessage

   ret=recv(sClient,szMessage,MSGSIZE,0);

   szMessage[ret]='\0';

   printf("Received[%dbytes]:

'%s'\n",ret,szMessage);

}

//Cleanup

closesocket(sClient);

WSACleanup();

return0;

}

客户端所做的事情相当简单,创建套接字,连接服务器,然后不停的发送和接收数据。

比较容易想到的一种服务器模型就是采用一个主线程,负责监听客户端的连接请求,当接收到某个客户端的连接请求后,创建一个专门用于和该客户端通信的套接字和一个辅助线程。

以后该客户端和服务器的交互都在这个辅助线程内完成。

这种方法比较直观,程序非常简单而且可移植性好,但是不能利用平台相关的特性。

例如,如果连接数增多的时候(成千上万的连接),那么线程数成倍增长,操作系统忙于频繁的线程间切换,而且大部分线程在其生命周期内都是处于非活动状态的,这大大浪费了系统的资源。

所以,如果你已经知道你的代码只会运行在Windows平台上,建议采用WinsockI/O模型。

一.选择模型

Select(选择)模型是Winsock中最常见的I/O模型。

之所以称其为“Select模型”,是由于它的“中心思想”便是利用select函数,实现对I/O的管理。

最初设计该模型时,主要面向的是某些使用UNIX操作系统的计算机,它们采用的是Berkeley套接字方案。

Select模型已集成到Winsock1.1中,它使那些想避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。

由于Winsock1.1向后兼容于Berkeley套接字实施方案,所以假如有一个Berkeley套接字应用使用了select函数,那么从理论角度讲,毋需对其进行任何修改,便可正常运行。

(节选自《Windows网络编程》第八章)

下面的这段程序就是利用选择模型实现的Echo服务器的代码(已经不能再精简了):

#include

#include

#definePORT      5150

#defineMSGSIZE   1024

#pragmacomment(lib,"ws2_32.lib")

int   g_iTotalConn=0;

SOCKETg_CliSocketArr[FD_SETSIZE];

DWORDWINAPIWorkerThread(LPVOIDlpParameter);

intmain()

{

WSADATA    wsaData;

SOCKET     sListen,sClient;

SOCKADDR_INlocal,client;

int        iaddrSize=sizeof(SOCKADDR_IN);

DWORD      dwThreadId;

//InitializeWindowssocketlibrary

WSAStartup(0x0202,&wsaData);

//Createlisteningsocket

sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

//Bind

local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

local.sin_family=AF_INET;

local.sin_port=htons(PORT);

bind(sListen,(structsockaddr*)&local,sizeof(SOCKADDR_IN));

//Listen

listen(sListen,3);

//Createworkerthread

CreateThread(NULL,0,WorkerThread,NULL,0,&dwThreadId);

while(TRUE)

{

   //Acceptaconnection

   sClient=accept(sListen,(structsockaddr*)&client,&iaddrSize);

   printf("Acceptedclient:

%s:

%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

   //Addsockettog_CliSocketArr

   g_CliSocketArr[g_iTotalConn++]=sClient;

}

return0;

}

DWORDWINAPIWorkerThread(LPVOIDlpParam)

{

int           i;

fd_set        fdread;

int           ret;

structtimevaltv={1,0};

char          szMessage[MSGSIZE];

while(TRUE)

{

   FD_ZERO(&fdread);

   for(i=0;i

   {

     FD_SET(g_CliSocketArr[i],&fdread);

   }

   //Weonlycarereadevent

   ret=select(0,&fdread,NULL,NULL,&tv);

   if(ret==0)

   {

     //Timeexpired

     continue;

   }

   for(i=0;i

   {

     if(FD_ISSET(g_CliSocketArr[i],&fdread))

     {

       //Areadeventhappenedong_CliSocketArr[i]

       ret=recv(g_CliSocketArr[i],szMessage,MSGSIZE,0);

    if(ret==0||(ret==SOCKET_ERROR&&WSAGetLastError()==WSAECONNRESET))

    {

     //Clientsocketclosed

         printf("Clientsocket%dclosed.\n",g_CliSocketArr[i]);

     closesocket(g_CliSocketArr[i]);

     if(i

         {           

           g_CliSocketArr[i--]=g_CliSocketArr[--g_iTotalConn];

         }

       }

    else

    {

     //Wereceivedamessagefromclient

         szMessage[ret]='\0';

     send(g_CliSocketArr[i],szMessage,strlen(szMessage),0);

       }

     }

   }

}

return0;

}

服务器的几个主要动作如下:

1.创建监听套接字,绑定,监听;

2.创建工作者线程;

3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;

4.接受客户端的连接。

这里有一点需要注意的,就是我没有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为64。

而且,这里决不能无条件的accept,服务器应该根据当前的连接数来决定是否接受来自某个客户端的连接。

一种比较好的实现方案就是采用WSAAccept函数,而且让WSAAccept回调自己实现的ConditionFunction。

如下所示:

intCALLBACKConditionFunc(LPWSABUFlpCallerId,LPWSABUFlpCallerData,LPQOSlpSQOS,LPQOSlpGQOS,LPWSABUFlpCalleeId,LPWSABUFlpCalleeData,GROUPFAR*g,DWORDdwCallbackData)

{

if(当前连接数

  returnCF_ACCEPT;

else

  returnCF_REJECT;

}

工作者线程里面是一个死循环,一次循环完成的动作是:

1.将当前所有的客户端套接字加入到读集fdread中;

2.调用select函数;

3.查看某个套接字是否仍然处于读集中,如果是,则接收数据。

如果接收的数据长度为0,或者发生WSAECONNRESET错误,则表示客户端套接字主动关闭,这时需要将服务器中对应的套接字所绑定的资源释放掉,然后调整我们的套接字数组(将数组中最后一个套接字挪到当前的位置上)

除了需要有条件接受客户端的连接外,还需要在连接数为0的情形下做特殊处理,因为如果读集中没有任何套接字,select函数会立刻返回,这将导致工作者线程成为一个毫无停顿的死循环,CPU的占用率马上达到100%。

二.异步选择

Winsock提供了一个有用的异步I/O模型。

利用这个模型,应用程序可在一个套接字上,接收以Windows消息为基础的网络事件通知。

具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。

该模型最早出现于Winsock的1.1版本中,用于帮助应用程序开发者面向一些早期的16位Windows平台(如WindowsforWorkgroups),适应其“落后”的多任务消息环境。

应用程序仍可从这种模型中得到好处,特别是它们用一个标准的Windows例程(常称为"WndProc"),对窗口消息进行管理的时候。

该模型亦得到了MicrosoftFoundationClass(微软基本类,MFC)对象CSocket的采纳。

(节选自《Windows网络编程》第八章)

我还是先贴出代码,然后做详细解释:

#include

#include

#definePORT     5150

#defineMSGSIZE  1024

#defineWM_SOCKETWM_USER+0

#pragmacomment(lib,"ws2_32.lib")

LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);

intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,PSTRszCmdLine,intiCmdShow)

{

staticTCHARszAppName[]=_T("AsyncSelectModel");

HWND        hwnd;

MSG         msg;

WNDCLASS    wndclass;

wndclass.style        =CS_HREDRAW|CS_VREDRAW;

wndclass.lpfnWndProc  =WndProc;

wndclass.cbClsExtra   =0;

wndclass.cbWndExtra   =0;

wndclass.hInstance    =hInstance;

wndclass.hIcon        =LoadIcon(NULL,IDI_APPLICATION);

wndclass.hCursor      =LoadCursor(NULL,IDC_ARROW);

wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

wndclass.lpszMenuName=NULL;

wndclass.lpszClassName=szAppName;

if(!

RegisterClass(&wndclass))

{

   MessageBox(NULL,TEXT("ThisprogramrequiresWindowsNT!

"),szAppName,MB_ICONERROR);

   return0;

}

hwnd=CreateWindow(szAppName,                 //windowclassname

                      TEXT("AsyncSelectModel"),//windowcaption

                      WS_OVERLAPPEDWINDOW,       //windowstyle

                      CW_USEDEFAULT,             //initialxposition

                      CW_USEDEFAULT,             //initialyposition

                      CW_USEDEFAULT,             //initialxsize

                      CW_USEDEFAULT,             //initialysize

                      NULL,                      //parentwindowhandle

                      NULL,                      //windowmenuhandle

                      hInstance,                 //programinstancehandle

                      NULL);                    //creationparameters

ShowWindow(hwnd,iCmdShow);

UpdateWindow(hwnd);

while(GetMessage(&msg,NULL,0,0))

{

   TranslateMessage(&msg);

   DispatchMessage(&msg);

}

returnmsg.wParam;

}

LRESULTCALLBACKWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)

{

WSADATA      wsd;

staticSOCKETsListen;

SOCKET       sClient;

SOCKADDR_IN  local,client;

int          ret,iAddrSize=sizeof(client);

char         szMessage[MSGSIZE];

switch(message)

{

caseWM_CREATE:

   //InitializeWindowsSocketlibrary

  WSAStartup(0x0202,&wsd);

  

  //Createlisteningsocket

   sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    

  //Bind

   local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

  local.sin_family=AF_INET;

  local.sin_port=htons(PORT);

  bind(sListen,(structsockaddr*)&local,sizeof(local));

  

  //Listen

   listen(sListen,3);

   //AssociatelisteningsocketwithFD_ACCEPTevent

  WSAAsyncSelect(sListen,hwnd,WM_SOCKET,FD_ACCEPT);

  return0;

caseWM_DESTROY:

   closesocket(sListen);

   WSACleanup();

   PostQuitMessage(0);

   return0;

caseWM_SOCKET:

   if(WSAGETSELECTERROR(lParam))

   {

     closesocket(wParam);

     break;

   }

   

   switch(WSAGETSELECTEVENT(lParam))

   {

   caseFD_ACCEPT:

     //Acceptaconnectionfromclient

     sClient=accept(wParam,(structsockaddr*)&client,&iAddrSize);

     

     //AssociateclientsocketwithFD_READandFD_CLOSEevent

     WSAAsyncSelect(sClient,hwnd,WM_SOCKET,FD_READ|FD_CLOSE);

     break;

   caseFD_READ:

     ret=recv(wParam,szMessage,MSGSIZE,0);

     if(ret==0||ret==SOCKET_ERROR&&WSAGetLastError()==WSAECONNRESET)

     {

       closesocket(wParam);

     }

     else

     {

       szMessage[ret]='\0';

       send(wParam,szMessage,strlen(szMessage),0);

     }

     break;

     

   caseFD_CLOSE:

     closesocket(wParam);     

     break;

   }

   return0;

}

returnDefWindowProc(hwnd,message,wParam,lParam);

}

在我看来,WSAAsyncSelect是最简单的一种WinsockI/O模型(之所以说它简单是因为一个主线程就搞定了)。

使用RawWindowsAPI写过窗口类应用程序的人应该都能看得懂。

这里,我们需要做的仅仅是:

1.在WM_CREATE消息处理函数中,初始化WindowsSocketlibrary,创建监听套接字,绑定,监听,并且调用WSAAsyncSelect函数表示我们关心在监听套接字上发生的FD_ACCEPT事件;

2.自定义一个消息WM_SOCKET,一旦在我们所关心的套接字(监听套接字和客户端套接字)上发生了某个事件,系统就会调用WndProc并且message参数被设置为WM_SOCKET;

3.在WM

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1