重叠模型的基本设计原理是让应用程序使用重叠的数据结构,一次投递一个或多个WinsockI/O请求。针对那些提交的请求,在它们完成之后,应用程序可为它们提供服务。模型的总体设计以Windows重叠I/O机制为基础。这个机制可通过ReadFile和WriteFile两个函数,在设备上执行I/O操作。
要想在一个套接字上使用重叠I/O模型,首先必须创建一个设置了重叠标志的套接字。
主要有两种方法来管理重叠I/O的请求。.事件对象通知 .完成实例。
事件通知:
重叠I/O的事件通知方法要求将Windows事件对象与WSAOVERLAPPED结构关联在一起。若使用一个WSAOVERLAPPED结构,发出像WSASend和WSARecv这样的I/O调用,它们会立即返回。
WSAOVERLAPPED结构为重叠I/O请求的初始化及其后续的完成之间提供了一种通信媒介。结构的定义如下:
typedef struct WSAOVERLAPPED
{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
}WSAOVERLAPPED, FAR* LPWSAOVERLAPPED;
Internal,InternalHigh,Offset,OffsetHigh字段均由系统在内部使用,不能有应用程序直接进行处理或使用。hEvent字段则允许应用程序将事件对象的句柄同操作关联起来。
一个重叠I/O完成以后,应用程序要负责获取重叠I/O操作的结果。一个重叠请求操作最终完成之后,在事件通知方法中,Winsock会更改与WSAOVERLAPPED结构关联的事件对象的事件传信状态,将未传信变成已传信。由于已经有一个事件对象分配给WSAOVERLAPPED结构,所有只需简单的调用WSAWaitForMultipleEvents函数,便可判断出重叠I/O调用将在什么时候完成。WSAWaitForMultipleEvents会等待一段指定时间,等待一个或多个事件进入已传信状态。 WSAWaitForMultipleEvents一次只能等待64个事件对象。确定某个重叠事件完成以后,接着需要调用WSAGetOverlappedResult函数,判断这个重叠调用是否成功。
BOOL WSAGetOverlappedResult(
SOCKET s, //重叠操作开始的时候,被指定的套接字
LPWSAOVERLAPPED lpOverlapped, //重叠操作开始的时候,被指定的WSAOVERLAPPED结构
LPDWORD lpcbTransfer,//负责接收一次重叠发送或接收操作实际传输的字节数
BOOL fWait,//用于决定函数是否应该等待挂起的重叠操作完成
LPWORD lpdwFlags //负责接收结果标志
);
若WSAGetOverlappedResult函数调用成功,返回值就是TRUE,意味着重叠操作完成成功,而且lpcbTransfer参数所指向的值已进行了更新,若返回FALSE,那么可能是由以下原因造成的:
.重叠I/O操作仍处于挂起状态
.重叠操作已经完成,但含有错误
.因为在提供给WSAGetOverlappedResult函数的一个或多个参数中存在错误,所有无法判断重叠操作的完成状态
失败后,lpcbTransfer所指向的值不会被更新,而且应用程序应调用WSAGetLastError函数查看错误原因。 利用事件通知机制设计一个简单的服务器应用程序,令其在一个套接字上对重叠I/O操作进行管理:
#define DATA_BUFSIZE 2046 void main(void)
{
WSABUF DataBuf;
char buffer[DATA_BUFSIZE];
DWORD EventTotal = ;
DWORD RecvBytes = ;
DWORD Flags = ;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAOVERLAPPED AcceptOverlapped;
SOCKET ListenSocket, AcceptSocket; //第一步
//启动Winsock,建立监听套接字
...
//第二步
//接收一个入站连接
AcceptSocket = accept(ListenSocket,NULL,NULL);
//第三步
//建立一个重叠结构 EventArray[EventTotal] = WSACreateEvent(); ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = EventArray[EventTotal]; DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer; EventTotal++; //第四步
//接收一个WSARecv请求,以便在套接字上接收数据
if(SOCKET_ERROR ==
WSARecv(AcceptSocket, &DataBuf, , &RecvBytes, &Flags, &AcceptOverlapped, NULL))
{
if(WSA_IO_PENDING != WSAGetLastError())
{
//出错
}
}
//处理套件子上的重叠接收
while(TRUE)
{
DWORD Index;
//第五步
//等候重叠I/O调用结束
Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
//索引应为0,因为EventArray中仅有一个事件 //第六步
//重置已传信事件
WSAResetEvent(EventArray[Index-WSA_WAIT_EVENT_0]);
//第七步
//确定重叠请求的状态
WSAGetOverlappedResult(AcceptSocket,&AcceptOverlapped,&BytesTransferred,FALSE,&Flags); //先 检查看通信对方是否已经关闭连接,如果关闭,则关闭套接字
if(BytesTransferred==)
{
printf("Closing socket %d\n", AcceptSocket);
closesocket(AcceptSocket);
WSACloseEvent(EventArray[Index-WSA_WAIT_EVENT_0]);
return ;
} //对接收到的数据进行某种处理
//DataBuf包含接收到的数据
... //第八步
//在套接字上投递另一个WSARecv请求 Flags = ;
ZeroMemory(&AccpetOverlapped, sizeof(WSAOVERLAPPED)); AcceptOverlapped.hEvent = EventArray[Index-WSA_WAIT_EVENT_0];
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
if(SOCKET_ERROR ==
WSARecv(AcceptSocket, &DataBuf, , &RecvBytes, &Flags, &AcceptOverlapped, NULL))
{
if(WSA_IO_PENDING != WSAGetLastError())
{
//出错
}
}
}
}
对该程序采用的编程步骤总结如下:
.创建一个套接字,开始在指定的端口上监听连接请求
.接受一个入站的连接请求
.为接收的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将该事件对象句柄分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents使用。
.将WSAOVERLAPPED指定为参数,在套接字上投递一个异步WSARecv请求
.使用步骤3的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入已传信状态
.使用WSAGetOverlappedResult函数,判断重叠调用的返回状态
.函数完成后,针对重叠数组,调用WSAResetEvent函数,从而重设事件对象,并对完成的重叠请求进行处理
.在套接字上投递另一个重叠WSARecv请求
.重复步骤5~
这个例子极易扩展,从而提供对多个套接字的支持。方法是将代码的重叠I/O处理部分移至一个对立的线程中,让主应用程序线程为额外的连接请求提供服务。
===================================================================
#include<winsock2.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib"); #define PORT 5050
#define MSGSIZE 1024 typedef struct
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
char szMessage[MSGSIZE];
DWORD NumberOfBytesRecvd;
DWORD Flags;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA; int g_iTotalConn = ;
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 lpParam);
void Cleanup(int index); int main()
{
WSADATA wsaData;
SOCKET sListen, sClient;
SOCKADDR_IN client, local;
DWORD dwThreadId;
int iAddrSize = sizeof(SOCKADDR_IN); WSAStartup(MAKEWORD(,), &wsaData);
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&local, , sizeof(SOCKADDR_IN));
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
local.sin_addr.s_addr = htonl(INADDR_ANY); bind(sListen, (SOCKADDR*)&local, sizeof(SOCKADDR_IN));
listen(sListen, ); CreateThread(NULL, , WorkerThread, NULL, , &dwThreadId); while(TRUE)
{
sClient = accept(sListen, (SOCKADDR*)&client, &iAddrSize);
printf("Accepted Client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
g_CliSocketArr[g_iTotalConn] = sClient;
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_pPerIoDataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();
g_CliEventArr[g_iTotalConn] = g_pPerIoDataArr[g_iTotalConn]->overlap.hEvent; WSARecv(g_CliSocketArr[g_iTotalConn],
&g_pPerIoDataArr[g_iTotalConn]->Buffer,
,
&g_pPerIoDataArr[g_iTotalConn]->NumberOfBytesRecvd,
&g_pPerIoDataArr[g_iTotalConn]->Flags,
&g_pPerIoDataArr[g_iTotalConn]->overlap,
NULL);
g_iTotalConn++;
}
closesocket(sListen);
WSACleanup(); return ;
} DWORD WINAPI WorkerThread(LPVOID lpParam)
{
int ret, index;
DWORD cbTransferred;
while(TRUE)
{
ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, , FALSE);
if(ret==WSA_WAIT_FAILED || ret==WSA_WAIT_TIMEOUT)
{
//如果当前没有客户端的话,要sleep一下,要不然CUP会占50%以上
if(g_iTotalConn==)
Sleep();
continue;
}
index = ret - WSA_WAIT_EVENT_0;
WSAResetEvent(g_CliEventArr[index]);
WSAGetOverlappedResult(g_CliSocketArr[index],
&g_pPerIoDataArr[index]->overlap,
&cbTransferred,
TRUE,
&g_pPerIoDataArr[index]->Flags);
if(cbTransferred==)
{
Cleanup(index);
}
else
{
g_pPerIoDataArr[index]->szMessage[cbTransferred] = '\0';
send(g_CliSocketArr[index],g_pPerIoDataArr[index]->szMessage,cbTransferred,); WSARecv(g_CliSocketArr[index],
&g_pPerIoDataArr[index]->Buffer,
,
&g_pPerIoDataArr[index]->NumberOfBytesRecvd,
&g_pPerIoDataArr[index]->Flags,
&g_pPerIoDataArr[index]->overlap,
NULL);
}
}
return ;
} void Cleanup(int index)
{
closesocket(g_CliSocketArr[index]);
WSACloseEvent(g_CliEventArr[index]);
HeapFree(GetProcessHeap(), , g_pPerIoDataArr[index]);
if(index<g_iTotalConn-)
{
g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn-];
g_CliEventArr[index] = g_CliEventArr[g_iTotalConn-];
g_pPerIoDataArr[index] = g_pPerIoDataArr[g_iTotalConn-];
}
g_pPerIoDataArr[--g_iTotalConn] = NULL;
} 完成例程:
完成例程是应用程序用来管理完成的重叠I/O请求的另一种方法。完成例程其实就是一些函数,我们将这些函数传递给重叠I/O请求,以供重叠I/O请求完成时由系统调用。它们的设计宗旨,是通过调用者的线程,为已完成的I/O请求提供服务。除此以外,应用程序可通过完成例程,继续进行重叠I/O的处理。
如果希望用完成例程为重叠I/O请求提供服务,应用程序必须为一个绑定I/O的Winsock函数指定一个完成例程,同时指定一个WSAOVERLAPPED结构。一个完成例程必须拥有下述函数原型:
void CALLBACK CompletionROUTINE(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
);
.dwError参数表明一个重叠操作的完成状态时什么
.cbTransferred参数指明了在重叠操作期间,实际传输的字节量是多大
.lpOverlapped参数指明传递到最初的I/O调用内的一个WSAOVERLAPPED结构
.dwFlags参数返回操作结束时可能用的标志
在用一个完成例程提交的重叠请求与用一个事件对象提交的重叠请求之间,存在着一个非常重要的区别。WSAOVERLAPPED的hEvent并未被使用,也就是说,不可以将一个事件对象同重叠请求关联在一起。用完成例程发出重叠I/O调用之后,调用线程一旦完成,最终必须为完成例程提供服务。这样,便要求我们将调用线程置于一种警觉等待状态。并在I/O操作完成后,对完成例程加以处理。WSAWaitForMultipleEvents可以将线程置于一种警觉的等待状态。这样做的缺点在于,我们必须还有一个事件对象可用于WSAWaitForMultipleEvents函数。假定应用程序用完成例程只对重叠请求进行处理,便不大可能有什么事件对象需要处理。作为一种变通方法,应用程序可用SleepEx函数将线程置于一种警觉的等待状态。当然,也可创建一个伪事件对象,它不与任何东西关联在一起。假如调用线程总是处于繁忙状态,而不是处于一种警觉的等待状态,那么根本不会有被投递的完成例程会得到调用。
WSAWaitForMultipleEvents通常会等待同WSAOVERLAPPED关联在一起的事件对象。该函数也用来将线程置于一种警觉的等待状态,并可为已经完成的重叠I/O请求进行完成例程的处理(前提是将fAlertable设为TRUE)。用完成例程接收重叠I/O请求之后,返回值是WSA_IO_COMPLETION,而不是事件数组中的一个事件对象的索引。SleepEx实际上和WSAWaitForMultipleEvents差不多,只是它不需要事件对象。
DWORD SleepEx(
DWORD dwMilliseconds,
BOOL bAlertable
);
dwMilliseconds定义了SleepEx函数的等待时间,以毫秒为单位,如果将dwMilliseconds设为INFINITE,那么SleepEx会无休止的等待下去。
bAlertable指定了完成例程的执行方式。假如将bAlertable设为FALSE,而且进行了一次I/O完成回叫,那么I/O完成函数就不会执行,而且该函数也不会返回。除非超过由dwMilliseconds规定的时间。若设为TRUE,那么完成例程便会得到执行,同时SleepEx函数返回WAIT_IO_COMPLETION。
下面代码演示了如果构建一个简单的服务器应用程序,令其采用前述方法,通过完成例程来实现对一个套接字请求管理:
#define DATA_BUFSIZE 4096 SOCKET AcceptSocket, ListenSocket;
WSABUF DataBuf;
char buffer[DATA_BUFSIZE];
WSAEVENT EventArray[MAXIMUM_WAIT_OBJECTS];
DWORD Flags, RecvBytes, Index; void main(void)
{
WSAOVERLAPPED Overlapped;
//第一步
//启动Winsock,建立监听套接字
... //第二步
//接受一个新连接
AcceptSocket = accept(ListenSocket, NULL, NULL); //第三步
//已经有一个接收套接字之后,开始使用带有完成例程的重叠I/O来处理I/O
//为了启动重叠I/O处理,先提交一个重叠WSARecv请求 Flags = ; ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED)); DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer; //第四步
//将WSAOVERLAPPED结构指定为一个参数,在套接字上投递一个异步WSARecv请求并提供下面的
//作为完成例程的WorkerRoutine函数 if(SOCKET_ERROR = WSARecv(AcceptSocket, &DataBuf, , &RecvBytes, &Flags, &Overlapped, WorkerRoutine))
{
if(WSA_IO_PENDING != WSAGetLastError())
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return ;
}
} //因为WSAWaitForMultipleEvents()API要求在一个或多个事件对象上等待,
//因此不得不创建一个伪事件对象。作为一种可选方案,也可使用SleepEx作为替代 EventArray[] = WSACreateEvent(); while(TRUE)
{
//第五步
Index = WSAWaitForMultipleEvents(, EventArray, FALSE, WSA_INFINITE, TRUE);
//第六步
if(Index==WSA_IO_COMPLETION)
{
//一个重叠请求完成例程结束,继续为更多的完成例程服务
continue;
}
else
{
//发生一个错误,停止处理
//如果正在处理一个事件对象,那么这也就可能是事件数组的一个索引
return ;
}
}
} void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred,
LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
DWORD RecvBytes, SendBytes;
DWORD Flags; if(Error!=||BytesTransferred==)
{
//要么是套接字上发生了一个错误,要么套接字已经被通信对方关闭
closesocket(AcceptSocket);
return ;
} //此刻,一个重叠的WSARecv请求顺利完成
//现在可接收DataBuf变量中包含的已接收的数据了
//处理完接收到的数据后,需要投递另外一个重叠的WSARecv或WSASend请求
//为简便起见,这里投递另外一个WSARecv请求 Flags = ; ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED)); DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer; if(SOCKET_ERROR = WSARecv(AcceptSocket, &DataBuf, , &RecvBytes, &Flags, &Overlapped, WorkerRoutine))
{
if(WSA_IO_PENDING != WSAGetLastError())
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return ;
}
}
}
程序主要的执行步骤:
.新建一个套接字,开始在指定端口上监听传入的连接
.接收一个入站连接
.为接收的套接字创建一个WSAOVERLAPPED结构
.在套接字上投递一个异步WSARecv请求,需要将WSAOVERLAPPED指定为一个参数,同时提供一个完成例程
.在将fAlertable设为TRUE的前提下,调用WSAWaitForMultipleEvents,并等待一个重叠I/O请求完成。重叠请求完成后,完成例程会自动执行,而且WSAWaitForMultipeEvents会返回一个WSA_IO_COMPLETION。在完成例程内,可用一个完成例程投递另一个重叠WSARecv请求。
.检查WSAWaitForMultipleEvents是否返回WSA_IO_COMPLETION
.重复步骤5~
重叠模型提供高性能的套接字I/O,因为使用这种模型的应用程序通知缓冲区收发系统直接使用的数据,所有跟前面几种不同。也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已到达套接字,则该数据将直接被拷贝到投递的缓冲区。在前述模型中,数据到达并被拷贝到单套接字接收缓冲区中,此时应用程序会被告知可以读入的容量。当应用程序调用接收函数之后,数据将从单套接字缓冲区拷贝到应用程序的缓冲区。
在事件中使用重叠I/O的缺点,也就是每次最多只能等待64个事件这一局限性。完成例程是一个不错的替代方案,但必须注意确保投递完成操作的线程进入警觉的等待状态,以便使完成例程能够圆满的结束。同时,还要确保完成例程不要做过量的运算,以便在很重的负载之下,这些完成过程能够尽快开始运行。
=============================================================
#include <stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "ws2_32.lib") #define PORT 5050
#define MSGSIZE 1024 typedef struct
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
char szMessage[MSGSIZE];
DWORD NumberOfBytesRecvd;
DWORD Flags;
SOCKET sClient;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA; int g_iTotalConn = ;
SOCKET g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
LPPER_IO_OPERATION_DATA g_pPerIoDataArr[MAXIMUM_WAIT_OBJECTS];
SOCKET g_sNewClientConnection = NULL;
BOOL g_bNewConnectionArrived = FALSE; DWORD WINAPI WorkerThread(LPVOID lpParam);
void CALLBACK CompletionRoutine(DWORD dwError, DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags); int main()
{
WSADATA wsaData;
SOCKET sListen;
SOCKADDR_IN local, client;
int iAddrSize = sizeof(SOCKADDR_IN);
DWORD dwThreadId;
WSAStartup(MAKEWORD(,), &wsaData);
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&local, , sizeof(SOCKADDR_IN));
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
local.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sListen, (SOCKADDR*)&local, sizeof(SOCKADDR_IN));
listen(sListen, ); CreateThread(NULL, , WorkerThread, NULL, , &dwThreadId); while(TRUE)
{
g_sNewClientConnection = accept(sListen, (SOCKADDR*)&client, &iAddrSize);
g_bNewConnectionArrived = TRUE;
printf("Accepted Client:%s:%d \n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
}
return ;
} DWORD WINAPI WorkerThread(LPVOID lpParam)
{
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
while(TRUE)
{
if(g_bNewConnectionArrived)
{
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,
,
&lpPerIOData->NumberOfBytesRecvd,
&lpPerIOData->Flags,
&lpPerIOData->overlap,
CompletionRoutine);
g_bNewConnectionArrived = FALSE;
}
SleepEx(,TRUE);
}
return ;
} void CALLBACK CompletionRoutine(DWORD dwError, DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
LPPER_IO_OPERATION_DATA lpPerIOData = (LPPER_IO_OPERATION_DATA)lpOverlapped;
if(dwError!=||cbTransferred==)
{
closesocket(lpPerIOData->sClient);
HeapFree(GetProcessHeap(),,lpPerIOData);
}
else
{
lpPerIOData->szMessage[cbTransferred] = '\0';
send(lpPerIOData->sClient, lpPerIOData->szMessage, cbTransferred, ); memset(&lpPerIOData->overlap, , sizeof(WSAOVERLAPPED));
lpPerIOData->Buffer.len = MSGSIZE;
lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
WSARecv(lpPerIOData->sClient,
&lpPerIOData->Buffer,
,
&lpPerIOData->NumberOfBytesRecvd,
&lpPerIOData->Flags,
&lpPerIOData->overlap,
CompletionRoutine);
}
}
用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中,主线程只用不停的接受连接即可;辅助线程判断有没有新的客户端连接被建立,如果有,就为那个客户端套接字激活一个异步的WSARecv操作,然后调用SleepEx使线程处于一种可警告的等待状态,以使得I/O完成后 CompletionROUTINE可以被内核调用。如果辅助线程不调用SleepEx,则内核在完成一次I/O操作后,无法调用完成例程(因为完成例程的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内)。
完成例程内的实现代码比较简单,它取出接收到的数据,然后将数据原封不动的发送给客户端,最后重新激活另一个WSARecv异步操作。注意,在这里用到了 “尾随数据”。我们在调用WSARecv的时候,参数lpOverlapped实际上指向一个比它大得多的结构 PER_IO_OPERATION_DATA,这个结构除了WSAOVERLAPPED以外,还被我们附加了缓冲区的结构信息,另外还包括客户端套接字等重要的信息。这样,在完成例程中通过参数lpOverlapped拿到的不仅仅是WSAOVERLAPPED结构,还有后边尾随的包含客户端套接字和接收数据缓冲区等重要信息。这样的C语言技巧在我后面介绍完成端口的时候还会使用到。

套接字I/O模型-重叠I/O的更多相关文章

  1. 套接字I/O模型-select

    共有6种类型套接字I/O模型.blocking(阻塞),select(选择),WSAAsyncSelect(异步选择),WSAEventSelect(事件选择),overlapped(重叠),comp ...

  2. 套接字I/O模型之WSAEventSelect

    今天我又学习了一种新的套接字I/O模型------WSAEventSelect,他与WSAAsyncSelect一样也是一种异步事件通知模型,不同的是WSAAsyncSelect是与窗口句柄关联在一起 ...

  3. 套接字I/O模型-WSAAsyncSelect

    利用这个异步I/O模型,应用程序可在一个套接字上接收以Windows消息为基础的网络事件通知.WSAAsyncSelect和WSAEventSelect提供读写数据能力的异步通知,但它们不提供异步数据 ...

  4. 套接字I/O模型-完成端口IOCP

    “完成端口”模型是迄今为止最为复杂的一种I/O模型.然而,假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!但不幸的是,该模型只适用于Windows NT和W ...

  5. Windsock套接字I/O模型学习 --- 第一章

    1. I/O模型共有以下几种: 阻塞(blocking)模型 选择(select)模型 WSAAsyncSelect模型 WSAEventSelect模型 重叠(overlapped)模型 完成端口( ...

  6. 套接字I/O模型-WSAEventSelect(转载)

    和WSAAsyncSelect类似,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知. 该模型最主要的区别是在于网络事件是由对象句柄完成的,而不是通过窗口例程完成. 事件通知 事件 ...

  7. Linux下套接字具体解释(三)----几种套接字I/O模型

    參考: 网络编程–IO模型演示样例 几种server端IO模型的简介及实现 背景知识 堵塞和非堵塞 对于一个套接字的 I/O通信,它会涉及到两个系统对象.一个是调用这个IO的进程或者线程,还有一个就是 ...

  8. Windsock套接字I/O模型学习 --- 第三章

    1. WSAAsyncSelect 模型 WSAAsyncSelect 模型比较简单,是为了适应Windows的消息驱动环境而设置的,WSAAsyncSelect 函数自动把套接字设为非阻塞模式.MF ...

  9. Windsock套接字I/O模型学习 --- 第二章

    1. select模型 select模型主要借助于apiselect来实现,所以先介绍一下select函数 int select( int nfds, // 忽略,仅是为了与 Berkeley 套接字 ...

随机推荐

  1. nslayoutConstraint

    1.vfl的正确编写格式 NSDictionary *dict1 = NSDictionaryOfVariableBindings(_boxV,_headerL,_imageV,_backBtn,_d ...

  2. SYN Cookie的原理和实现

          本文主要内容:SYN Cookie的原理,以及它的内核实现. 内核版本:3.6 SYN Flood 下面这段介绍引用自[1]. SYN Flood是一种非常危险而常见的Dos攻击方式.到目 ...

  3. Interview----2 sum

    题目:输入一个已经按升序排序过的数组和一个数字, 在数组中查找两个数,使得它们的和正好是输入的那个数字. 要求时间复杂度是 O(n).如果有多对数字的和等于输入的数字,输出任意一对即可. 例如输入数组 ...

  4. PHP面向对象中常用的关键字和魔术方法

    PHP面向对象中常用的关键字 final        1.final不能修饰成员属性(类中常量不是用这个关键字)        2.final只能修饰类和方法 作用:            使用fi ...

  5. Redis - 密码配置和主从复制

    使用config set命令修改requirepass参数配置Redis密码config set requirepass password 也可以通过配置文件修改密码,重启后生效. 克隆虚拟机,分别运 ...

  6. Android绘图之渐隐动画

    实现了一个有趣的小东西:使用自定义View绘图,一边画线,画出的线条渐渐变淡,直到消失.效果如下图所示: 用属性动画或者渐变填充(Shader)可以做到一笔一笔的变化,但要想一笔渐变(手指不抬起边画边 ...

  7. HDU2222 (AC自动机)

    AC自动机模板题. 被卡内存了 死活A不掉.. AC自动机参考教程: http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html ...

  8. DBImg: 图片文件-二进制文件的转换

    using System; using System.IO; using System.Drawing; //using System.Collections.Generic; //using Sys ...

  9. OpenFlow Switch学习笔记(三)——Flow Tables

    这次我们主要讨论下OpenFlow Switch的核心组件之一——Flow Tables,以了解其内部的 matching 以及 action handling 机制.下文将会分为几个部分来逐步详述O ...

  10. genome file format

    Some of the bedtools (e.g., genomeCoverageBed,complementBed, slopBed) need to know the size of the c ...