您的位置:首页 > 理论基础 > 计算机网络

Overlapped I/O模型--事件通知【摘录自《Windows网络编程》】

2016-01-25 16:34 696 查看
原文:http://www.cnblogs.com/NeuqUstcIim/archive/2008/08/16/1269479.html

重叠I / O的事件通知方法要求将Wi n 3 2事件对象与W S A O V E R L A P P E D结构关联在一起。若使用一个W S A O V E R L A P P E D结构,发出像W S A S e n d和W S A R e c v这样的I / O调用,它们会立即返回。
一个重叠I / O请求最终完成后,我们的应用程序要负责取回重叠I / O操作的结果。一个重叠请求操作最终完成之后,在事件通知方法中, Wi n s o c k会更改与一个W S A O V E R L A P P E D结构对应的一个事件对象的事件传信状态,将其从“未传信”变成“已传信”。 由于一个事件对象

已分配给W S A O V E R L A P P E D结构,所以只需简单地调用W S AWa i t F o r M u l t i p l e E v e n t s函数,从而判断出一个重叠I / O调用在什么时候完成。

注意: W S AWa i t F o r M u l t i p l e E v e n t s返回只是说明重叠IO操作完成,但是是成功的完成还是失败的完成还要调用W S A G e t O v e r l a p p e dR e s u l t(取得重叠结构)函数

如W S A G e t O v e r l a p p e d R e s u l t函数调用成功,返回值就是T R U E。这意味着我们的重叠I / O操作已成功完成,而且由l p c b Tr a n s f e r参数指向的值已进行了更新。

我们向大家阐述了如何编制一个简单的服务器应用,令其在一个套接字上对重叠I / O操作进行管理,程序完全利用了前述的事件通知机制。对该程序采用的编程步骤总结如下:
1) 创建一个套接字,开始在指定的端口上监听连接请求。
2) 接受一个进入的连接请求。
3) 为接受的套接字新建一个W S A O V E R L A P P E D结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后由W S AWa i t F o r M u l t i p l e E v e n t s函数使用。
4) 在套接字上投递一个异步W S A R e c v请求,指定参数为W S A O V E R L A P P E D结构。
注意函数通常会以失败告终,返回S O C K E T _ E R R O R错误状态W S A _ I O _ P E N D I N G
(I/O操作尚未完成)。
5) 使用步骤3 )的事件数组,调用W S AWa i t F o r M u l t i p l e E v e n t s函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件的“触发”)。
6) WSAWa i t F o r M u l t i p l e E v e n t s函数完成后,针对事件数组,调用W S A R e s e t E v e n t(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
7) 使用W S A G e t O v e r l a p p e d R e s u l t函数,判断重叠调用的返回状态是什么。
8) 在套接字上投递另一个重叠W S A R e c v请求。

9) 重复步骤5 ) ~ 8 )。

点击(此处)折叠或打开

#include "stdafx.h"

#include <winsock2.h>

#define DATA_BUFSIZE 4096

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])

{

DWORD EventTotal = 0,RecvBytes = 0, Flags = 0;

char buffer[DATA_BUFSIZE];

WSABUF DataBuf;

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

WSAOVERLAPPED AcceptOverlapped;

SOCKET Listen,Accept;

//step1:

//start Winsock and set up a listening socket

WSADATA wsaData;

WSAStartup(MAKEWORD(2,2), &wsaData);

ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

u_short port = 27015;

char* ip;

sockaddr_in service;

service.sin_family = AF_INET;

service.sin_port = htons(port);

hostent* thisHost;

thisHost = gethostbyname("");

ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);

service.sin_addr.s_addr = inet_addr(ip);

//-----------------------------------------

// Bind the listening socket to the local IP address

// and port number

bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));

//-----------------------------------------

// Set the socket to listen for incoming

// connection requests

listen(ListenSocket, 1);

printf("Listening\n");

//step2:

Accept=accept(Listen,NULL,NULL);

//step3:

//set up an overlapped structure

EventArray[EventTotal]=WSACreateEvent();//先存到事件数组中

ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));

AcceptOverlapped.hEvent = EventArray[EventTotal];

DataBuf.len = DATA_BUFSIZE;

DataBuf.buf =buffer;

EventTotal++;

//step4:

//投递WSARecv准备在Accept套接字上接收数据

WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);

while (TRUE){

//step5:

//等待overlapped IO调用的完成

DWORD Index;

Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

//step6:

// Reset the signaled event

WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

//step7:

// Determine the status of the overlapped event

WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags);

// If the connection has been closed, close the accepted socket

if (BytesTransferred == 0) {

printf("Closing Socket %d\n", AcceptSocket);

closesocket(AcceptSocket);

WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

return;

}

// If data has been received,do something with received data

//DataBuf contains the received data

//step8:

//post another WSARecv () request on the socket

Flag=0;

ZeroMemory(&AcceptOverlapped,sizeof(WSAOVERLAPPED));

AcceptOverlapped.hEvent=EventArray[Index - WSA_WAIT_EVENT_0];//该事件对象已经被复位

DataBuf.len = DATA_BUFSIZE;

DataBuf.buf =buffer;

WSARecv(Accept,&DataBuf,1,&RecvBytes,&Flag,&AcceptOverlapped,NULL);

}

return 0;

}

改进后的程序(并非实际可以运行的程序,只是为了理清思路):

点击(此处)折叠或打开

#define LISTEN_PORT 5000

#define DATA_BUFSIZE 8192

#define POST_RECV 0X01

#define POST_SEND 0X02

int main( )

{

LPPER_HANDLE_DATA lpPerHandleData;

SOCKET hListenSocket;

SOCKET hClientSocket;

SOCKADDR_IN ClientAddr;

int nAddrLen;

HANDLE hThread;

// Start WinSock and create a listen socket.

listen(hListenSocket, 5);

for (; ;)

{

nAddrLen = sizeof(SOCKADDR);

hClientSocket = accept(hListenSocket, (LPSOCKADDR)&ClientAddr, &nAddrLen);

lpPerHandleData = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));

lpPerHandleData->hSocket = hClientSocket;

// 注意这里将连接的客户端的IP地址,保存到了lpPerHandleData字段中了

strcpy(lpPerHandleData->szClientIP, inet_ntoa(ClientAddr.sin_addr));

// 为本次客户请求产生子线程

hThread = CreateThread(

NULL,

0,

OverlappedThread,

lpPerHandleData, // 将lpPerHandleData传给子线程

0,

NULL

);

CloseHandle(hThread);

}

return 0;

}

DWORD WINAPI OverlappedThread(LPVOID lpParam)

{

LPPER_HANDLE_DATA lpPerHandleData = (LPPER_HANDLE_DATA)lpParam;

WSAOVERLAPPED Overlapped;

WSABUF wsaBuf;

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];//事件对象数组

DWORD dwEventTotal = 0, // 程序中事件的总数

char Buffer[DATA_BUFSIZE];

BOOL bResult;

int nResult;

EventArray[dwEventTotal] = WSACreateEvent();

ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

Overlapped.hEvent = EventArray[dwEventTotal]; // 关联事件

ZeroMemory(Buffer, DATA_BUFSIZE);

wsaBuf.buf = Buffer;

wsaBuf.len = sizeof(Buffer);

lpPerHandleData->nOperateType = POST_RECV; // 记录本次操作是Recv(..)

dwEventTotal ++; // 总数加一

dwFlags = 0;

nResult = WSARecv(

lpPerHandleData->hSocket, // Receive socket

&wsaBuf, // 指向WSABUF结构的指针

1, // WSABUF数组的个数

&dwNumOfBytesRecved, // 存放当WSARecv完成后所接收到的字节数,实际接收到的字节数

&dwFlags, // A pointer to flags

&Overlapped, // A pointer to a WSAOVERLAPPED structure

NULL // A pointer to the completion routine,this is NULL

);

if ( nResult == SOCKET_ERROR && GetLastError() != WSA_IO_PENDING)

{

printf("WSARecv(..) failed.\n");

free(lpPerHandleData);

closesocket(lpPerHandleData->hSocket;

WSACloseEvent(EventArray[dwEventTotal]);

return -1;

}

while (TRUE)

{

DWORD dwIndex;

dwIndex = WSAWaitForMultipleEvents(dwEventTotal, EventArray ,

FALSE ,WSA_INFINITE,FALSE);

WSAResetEvent(EventArray[dwIndex– WSA_WAIT_EVENT_0]);

bResult = WSAGetOverlappedResult(

lpPerHandleData->hSocket,

&Overlapped,

&dwBytesTransferred, // 当一个同步I/O完成后,接收到的字节数

TRUE, // 等待I/O操作的完成

&dwFlags

);

if (bResult == FALSE && WSAGetLastError() != WSA_IO_INCOMPLETE)

{

printf("WSAGetOverlappdResult(..) failed.\n");

free(lpPerHandleData);

return 0; // 错误退出

}

if (dwBytesTransferred == 0)

{

printf("客户端已退出,将断开与之的连接!\n");

closesocket(lpPerHandleData->hSocket);

free(lpPerHandleData);

break;

}

// 在这里将接收到的数据进行处理

printf("Received from IP: %s.\ndata: %s\n", lpPerHandleData->szClientIP, wsaBuf.buf);

// 发送另外一个请求操作

ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

lpPerHandleData->nOperateType = POST_RECV;

dwFlags = 0;

nResult = WSARecv();

if (){}

}

return 1;

}

最后的一个改进版本,看上去是个不错的版本,相对来说算是比较实用的。但是使用了过多的全局变量,代码是C风格的,不可取。

点击(此处)折叠或打开

#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);

//网上的代码如此,好奇怪,为什么是在连接还没开始的时候就创建一个线程,而不是像上面的程序一样,accept一个

//connection创建一个线程,并将从connection 的socket中获取的信息当作形参传递给workThread?

//这里是只创建一个线程为重叠socket I/O操作服务

// Create worker thread

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

while (TRUE)

{

// Accept a connection

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

//这很好,在accept函数中的后两个形参中获取client的相关信息,并直接在server端的控制台中显示出来

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

g_CliSocketArr[g_iTotalConn] = sClient;//添加到socket数组中

// Allocate a PER_IO_OPERATION_DATA structure

g_pPerIODataArr[g_iTotalConn] = ER(LPPER_IO_OPATION_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]->eszMssage;

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;

}

这个模型与上述其他模型不同的是它使用Winsock2提供的异步I/O函数WSARecv。在调用WSARecv时,指定一个WSAOVERLAPPED结构,这个调用不是阻塞的,也就是说,它会立刻返回。一旦有数据到达的时候,被指定的WSAOVERLAPPED结构中的hEvent被Signaled。由于下面这个语句g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent;

使得与该套接字相关联的WSAEVENT对象也被Signaled,所以WSAWaitForMultipleEvents的调用操作成功返回。我们现在应该做的就是与调用WSARecv相同的WSAOVERLAPPED结构为参数调用WSAGetOverlappedResult,从而得到本次I/O传送的字节数等相关信息。在取得接收的数据后,把数据原封不动的发送到客户端,然后重新激活一个WSARecv异步操作。



阅读(523) | 评论(0) | 转发(0) |

0
上一篇:Linux C++中需要的头文件

下一篇:【转】Linux AIO机制

相关热门文章

Serv-u的ODBC数据库做法(完整...

10种启动故障解决方法(转)...

Auto CAD 2007中文版 迅雷免费...

关于自动弹出网页的解决方法...

出现“high definition audio...

linux dhcp peizhi roc

关于Unix文件的软链接

求教这个命令什么意思,我是新...

sed -e "/grep/d" 是什么意思...

谁能够帮我解决LINUX 2.6 10...

给主人留下些什么吧!~~

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: