重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构,一次投递一个或多个Winsock I/O请求。针对那些提交的请求,在它们完成之后,应用程序可为它们提供服务。该模型适用于除Windows CE之外的各种Windows平台。模型的总体设计以Win32重叠I/O机制为基础。那个机制可以通过ReadFile和WriteFile两个函数,针对设备执行I/O操作。
关键是理解“重叠”两个字,就是你把发送的数据交给系统,然后自己做别的事情,在你干自己的事情时,系统同时也正在完成你交给他的任务,两者同时进行。系统完成后会调用你给他的代码或者通知你。所以,“重叠”指的是时间上的重叠。
要想在一个套结字上使用重叠I/O模型,首先必须使用WSA_FLAG_OVERLAPPED这个标志,创建一个套结字。如下所示:
s = WSASocket( AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED );
创建套结字时,假如使用的是socket函数,而非WSASocket函数,那么会默认设置WSA_FLAG_OVERLAPPED标志。该标志可与下面函数相关联:
WSASend
WSASendTo
WSARecv
WSARecvFrom
WSAIoctl
AcceptEx
TransmitFile
若随一个WSAOVERLAPPED结构一起调用这些函数,函数会立即完成并返回,无论套结字是否设置为阻塞模式。主要有两个方法用来管理一个重叠I/O请求的完成:我们的应用程序客等待“事件对象通知”,亦可通过“完成例程”,对已完成的请求加以处理。
编写一个简单重叠I/O模式服务器程序,基本步骤如下:
1)创建一个套结字,开始在指定的端口上监听连接请求。
2)接受一个进入的连接请求。
3)为接受的套结字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后WSAWaitForMultipleEvents函数使用。
4)在套结字上投递一个异步WSARecv请求,指定参数为WSAOVERLAPPED结构。(函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDING)。
5)使用步骤3)的事件数组,调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(等待那个事件触发)。
6)WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
7)使用WSAGetOverlappedResult函数,判断重叠调用的返回状态是什么。
8)在套结字上投递另一个重叠WSARecv请求。
9)重复步骤5)~8)。
下面附上源代码
Server端代码:
#include <stdio.h>
#include <Winsock2.h>
#pragma comment(lib, "WS2_32.lib")
#define MYPORT 8001
#define MYIP "127.0.0.1"
#define DATA_BUFSIZE 1024
void showerror(const char* function);
void main()
{
WORD wVersion = MAKEWORD( 2, 0 );
WSADATA wsdata;
WSABUF DataBuf;
DWORD EventTotal = 0;
DWORD RecvBytes = 0;
DWORD BytesTransferred = 0;
DWORD Flags = 0;
DWORD Index = 0;
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
WSAOVERLAPPED AcceptOverlapped;
SOCKET ListenSocket, AcceptSocket;
struct sockaddr_in addr;
int addrlen = sizeof(struct sockaddr);
char szbuffer[DATA_BUFSIZE];
// 加载socket2.0 dll
int nResult = WSAStartup( wVersion, &wsdata );
if( nResult != 0 )
{
printf( "error in function WSAStartup(): %d\n", WSAGetLastError() );
return;
}
printf("WSAStartup success\n");
// Step 1:
// Start Winsock and set up a listening socket
printf("create socket...\n");
ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if( ListenSocket == INVALID_SOCKET )
{
showerror( "socket" );
return;
}
printf("create socket success\n");
addr.sin_family = AF_INET;
addr.sin_port = htons( MYPORT );
addr.sin_addr.s_addr = inet_addr( MYIP );
printf("bind socket...\n");
nResult = bind( ListenSocket, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) );
if( nResult == SOCKET_ERROR )
{
showerror( "bind" );
return;
}
printf("bind socket success\n");
printf("listen socket...\n");
nResult = listen( ListenSocket, 5 );
if( nResult == SOCKET_ERROR )
{
showerror( "listen" );
return;
}
printf("listen socket success\n");
// Step 2:
// Accept an inbound connection
AcceptSocket = accept( ListenSocket, NULL, NULL );
// Step 3:
// Set up an overlapped structure
EventArray[EventTotal] = WSACreateEvent();
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = EventArray[EventTotal];
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = szbuffer;
EventTotal++;
// Step 4:
// Post a WSARecv request to begin receiving data on the socket
WSARecv( AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL );
while(TRUE)
{
// Step 5:
// Wait for the overlapped I/O call to complete
Index = WSAWaitForMultipleEvents( EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE );
// Step 6:
// Reset the signaled event
WSAResetEvent( EventArray[Index - WSA_WAIT_EVENT_0] );
// Step 7:
// Determine the status of the overlapped
WSAGetOverlappedResult( AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags );
// First check to see whether the peer has closed the connection,
// and if so, close the socket
if( BytesTransferred == 0 )
{
showerror( "Closing socket" );
closesocket( AcceptSocket );
WSACloseEvent( EventArray[Index - WSA_WAIT_EVENT_0] );
return;
}
// Do someting with the received data
// DataBuf contains the received data
printf( "Received: %s\n", DataBuf.buf );
// Step 8:
// Post another WSARecv() request on the socket
Flags = 0;
ZeroMemory( &AcceptOverlapped, sizeof(AcceptOverlapped) );
AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = szbuffer;
WSARecv( AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL );
}
// 关闭socket
closesocket( ListenSocket );
// 卸载socket2.0 dll
WSACleanup();
}
void showerror(const char* function)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,0, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //默认语言
(LPTSTR)&lpMsgBuf, 0, NULL );
printf("error in function %s:\n描述: %s\n", function, (char*)lpMsgBuf);
//释放内存
LocalFree( lpMsgBuf );
}
来源: <socket-重叠模型(overlap)_Focus_新浪博客>