二.异步接纳,有个别时候 overlapped I/O 能够取代八线程的功力

    那一章描述怎样采纳 overlapped I/O(约等于 asynchronous
I/O)。某个时候 overlapped I/O 能够替代二拾二十四线程的功能。可是,overlapped
I/O 加上completion ports,常被规划为二十四线程管理,以便在四个“受制于 I/O
的程序”(所谓 I/O bound 程序)中赢得高功能。

    译注    深层研商 Win3二 平台(WinNT 和 Win玖伍)的 File Systems 和
Device I/O 的图书极少。Advanced Windows 3rd edition(Jeffrey
Richter/Microsoft Press)第 一三章和第 1四 章有诸多珍奇的开始和结果,可供仿照效法。
  
 甘休方今本人早就花了数章篇幅告诉各位“怎么样”以及“为何”要选用线程。作者将以那1章介绍一个你可能不想选拔10二线程的场馆。大多应用程序,比方终端机模拟程序,都亟需在同一时间管理对四个上述的文本的读写操作。利用
Win32 所谓的 overlapped I/O 个性,你就可以让具备那么些 I/O
操作并行处理,并且当其余3个 I/O
完成时,你的次第会接到1个通报。其余操作系统把这一个本性称为 nonblocking
I/O 或 asynchronous I/O。
  
 回头看看第2章和第3章,这里已经示范怎么产生四个线程担负后台打字与印刷操作。事实上“后台打字与印刷”这种说法是不当的,大家在后台所进行的操作只可是是发出打字与印刷机所需的数额。Windows
操作系统担当以打字与印刷管理器(Printer Manager)实现打字与印刷。打字与印刷管理器会
“spooled”
那多少个准备好的数据,并且以打印机所能接受的进程,逐步地将数据喂给打印机。
    关键在于 I/O 设备是个慢速设备,不论打印机、调制解调器,以至硬盘,与
CPU 相比较都奇慢无比。坐下来干等 I/O
的变成是一件不甚明智的工作。一时候数据的流动率特别惊人,把数量从您的文书服务器中以
Ethernet
速度搬走,其速度可能高达每秒一百万个字节。假若您品尝从文件服务器中读取100KB
,在用户的眼光来看差不离是一下子成功。不过,要领悟,你的线程试行这一个命令,已经浪费了
10 个一百万次 CPU 周期。
    未来想象一下,同多个文本服务器,透过一条拨号线路,被 Remote Access
Services(RAS)管理。于是,100KB 的数据量在 ISDN 上急需 一五 秒,在
9600bps
线路上需求差不多两分钟。固然从用户的见解来看,三个窗口在这么长的小时中完全未有反应,也是十分可怕的。
    这一个难题的鲜明缓慢解决方案正是,使用另多个线程来开始展览 I/O。但是,那就
    发生出部分有关难点,包罗什么在主线程中操控许多少个 worker
线程、怎么样设定同步机制、怎样管理错误意况、怎么着体现对话框。那么些问题都就要本章出现并化解之。
    四个最简易的回复:overlapped I/O 是 Win3贰的一项技巧,你可以供给操作系统为您传送数据,并且在传递达成时通报你。那项本事让你的次序在I/O
进行进程中还能够够承袭处监护人务。事实上,操作系统内部便是以线程来达成overlapped I/O。你能够收获线程的兼具收益,而不需提交什么难过代价。
        重要!
        Windows 玖五 所辅助的 overlapped I/O 有个别限制,只适用于 named
pipes 、mailslots 、serial I/O 、以及 socket() 或 accept()
所传回来的sockets,它并不支持磁盘或光盘中的文件操作。本章的具备例子只在Windows
NT 下能力够行得通运作。

    那1章对于 overlapped I/O
的商议,将从最简便的利用起来,然后再衍生和变化到最高等的选用。
        i 激发的公文 handles
        i 激发的 event 对象
        i 异步进程调用(Asynchronous Procedure Calls,APCs)
        i I/O completion ports
    当中以 I/O completion ports
特别显得主要,因为它们是有一无二适用于高负荷服务器(必须同时保险广大接连线路)的一个技艺。Completion
ports 利用一些线程,扶助平衡由“I/O
请求”所引起的负载。那样的架构非常契合用在SMP 系统(译注:支持八个 CPU
的操作系统)中发生所谓的 “scalable” 服务器。
    译注     所谓 scalable 系统,是指能够藉着扩大 RAM 或磁盘空间或 CPU
个数而升迁应用程序效用的一种系统。

Socket模型详解

两种I/O模式

1.抉择模型

二.异步采用

3.事变选取

四.重叠I/O模型

伍.到位端口模型

四种I/O模型的可比

 

 

 

两种I/O模式

1、 两种I/O模式

堵塞格局:推行I/O操作达成前会一贯开始展览等待,不会将调控权交给程序。套接字默感到阻塞方式。能够通过八线程本事拓展管理。

非阻塞模式:试行I/O操作时,Winsock函数会回来并交出调控权。这种形式应用起来相比复杂,因为函数在并未有运转实现就举办重回,会不停地回到 WSAEWOULDBLOCK错误。但作用庞大。

 

设若您想在Windows平台上创设服务器应用,那么I/O模型是您必须思考的。Windows操作系统提供了增选(Select)、异步选拔(WSAAsyncSelect)、事件选择(WSA伊芙ntSelect)、重叠I/O(Overlapped I/O)和完毕端口(Completion Port)共各种I/O模型。每壹种模型均适用于一种特定的选取场景。技士应该对和谐的使用须求极度确定,而且综合思虑到程序的扩大性和可移植性等因素,作出自身的精选。

 

笔者会以叁个答复反射式服务器(与《Windows互联网编制程序》第8章同样)来介绍那多种I/O模型。

咱俩借使客户端的代码如下(为代码直观,省去全数错误检查,以下同):

客户端

#include
<WINSOCK2.H>

#include
<stdio.h>

 

#define SERVER_ADDRESS
“137.117.2.148”

#define PORT          
5150

#define MSGSIZE       
1024

 

#pragma comment(lib,
“ws2_32.lib”)

 

int main()

{

 WSADATA     wsaData;

 SOCKET      sClient;

 SOCKADDR_IN server;

 char       
szMessage[MSGSIZE];

 int         ret;

 

 // Initialize Windows socket
library

 WSAStartup(0x0202,
&wsaData);

 

 // Create client
socket

 sClient = socket(AF_INET, SOCK_STREAM,
IPPROTO_TCP);

 

 // Connect to server

 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, (struct sockaddr *)&server,
sizeof(SOCKADDR_IN));

 

 while (TRUE)

 {

    printf(“Send:”);

 gets(szMessage);

 

    // Send message

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

 

    // Receive message

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

    szMessage[ret] =
‘\0’;

 

    printf(“Received [%d
bytes]: ‘%s’\n”, ret, szMessage);

 }

 

 // Clean up

 closesocket(sClient);

 WSACleanup();

 return 0;

}

 

客户端所做的事体非凡简单,创造套接字,连接服务器,然后不停的出殡和接收数据。

 

相比较轻易想到的1种服务器模型正是运用二个主线程,负担监听客户端的连年请求,当收到到有些客户端的连日请求后,创造2个特地用来和该客户端通讯的套接字和3个增加帮衬线程。以后该客户端和服务器的互相都在这些援救线程内达成。这种办法相比直观,程序特别简单而且可移植性好,可是无法利用阳台相关的特色。比如,即便连接数加多的时候(数不胜数的连年),那么线程数成倍增加,操作系统忙于频仍的线程间切换,而且多数线程在其生命周期内都是地处非活动状态的,那大大浪费了系统的能源。所以,如若您曾经知晓你的代码只会运作在Windows平台上,建议采纳Winsock I/O模型。

 

一.选用模型

Select(选用)模型是Winsock中最广大的I/O模型。之所以称其为“Select模型”,是出于它的“中央观念”正是使用select函数,达成对I/O的管理。最初安排该模型时,首要面向的是某个使用UNIX操作系统的微型计算机,它们采取的是Berkeley套接字方案。Select模型已融为1体到Winsock 壹.第11中学,它使那几个想防止在套接字调用进程中被无辜“锁定”的应用程序,选用1种有序的诀窍,同时拓展对三个套接字的治本。由于Winsock 一.一向后约等于Beck雷套接字施行方案,所以一旦有1个贝Klay套接字应用使用了select函数,那么从理论角度讲,毋需对其进展其余修改,便可正常运作。(节选自《Windows互联网编制程序》第九章)

上面包车型客车这段程序正是选择采取模型实现的Echo服务器的代码(已经不能够再轻巧了):

Echo服务器

#include
<winsock.h>

#include
<stdio.h>

 

#define PORT      
5150

#define MSGSIZE   
1024

 

#pragma comment(lib,
“ws2_32.lib”)

 

int    g_iTotalConn =
0;

SOCKET
g_CliSocketArr[FD_SETSIZE];

 

DWORD WINAPI
WorkerThread(LPVOID lpParameter);

 

int main()

{

 WSADATA     wsaData;

 SOCKET      sListen,
sClient;

 SOCKADDR_IN local,
client;

 int         iaddrSize =
sizeof(SOCKADDR_IN);

 DWORD      
dwThreadId;

 

 // Initialize Windows socket
library

 WSAStartup(0x0202,
&wsaData);

 

 // Create listening
socket

 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, (struct
sockaddr *)&local, sizeof(SOCKADDR_IN));

 

 // Listen

 listen(sListen, 3);

 

 // Create worker
thread

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

 

 while (TRUE)

 {

    // Accept a
connection

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

    printf(“Accepted
client:%s:%d\n”, inet_ntoa(client.sin_addr),
ntohs(client.sin_port));

 

    // Add socket to
g_CliSocketArr

   
g_CliSocketArr[g_iTotalConn++] = sClient;

 }

 

 return 0;

}

 

DWORD WINAPI
WorkerThread(LPVOID lpParam)

{

 int            i;

 fd_set        
fdread;

 int            ret;

 struct timeval tv = {1,
0};

 char          
szMessage[MSGSIZE];

 

 while (TRUE)

 {

   
FD_ZERO(&fdread);//将fdread开端化空集

    for (i = 0; i <
g_iTotalConn; i++)

    {

      FD_SET(g_CliSocketArr,
&fdread);//将在检查的套接口出席到聚聚集

    }

 

    // We only care read
event

    ret = select(0, &fdread,
NULL, NULL, &tv);//每隔一段时间,检查可读性的套接口

 

    if (ret == 0)

    {

      // Time expired

      continue;

    }

 

    for (i = 0; i <
g_iTotalConn; i++)

    {

      if
(FD_ISSET(g_CliSocketArr, &fdread))//假设可读

      {

        // A read event
happened on g_CliSocketArr

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

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

    {

     // Client socket
closed

          printf(“Client
socket %d closed.\n”, g_CliSocketArr);

    
closesocket(g_CliSocketArr);

     if (i < g_iTotalConn

  • 1)

          {           

           
g_CliSocketArr[i–] = g_CliSocketArr[–g_iTotalConn];

          }

        }

    else

    {

     // We received a message
from client

          szMessage[ret] =
‘\0’;

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

        }

      }

    }

 }

 

 return 0;

}

 

服务器的多少个第1动作如下:

一.开立监听套接字,绑定,监听;

二.创办工笔者线程;

三.创制二个套接字数组,用来存放在当前有所移动的客户端套接字,每accept3个总是就创新叁遍数组;

四.承受客户端的连日。

那边有一点急需专注的,正是自个儿未有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为6四。而且,这里决不能够无条件的accept,服务器应该依靠如今的连接数来支配是还是不是接受来自有些客户端的总是。壹种比较好的完结方案正是应用WSAAccept函数,而且让WSAAccept回调自个儿实现的Condition Function。如下所示:

 

int CALLBACK
ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS
lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR
* g,DWORD dwCallbackData)

{

if (当前连接数 < FD_SETSIZE)

 return CF_ACCEPT;

else

 return CF_REJECT;

}

 

劳重力线程里面是3个死循环,一遍巡回实现的动作是:

一.将日前持有的客户端套接字插足到读集fdread中;

2.调用select函数;

三.翻看有些套接字是或不是依然处于读集中,如果是,则接收数据。假如接受的数目长度为0,也许发生WSAECONNRESET错误,则表示客户端套接字主动关闭,那时急需将服务器中对应的套接字所绑定的能源自由掉,然后调节大家的套接字数组(将数组中最后多少个套接字挪到最近的岗位上)

 

除却部要求要有标准接受客户端的总是外,还要求在连年数为0的情况下做特殊处理,因为要是读聚集未有别的套接字,select函数会即刻回去,那将促成工小编线程成为贰个毫无停顿的死循环,CPU的占用率马上到达百分百。

 

关系到套接字列表的操作都亟待利用循环,在轮询的时候,供给遍历壹次,再新的壹轮开端时,将列表参预队列又须求遍历一次.也正是说,Select在职业三回时,须求至少遍历2回列表,那是它功效极低的原由之一.在周边的网络连接方面,照旧引入应用IOCP或EPOLL模型.可是Select模型能够动用在诸如对阵类游戏上,比如类似星际这种,因为它精美易于落到实处,而且对战类游戏的网络连接量并十分的小.

 

对此Select模型想要突破Windows 63个限制以来,能够应用分层轮询,一次轮询六十四个.譬喻套接字列表为12九个,在率先次轮询时,将前65个放入队列中用Select实市场价格况查询,待此番操作全体了事后.将后61个再参预轮询队列中张开轮询管理.那样管理供给在非阻塞式下专门的学业.就那样类推,Select也能支撑不过五个.

 

 

二.异步选用

Winsock提供了一个管用的异步I/O模型。利用这一个模型,应用程序可在三个套接字上,接收以Windows消息为根基的互联网事件通报。具体的做法是在建好3个套接字后,调用WSAAsyncSelect函数。该模型最早出现于Winsock的一.壹版本中,用于扶持应用程序开荒者面向一些最初的拾伍位Windows平台(如Windows for Workgroups),适应其“落后”的多任务音讯情状。应用程序仍可从这种模型中得到好处,极其是它们用三个标准的Windows例程(常称为”WndProc”),对窗口新闻实行政管理制的时候。该模型亦赢得了Microsoft Foundation
Class(微软基本类,MFC)对象CSocket的选择。(节选自《Windows互连网编制程序》第7章)

自己大概先贴出代码,然后做详细表达:

#include
<winsock.h>

#include
<tchar.h>

 

#define PORT      5150

#define MSGSIZE   1024

#define
WM_SOCKET WM_USER+0

 

#pragma comment(lib,
“ws2_32.lib”)

 

LRESULT CALLBACK WndProc(HWND,
UINT, WPARAM, LPARAM);

 

int WINAPI WinMain(HINSTANCE
hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

 static TCHAR szAppName[] =
_T(“AsyncSelect Model”);

 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
(“This program requires Windows NT!”), szAppName, MB_ICONERROR)
;

    return 0 ;

 }

 

 hwnd = CreateWindow
(szAppName,                  // window class name

                       TEXT
(“AsyncSelect Model”), // window caption

                      
WS_OVERLAPPEDWINDOW,        // window style

                      
CW_USEDEFAULT,              // initial x position

                      
CW_USEDEFAULT,              // initial y position

                      
CW_USEDEFAULT,              // initial x size

                      
CW_USEDEFAULT,             // initial y size

                      
NULL,                       // parent window handle

                      
NULL,                       // window menu handle

                      
hInstance,                  // program instance handle

                       NULL)
;                     // creation parameters

 

 ShowWindow(hwnd,
iCmdShow);

 UpdateWindow(hwnd);

 

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

 {

    TranslateMessage(&msg)
;

    DispatchMessage(&msg)
;

 }

 

 return msg.wParam;

}

 

LRESULT CALLBACK WndProc (HWND
hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

 WSADATA       wsd;

 static SOCKET sListen;

 SOCKET        sClient;

 SOCKADDR_IN   local,
client;

 int           ret, iAddrSize
= sizeof(client);

 char         
szMessage[MSGSIZE];

 

switch (message)

{

case WM_CREATE:

    // Initialize Windows
Socket library

 WSAStartup(0x0202,
&wsd);

 

 // Create listening
socket

    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, (struct
sockaddr *)&local, sizeof(local));

 

 // Listen

    listen(sListen, 3);

 

    // Associate listening
socket with FD_ACCEPT event

 WSAAsyncSelect(sListen, hwnd, WM_SOCKET,
FD_ACCEPT);

 return 0;

 

  case WM_DESTROY:

   
closesocket(sListen);

    WSACleanup();

    PostQuitMessage(0);

    return 0;

 

 case WM_SOCKET:

    if
(WSAGETSELECTERROR(lParam))

    {

     
closesocket(wParam);

      break;

    }

   

    switch (WSAGETSELECTEVENT(lParam))//取低位字节,互联网事件

    {

    case FD_ACCEPT:

      // Accept a connection
from client

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

     

      // Associate client
socket with FD_READ and FD_CLOSE event

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

      break;

 

    case FD_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;

     

    case FD_CLOSE:

     
closesocket(wParam);     

      break;

    }

    return 0;

 }

 

 return DefWindowProc(hwnd, message,
wParam, lParam);

}

 

 

在小编看来,WSAAsyncSelect是最简便易行的1种Winsock I/O模型(之所以说它大约是因为八个主线程就化解了)。使用Raw Windows API写过窗口类应用程序的人相应都能看得懂。这里,大家需求做的唯有是:

1.在WM_CREATE新闻管理函数中,伊始化Windows Socket library,创制监听套接字,绑定,监听,并且调用WSAAsyncSelect函数表示我们关切在监听套接字上产生的FD_ACCEPT事件;

二.自定义2个消息WM_SOCKET,1旦在大家所关怀的套接字(监听套接字和客户端套接字)上发生了某些事件,系统就能够调用WndProc并且message参数被安装为WM_SOCKET;

3.在WM_SOCKET的新闻管理函数中,分别对FD_ACCEPT、FD_READ和FD_CLOSE事件开始展览拍卖;

肆.在窗口销毁消息(WM_DESTROY)的处理函数中,大家关闭监听套接字,清除Windows Socket library

 

上面那张用于WSAAsyncSelect函数的网络事件类型表能够让您对种种网络事件有更明了的认知:

表1

 

FD_READ 应用程序想要接收有关是不是可读的通告,以便读入数据

FD_WHavalITE 应用程序想要接收有关是还是不是可写的照拂,以便写入数据

FD_OOB 应用程序想接收是还是不是有带外(OOB)数据到达的文告

FD_ACCEPT 应用程序想吸收与进入连接有关的文告

FD_CONNECT 应用程序想吸收与一遍接二连三或然多点join操作完毕的通报

FD_CLOSE 应用程序想吸收与套接字关闭有关的打招呼

FD_QOS 应用程序想接受套接字“服务质量”(QoS)产生变动的通告

FD_GROUP_QOS 应用程序想吸收套接字组“服务品质”产生更改的照拂(以后没什么用处,为现在套接字组的选择保留)

FD_ROUTING_INTERFACE_CHANGE 应用程序想接受在内定的自由化上,与路由接口爆发变化的通告

FD_ADDRESS_LIST_CHANGE 应用程序想吸收针对套接字的情厂家族,本地地址列表发生变化的通知

 

三.事件选择

Winsock提供了另三个得力的异步I/O模型。和WSAAsyncSelect模型类似的是,它也同意应用程序在一个或多少个套接字上,接收以事件为底蕴的互连网事件通报。对于表一总结的、由WSAAsyncSelect模型行使的互联网事件来讲,它们均可纹丝不动地移植到新模型。在用新模型开拓的应用程序中,也能接受和拍卖全数那一个事件。该模型最重大的差别在于互连网事件会投递至一个事变指标句柄,而非投递至3个窗口例程。(节选自《Windows互联网编制程序》第楚辞)

要么让我们先看代码然后举办分析:

#include <winsock2.h>

#include <stdio.h>

 

#define PORT    5150

#define MSGSIZE 1024

 

#pragma comment(lib, “ws2_32.lib”)

 

int      g_iTotalConn = 0;

SOCKET  
g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];

WSAEVENT
g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

 

DWORD WINAPI WorkerThread(LPVOID);

void Cleanup(int index);

 

int main()

{

 WSADATA     wsaData;

 SOCKET      sListen, sClient;

 SOCKADDR_IN local, client;

 DWORD       dwThreadId;

 int         iaddrSize =
sizeof(SOCKADDR_IN);

 

 // Initialize Windows Socket
library

 WSAStartup(0x0202, &wsaData);

 

 // Create listening socket

 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, (struct sockaddr *)&local,
sizeof(SOCKADDR_IN));

 

 // Listen

 listen(sListen, 3);

 

 // Create worker thread

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

 

 while (TRUE)

 {

    // Accept a connection

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

    printf(“Accepted client:%s:%d\n”,
inet_ntoa(client.sin_addr), ntohs(client.sin_port));

 

    // Associate socket with network
event

    g_CliSocketArr[g_iTotalConn] =
sClient;//接受连接的套接口

g_CliEventArr[g_iTotalConn] = WSACreate伊芙nt();//重回事件目的句柄

//在套接口上将1个或三个网络事件与
事件对象关系在协同

    WSAEventSelect(g_CliSocketArr[g_iTotalConn],//套接口

           
       g_CliEventArr[g_iTotalConn],//事件目标

                   FD_READ |
FD_CLOSE);//互连网事件

    g_iTotalConn++;

 }

}

 

DWORD WINAPI WorkerThread(LPVOID
lpParam)

{

 int              ret, index;

 WSANETWORKEVENTS NetworkEvents;

 char            
szMessage[MSGSIZE];

 

 while (TRUE)

 { //重回导致重回的事件目的

ret = WSAWaitForMultipleEvents(g_iTotalConn,//数组中的句柄数目

g_Cli伊芙ntArr,//指向1个风云目的句柄数组的指针

 FALSE, //T,都进才回;F,一进就回

一千, //超时间隔

FALSE);//是或不是实行到位例程

    if (ret == WSA_WAIT_FAILED || ret ==
WSA_WAIT_TIMEOUT)

    {

      continue;

    }

 

index = ret – WSA_WAIT_EVENT_0;

//在套接口上查询与事件指标关系的互联网事件

   
WSAEnumNetworkEvents(g_CliSocketArr[index], g_CliEventArr[index],
&NetworkEvents);

//处理FD-READ互连网事件

    if (NetworkEvents.lNetworkEvents &
FD_READ)

    {

      // Receive message from client

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

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

      {

        Cleanup(index);

      }

      else

      {

        szMessage[ret] = ‘\0’;

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

      }

    }

    //管理FD-CLOSE网络事件

    if (NetworkEvents.lNetworkEvents &
FD_CLOSE)

 {

   Cleanup(index);

 }

 }

 return 0;

}

 

void Cleanup(int index)

{

 closesocket(g_CliSocketArr[index]);

WSACloseEvent(g_CliEventArr[index]);

 

if (index < g_iTotalConn – 1)

{

 g_CliSocketArr[index] =
g_CliSocketArr[g_iTotalConn – 1];

 g_CliEventArr[index] =
g_CliEventArr[g_iTotalConn – 1];

}

g_iTotalConn–;

}

 

事件接纳模型也相比较轻易,达成起来也不是太复杂,它的基本思维是将种种套接字都和3个WSAEVENT对象对应起来,并且在论及的时候钦赐须要关心的如何网络事件。1旦在有些套接字上发出了大家关心的风云(FD_READ和FD_CLOSE),与之相关联的WSAEVENT对象被Signaled。程序定义了八个全局数组,三个套接字数组,1个WSAEVENT对象数组,其尺寸都以MAXIMUM_WAIT_OBJECTS(6四),五个数组中的成分壹1对应。

1致的,这里的顺序尚未设想五个难题,1是不可能无尺度的调用accept,因为大家帮助的并发连接数有限。化解措施是将套接字按MAXIMUM_WAIT_OBJECTS分组,每MAXIMUM_WAIT_OBJECTS个套接字壹组,每壹组分配八个劳动力线程;可能应用WSAAccept取代accept,并回调本人定义的Condition Function。首个难点是一向不对连日数为0的场合做特殊处理,程序在接连数为0的时候CPU占用率为百分百。

 

四.重叠I/O模型

Winsock贰的公布使得Socket I/O有了和文书I/O统一的接口。大家得以因而选拔Win3二文本垄断(monopoly)函数ReadFile和WriteFile来开始展览Socket I/O。伴随而来的,用于一般文书I/O的重叠I/O模型和姣好端口模型对Socket I/O也适用了。那几个模型的独到之处是足以达标更佳的系统本性,不过达成相比较复杂,里面涉及较多的C语言技巧。譬如大家在成就端口模型中会平日用到所谓的“尾随数据”。

 

一.用事件通报方式达成的重叠I/O模型

#include
<winsock2.h>

#include
<stdio.h>

 

#define PORT    5150

#define MSGSIZE 1024

 

#pragma comment(lib,
“ws2_32.lib”)

 

typedef struct

{

 WSAOVERLAPPED overlap;

 WSABUF        Buffer;

 char         
szMessage[MSGSIZE];

 DWORD        
NumberOfBytesRecvd;

 DWORD         Flags;

}PER_IO_OPERATION_DATA,
*LPPER_IO_OPERATION_DATA;

 

int                    
g_iTotalConn = 0;

SOCKET                 
g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];

WSAEVENT               
g_CliEventArr[MAXIMUM_WAIT_OBJECTS];

LPPER_IO_OPERATION_DATA
g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS];

 

DWORD WINAPI
WorkerThread(LPVOID);

void Cleanup(int);

 

int main()

{

 WSADATA     wsaData;

 SOCKET      sListen,
sClient;

 SOCKADDR_IN local,
client;

 DWORD      
dwThreadId;

 int         iaddrSize =
sizeof(SOCKADDR_IN);

 

 // Initialize Windows Socket
library

 WSAStartup(0x0202,
&wsaData);

 

 // Create listening
socket

 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, (struct
sockaddr *)&local, sizeof(SOCKADDR_IN));

 

 // Listen

 listen(sListen, 3);

 

 // Create worker
thread

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

 

 while (TRUE)

 {

    // Accept a
connection

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

    printf(“Accepted
client:%s:%d\n”, inet_ntoa(client.sin_addr),
ntohs(client.sin_port));

 

   
g_CliSocketArr[g_iTotalConn] = sClient;

   

    // Allocate a
PER_IO_OPERATION_DATA structure

   
g_pPerIODataArr[g_iTotalConn] =
(LPPER_IO_OPERATION_DATA)HeapAlloc(

      GetProcessHeap(),

     
HEAP_ZERO_MEMORY,

     
sizeof(PER_IO_OPERATION_DATA));

   
g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;

   
g_pPerIODataArr[g_iTotalConn]->Buffer.buf =
g_pPerIODataArr[g_iTotalConn]->szMessage;

   
g_CliEventArr[g_iTotalConn] =
g_pPerIODataArr[g_iTotalConn]->overlap.hEvent =
WSACreateEvent();

 

    // Launch an asynchronous
operation

    WSARecv(

     
g_CliSocketArr[g_iTotalConn],

     
&g_pPerIODataArr[g_iTotalConn]->Buffer,

      1,

     
&g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd,

     
&g_pPerIODataArr[g_iTotalConn]->Flags,

     
&g_pPerIODataArr[g_iTotalConn]->overlap,

      NULL);

   

    g_iTotalConn++;

 }

 

 closesocket(sListen);

 WSACleanup();

 return 0;

}

 

DWORD WINAPI
WorkerThread(LPVOID lpParam)

{

 int   ret, index;

 DWORD cbTransferred;

 

 while (TRUE)

 {

    ret =
WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000,
FALSE);

    if (ret ==
WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)

    {

      continue;

    }

 

    index = ret –
WSA_WAIT_EVENT_0;

   
WSAResetEvent(g_CliEventArr[index]);

 

   
WSAGetOverlappedResult(

     
g_CliSocketArr[index],

     
&g_pPerIODataArr[index]->overlap,

      &cbTransferred,

      TRUE,

     
&g_pPerIODataArr[g_iTotalConn]->Flags);

 

    if (cbTransferred ==
0)

    {

      // The connection was
closed by client

      Cleanup(index);

    }

    else

    {

      //
g_pPerIODataArr[index]->szMessage contains the received
data

     
g_pPerIODataArr[index]->szMessage[cbTransferred] = ‘\0’;

     
send(g_CliSocketArr[index],
g_pPerIODataArr[index]->szMessage,\

        cbTransferred,
0);

 

      // Launch another
asynchronous operation

      WSARecv(

       
g_CliSocketArr[index],

       
&g_pPerIODataArr[index]->Buffer,

        1,

       
&g_pPerIODataArr[index]->NumberOfBytesRecvd,

       
&g_pPerIODataArr[index]->Flags,

       
&g_pPerIODataArr[index]->overlap,

        NULL);

    }

 }

 

 return 0;

}

 

void Cleanup(int index)

{

 closesocket(g_CliSocketArr[index]);

 WSACloseEvent(g_CliEventArr[index]);

 HeapFree(GetProcessHeap(), 0,
g_pPerIODataArr[index]);

 

 if (index < g_iTotalConn

  • 1)

 {

    g_CliSocketArr[index] =
g_CliSocketArr[g_iTotalConn – 1];

    g_CliEventArr[index] =
g_CliEventArr[g_iTotalConn – 1];

    g_pPerIODataArr[index]
= g_pPerIODataArr[g_iTotalConn – 1];

 }

 

 g_pPerIODataArr[–g_iTotalConn]
= NULL;

}

 

那个模型与上述任何模型差别的是它利用Winsock二提供的异步I/O函数WSARecv。在调用WSARecv时,钦赐二个WSAOVE奥迪Q5L应用软件ED结构,那个调用不是阻塞的,也正是说,它会即时回去。1旦有数量达到的时候,被钦赐的WSAOVE途锐L应用程式ED结构中的h伊夫nt被Signaled。由于上面这么些讲话

g_CliEventArr[g_iTotalConn] =
g_pPerIODataArr[g_iTotalConn]->overlap.hEvent;

使得与该套接字相关联的WSAEVENT对象也被Signaled,所以WSAWaitForMultiple伊夫nts的调用操作成功重回。我们今后应该做的正是用与调用WSARecv一样的WSAOVE昂CoraL应用软件ED结构为参数调用WSAGetOverlappedResult,从而赢得此次I/O传送的字节数等有关新闻。在猎取接收的数量后,把多少纹丝不动的发送到客户端,然后重新激活七个WSARecv异步操作。

 

二.用完了例程格局贯彻的重叠I/O模型

#include <WINSOCK2.H>

#include <stdio.h>

 

#define PORT    5150

#define MSGSIZE 1024

 

#pragma comment(lib, “ws2_32.lib”)

 

typedef struct

{

WSAOVERLAPPED overlap;

WSABUF        Buffer;

 char          szMessage[MSGSIZE];

DWORD         NumberOfBytesRecvd;

DWORD         Flags;

SOCKET        sClient;

}PER_IO_OPERATION_DATA,
*LPPER_IO_OPERATION_DATA;

 

DWORD WINAPI WorkerThread(LPVOID);

void CALLBACK CompletionROUTINE(DWORD,
DWORD, LPWSAOVERLAPPED, DWORD);

 

SOCKET g_sNewClientConnection;

BOOL   g_bNewConnectionArrived =
FALSE;

 

int main()

{

 WSADATA     wsaData;

 SOCKET      sListen;

 SOCKADDR_IN local, client;

 DWORD       dwThreadId;

 int         iaddrSize =
sizeof(SOCKADDR_IN);

 

 // Initialize Windows Socket
library

 WSAStartup(0x0202, &wsaData);

 

 // Create listening socket

 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, (struct sockaddr *)&local,
sizeof(SOCKADDR_IN));

 

 // Listen

 listen(sListen, 3);

 

 // Create worker thread

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

 

 while (TRUE)

 {

    // Accept a connection

    g_sNewClientConnection =
accept(sListen, (struct sockaddr *)&client, &iaddrSize);

    g_bNewConnectionArrived = TRUE;

    printf(“Accepted client:%s:%d\n”,
inet_ntoa(client.sin_addr), ntohs(client.sin_port));

 }

}

 

DWORD WINAPI WorkerThread(LPVOID
lpParam)

{

LPPER_IO_OPERATION_DATA lpPerIOData =
NULL;

 

 while (TRUE)

 {

    if (g_bNewConnectionArrived)

    {

      // Launch an asynchronous operation
for new arrived connection

      lpPerIOData =
(LPPER_IO_OPERATION_DATA)HeapAlloc(

        GetProcessHeap(),

        HEAP_ZERO_MEMORY,

       
sizeof(PER_IO_OPERATION_DATA));

      lpPerIOData->Buffer.len =
MSGSIZE;

      lpPerIOData->Buffer.buf =
lpPerIOData->szMessage;

      lpPerIOData->sClient =
g_sNewClientConnection;

     

     
WSARecv(lpPerIOData->sClient,

        &lpPerIOData->Buffer,

        1,

       
&lpPerIOData->NumberOfBytesRecvd,

        &lpPerIOData->Flags,

        &lpPerIOData->overlap,

        CompletionROUTINE);     

     

     g_bNewConnectionArrived =
FALSE;

    }

 

    SleepEx(1000, TRUE);

 }

 return 0;

}

 

void CALLBACK CompletionROUTINE(DWORD
dwError,

                                DWORD
cbTransferred,

                               
LPWSAOVERLAPPED lpOverlapped,

                                DWORD
dwFlags)

{

 LPPER_IO_OPERATION_DATA lpPerIOData =
(LPPER_IO_OPERATION_DATA)lpOverlapped;

 

 if (dwError != 0 || cbTransferred ==
0)

{

    // Connection was closed by
client

 closesocket(lpPerIOData->sClient);

 HeapFree(GetProcessHeap(), 0,
lpPerIOData);

}

 else

 {

   
lpPerIOData->szMessage[cbTransferred] = ‘\0’;

    send(lpPerIOData->sClient,
lpPerIOData->szMessage, cbTransferred, 0);

   

    // Launch another asynchronous
operation

    memset(&lpPerIOData->overlap, 0,
sizeof(WSAOVERLAPPED));

    lpPerIOData->Buffer.len =
MSGSIZE;

    lpPerIOData->Buffer.buf =
lpPerIOData->szMessage;   

 

    WSARecv(lpPerIOData->sClient,

      &lpPerIOData->Buffer,

      1,

     
&lpPerIOData->NumberOfBytesRecvd,

      &lpPerIOData->Flags,

      &lpPerIOData->overlap,

      CompletionROUTINE);

 }

}

 

用完了例程来落实重叠I/O比用事件通报简单得多。在那些模型中,主线程只用不停的承受连接就能够;扶助线程判别有未有新的客户端连接被确立,假如有,就为非常客户端套接字激活三个异步的WSARecv操作,然后调用SleepEx使线程处于壹种可警告的等待状态,以使得I/O完结后CompletionROUTINE能够被基本调用。借使帮助线程不调用SleepEx,则根本在成就二次I/O操作后,无法调用达成例程(因为做到例程的运行应该和当下激活WSARecv异步操作的代码在同一个线程之内)。

做到例程内的完毕代码比较轻巧,它抽出接收到的数量,然后将数据一点儿也不动的发送给客户端,最终再度激活另三个WSARecv异步操作。注意,在此处运用了“尾随数据”。大家在调用WSARecv的时候,参数lpOverlapped实际上指向二个比它大得多的构造PEHaval_IO_OPERATION_DATA,那些组织除了WSAOVE奥德赛LAPPED以外,还被大家附加了缓冲区的构造音讯,其它还包含客户端套接字等主要的音讯。这样,在产生例程中经过参数lpOverlapped得到的不唯有是WSAOVECRUISERL应用软件ED结构,还有前边尾随的隐含客户端套接字和接收数据缓冲区等根本音信。那样的C语言能力在本身后边介绍完了端口的时候还会使用到。

 

五.成就端口模型

“实现端口”模型是时至明日最为复杂的1种I/O模型。然则,若是一个应用程序同时要求管理为数众多的套接字,那么采取这种模型,往往能够达到规定的标准最棒的类别品质!但不幸的是,该模型只适用于Windows NT和Windows 3000操作系统。因其设计的纷纷,惟有在你的应用程序须求同时管住数百以至上千个套接字的时候,而且希望随着系统内安装的CPU数量的充实,应用程序的天性也足以线性升高,才应思虑选取“完成端口”模型。要铭记在心的二个基本准则是,假若要为Windows NT或Windows 2000支付高品质的服务器应用,同时期待为大气套接字I/O请求提供劳务(Web服务器正是那地点的杰出例子),那么I/O完毕端口模型就是极品接纳!(节选自《Windows网络编制程序》第10章)

成功端口模型是自身最心爱的一种模型。就算其促成比较复杂(其实自个儿觉着它的落到实处比用事件通报落实的重叠I/O轻松多了),但其作用是危言耸听的。笔者在T公司的时候曾经帮同事写过二个邮件服务器的属性测试程序,用的就是实现端口模型。结果注解,完结端口模型在多连接(数不完)的情景下,仅仅凭借1七个帮忙线程,就能够直达异常高的吞吐量。上面我照旧从代码说到:

#include
<WINSOCK2.H>

#include
<stdio.h>

 

#define PORT    5150

#define MSGSIZE 1024

 

#pragma comment(lib,
“ws2_32.lib”)

 

typedef enum

{

 RECV_POSTED

}OPERATION_TYPE;

 

typedef struct

{

WSAOVERLAPPED overlap;

WSABUF         Buffer;

 char          
szMessage[MSGSIZE];

DWORD         
NumberOfBytesRecvd;

DWORD          Flags;

OPERATION_TYPE
OperationType;

}PER_IO_OPERATION_DATA,
*LPPER_IO_OPERATION_DATA;

 

DWORD WINAPI
WorkerThread(LPVOID);

 

int main()

{

 WSADATA                
wsaData;

 SOCKET                 
sListen, sClient;

 SOCKADDR_IN            
local, client;

 DWORD                   i,
dwThreadId;

 int                    
iaddrSize = sizeof(SOCKADDR_IN);

 HANDLE                 
CompletionPort = INVALID_HANDLE_VALUE;

 SYSTEM_INFO            
systeminfo;

 LPPER_IO_OPERATION_DATA
lpPerIOData = NULL;

 

 // Initialize Windows Socket
library

 WSAStartup(0x0202,
&wsaData);

 

 // Create completion
port

 CompletionPort =
CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

 

 // Create worker
thread

 GetSystemInfo(&systeminfo);

 for (i = 0; i <
systeminfo.dwNumberOfProcessors; i++)

 {

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

 }

 

 // Create listening
socket

 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, (struct
sockaddr *)&local, sizeof(SOCKADDR_IN));

 

 // Listen

 listen(sListen, 3);

 

 while (TRUE)

 {

    // Accept a
connection

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

    printf(“Accepted
client:%s:%d\n”, inet_ntoa(client.sin_addr),
ntohs(client.sin_port));

 

    // Associate the newly
arrived client socket with completion port

   
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient,
0);

   

    // Launch an asynchronous
operation for new arrived connection

    lpPerIOData =
(LPPER_IO_OPERATION_DATA)HeapAlloc(

      GetProcessHeap(),

     
HEAP_ZERO_MEMORY,

     
sizeof(PER_IO_OPERATION_DATA));

    lpPerIOData->Buffer.len
= MSGSIZE;

    lpPerIOData->Buffer.buf
= lpPerIOData->szMessage;

   
lpPerIOData->OperationType = RECV_POSTED;

    WSARecv(sClient,

     
&lpPerIOData->Buffer,

      1,

     
&lpPerIOData->NumberOfBytesRecvd,

     
&lpPerIOData->Flags,

     
&lpPerIOData->overlap,

      NULL);

 }

 

 PostQueuedCompletionStatus(CompletionPort,
0xFFFFFFFF, 0, NULL);

CloseHandle(CompletionPort);

closesocket(sListen);

WSACleanup();

return 0;

}

 

DWORD WINAPI
WorkerThread(LPVOID CompletionPortID)

{

 HANDLE                 
CompletionPort=(HANDLE)CompletionPortID;

 DWORD                  
dwBytesTransferred;

 SOCKET     
            sClient;

 LPPER_IO_OPERATION_DATA
lpPerIOData = NULL;

 

 while (TRUE)

 {

   
GetQueuedCompletionStatus(

      CompletionPort,

     
&dwBytesTransferred,

      &sClient,

      (LPOVERLAPPED
*)&lpPerIOData,

      INFINITE);

    if (dwBytesTransferred ==
0xFFFFFFFF)

    {

      return 0;

    }

   

    if
(lpPerIOData->OperationType == RECV_POSTED)

    {

      if (dwBytesTransferred
== 0)

      {

        // Connection was
closed by client

       
closesocket(sClient);

       
HeapFree(GetProcessHeap(), 0, lpPerIOData);       

      }

      else

      {

       
lpPerIOData->szMessage[dwBytesTransferred] = ‘\0’;

        send(sClient,
lpPerIOData->szMessage, dwBytesTransferred, 0);

       

        // Launch another
asynchronous operation for sClient

        memset(lpPerIOData, 0,
sizeof(PER_IO_OPERATION_DATA));

       
lpPerIOData->Buffer.len = MSGSIZE;

       
lpPerIOData->Buffer.buf = lpPerIOData->szMessage;

       
lpPerIOData->OperationType = RECV_POSTED;

       
WSARecv(sClient,

         
&lpPerIOData->Buffer,

          1,

         
&lpPerIOData->NumberOfBytesRecvd,

         
&lpPerIOData->Flags,

         
&lpPerIOData->overlap,

          NULL);

      }

    }

 }

return 0;

}

 

 

首先,说说主线程:

一.创建完结端口对象

二.创建工作者线程(这里工小编线程的数额是安分守纪CPU的个数来支配的,那样可以达到规定的规范最好质量)

叁.成立监听套接字,绑定,监听,然后程序进入循环

肆.在循环中,小编做了以下几件专门的学业:

(一).接受二个客户端连接

(二).将该客户端套接字与完毕端口绑定到共同(仍旧调用CreateIoCompletionPort,但此次的效率不一),注意,按道理来讲,此时传递给CreateIoCompletionPort的第4个参数应该是3个成就键,一般来说,程序都是传递一个单句柄数据结构的地方,该单句柄数据包蕴了和该客户端连接有关的音讯,由于大家只关心套接字句柄,所以直接将套接字句柄作为成功键传递;

(三).触发叁个WSARecv异步调用,此番又用到了“尾随数据”,使接收数据所用的缓冲区紧跟在WSAOVE冠道L应用软件ED对象之后,其它,还有操作类型等要害音信。

 

在劳重力线程的巡回中,大家

一.调用GetQueuedCompletionStatus获得此番I/O的相干音信(比如套接字句柄、传送的字节数、单I/O数据结构的地址等等)

贰.通过单I/O数据结构找到接收数据缓冲区,然后将数据维持原状的发送到客户端

3.双重接触贰个WSARecv异步操作

 

多种I/O模型的比较

小编会从以下多少个方面来张开相比较

*有无每线程6四连接数限制

1旦在挑选模型中从不重新定义FD_SETSIZE宏,则每个fd_set暗中同意能够装下陆拾一个SOCKET。一样的,受MAXIMUM_WAIT_OBJECTS宏的震慑,事件选用、用事件通报落实的重叠I/O都有每线程最大64连接数限制。要是连接数数不完,则必须对客户端套接字举行分组,这样,势必扩张程序的复杂度。

相反,异步选用、用完了例程实现的重叠I/O和实现端口不受此限制。

 

*线程数

而外异步选用以外,其余模型起码要求二个线程。贰个主线程和二个相助线程。同样的,假诺连接数大于6四,则选用模型、事件选择和用事件通报落到实处的重叠I/O的线程数还要加进。

 

*兑现的复杂度

自个儿的个人意见是,在落实难度上,异步选用<选拔<用完了例程完结的重叠I/O<事件选用<完成端口<用事件通报得以实现的重叠I/O

 

*性能

是因为选拔模型中年老年是都要重设读集,在select函数再次来到后还要针对具备套接字实行每个测试,小编的痛感是成效相比较差;完结端口和用完了例程达成的重叠I/O基本上不涉及全局数据,效能应该是最高的,而且在多管理器景况下造成端口还要高级中学一年级些;事件选用和用事件通报落实的重叠I/O在促成机制上皆以行使WSAWaitForMultiple伊芙nts,认为功用大致;至于异步选择,倒霉相比较。所以自身的下结论是:选拔<用事件通报落到实处的重叠I/O<事件选用<用完了例程完毕的重叠I/O<完毕端口

 

from:http://www.cppblog.com/changshoumeng/articles/113441.html

相关文章