Overlapped I/O模型--事件通知【摘录自《Windows网络编程》】
2016-01-25 16:34
615 查看
原文: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异步操作。
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(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...
给主人留下些什么吧!~~
评论热议
重叠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异步操作。
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(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...
给主人留下些什么吧!~~
评论热议
相关文章推荐
- Overlapped I/O模型--事件通知【摘录自《Windows网络编程》】
- asynchttpclient unknownhostexception
- 如何检测网络端口是否被占用
- Android中webview加载网络资源
- 如何检测网络端口是否被占用
- 网络的7层协议
- 知名网络后端开源软件集合
- Android开发实现网络加载多张图片并可以滑动
- 使用WinINet发送HTTP请求并获取数据[原]
- 使用WinINet发送HTTP请求并获取数据[原]
- Delphi XE8 用HttpClient下载文件
- urllib2请求csdn时,显示: urllib2.HTTPError: HTTP Error 403: Forbidden
- fresco添加http header
- VC 网络编程 基础模型
- 使用mockwebserver对http与https进行网络测试
- 抓包工具 tcpdump , tshark
- https原理
- HttpURLConnection GET 返回没有具体信息时判断成功
- iOS_UI_网络数据请求
- 基础网络加载