socket-重叠模型(overlap)

socket-重叠模型(overlap)
重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构,一次投递一个或多个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 ", WSAGetLastError() );
return;
}
printf("WSAStartup success ");


// Step 1:
// Start Winsock and set up a listening socket
printf("create socket... ");
ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if( ListenSocket == INVALID_SOCKET )
{
showerror( "socket" );
return;
}
printf("create socket success ");

addr.sin_family = AF_INET;
addr.sin_port = htons( MYPORT );
addr.sin_addr.s_addr = inet_addr( MYIP );
printf("bind socket... ");
nResult = bind( ListenSocket, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) );
if( nResult == SOCKET_ERROR )
{
showerror( "bind" );
return;
}
printf("bind socket success ");

printf("listen socket... ");
nResult = listen( ListenSocket, 5 );
if( nResult == SOCKET_ERROR )
{
showerror( "listen" );
return;
}
printf("listen socket success ");

// 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 ", 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: 描述: %s ", function, (char*)lpMsgBuf);

//释放内存
LocalFree( lpMsgBuf );
}

 
原文地址:https://www.cnblogs.com/S-volcano/p/5032087.html