您的位置:首页 > 其它

重叠I/O之事件通知

2016-11-28 13:16 477 查看
使用重叠I/O之事件通知模型步骤如下:

一、打开服务器(包括初始化、创建socket、绑定、监听)

其中创建socket的时候可以使用WSASocket,也可以简单的使用socket,因为socket默认已经加上了WSA_FLAG_OVERLAPPED这个标志。

二、创建ThreadAccept线程

这个线程主要是用来不断的接受client的请求,并为每一个成功连接到server的client创建一个事件对象并将它与重叠结构绑定起来。然后以此为参数调用WSARecv函数,也就是投递一个事件,剩下的事情交给操作系统去做,当有对应的事件发生,系统就会以事件的形式通知应用程序。

伪代码如下:

while(1)
{
accept a new client;          //accept()
...
Create a new event object;    //WSACreateEvent()
...
bind with overlapped struct;  //WSAOVERLAPPED
...
post a WSARecv;               //WSARecv()
}


其中一些参数的初始化和错误处理省略了。

如图:



三、创建ThreadHandle线程

这个线程主要用来等待事件的发生以及处理接收到的数据。

其主要步骤就是等待某个socket上的事件发生,然后触发这个函数返回一个index值(注意,这里的index还不是对应事件数组的索引,还要减去WSA_WAIT_EVENT_0),之后通过这个index索引到对应的socket,以此为参数调用WSAGetOverlappedResult函数得到重叠操作的结果。在之后就是处理接收到的数据以及再一次投递一个WSARecv。

伪代码如下:

while(1)
{
wait for event happend; //WSAWaitForMultipleEvents()
...
reset the event object; //WSAResetEvent()
...
get the overlapped operation result;//WSAGetOverlappedResult()
...
handle the recv data;
...
post a WSASend or WSARecv;
}


其中一些参数的初始化和错误处理省略了。

如图:



示例代码:

main function

#include <WinSock2.h>
#include <process.h>
#pragma comment(lib,"ws2_32.lib")
#define MAXBUF 128                                              //缓冲区大小
SOCKET g_sServer = INVALID_SOCKET;                              //server socket
SOCKET g_sClient[WSA_MAXIMUM_WAIT_EVENTS] = { INVALID_SOCKET }; //client socket array
WSAEVENT g_event[WSA_MAXIMUM_WAIT_EVENTS];                      //event array
WSAOVERLAPPED g_wsaOverLapped_Recv[WSA_MAXIMUM_WAIT_EVENTS];    //接收重叠结构数组
WSAOVERLAPPED g_wsaOverLapped_Send[WSA_MAXIMUM_WAIT_EVENTS];    //发送重叠结构数组
WSABUF g_wsaBuf[WSA_MAXIMUM_WAIT_EVENTS] = { 0 };               //接收和发送缓冲区,因为是转发,所以接收和发送的数据是一样的
DWORD g_dwRecvBytes[WSA_MAXIMUM_WAIT_EVENTS] = {0};             //接收到的字节数
DWORD g_dwSendBytes[WSA_MAXIMUM_WAIT_EVENTS] = { 0 };           //接收到的字节数
int g_numClient = 0;                                            //当前连接的client数

int main()
{

//打开服务器
if (OpenTCPServer())
{
_beginthreadex(NULL, 0, ThreadAccept, NULL, 0, NULL);     //开启接受client请求线程
_beginthreadex(NULL, 0, ThreadHandleData, NULL, 0, NULL); //开启处理数据线程
}
Sleep(100000000);                                             //主函数睡眠
closesocket(g_sServer);
WSACleanup();
return 0;
}


OpenServer function

/*
@function OpenTCPServer             打开TCP服务器
@return  成功返回TRUE 失败返回FALSE
*/
BOOL OpenTCPServer()
{
WSADATA wsaData = { 0 };
SOCKADDR_IN ServerAddr = { 0 };
BOOL bRet = FALSE;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(18000);
ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
do
{
if (!WSAStartup(MAKEWORD(2, 2), &wsaData))
{
if (LOBYTE(wsaData.wVersion) == 2 || HIBYTE(wsaData.wVersion) == 2)
{
//在套接字上使用重叠I/O模型,必须使用WSA_FLAG_OVERLAPPED标志创建套接字
//g_sServer = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
g_sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (g_sServer != INVALID_SOCKET)
{
if (SOCKET_ERROR != bind(g_sServer, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))
{
if (SOCKET_ERROR != listen(g_sServer, SOMAXCONN))
{
bRet = TRUE;
break;
}
closesocket(g_sServer);
}
closesocket(g_sServer);
}
}
}
} while (FALSE);
return bRet;
}


ThreadAccept function

//接受client请求线程
unsigned int __stdcall ThreadAccept(void* lparam)
{
SOCKADDR_IN addrClient = { 0 };
int iLenAddr = sizeof(addrClient);
//初始化接收重叠结构
memset(&g_wsaOverLapped_Recv, 0, sizeof(g_wsaOverLapped_Recv));
//初始化发送重叠结构
memset(&g_wsaOverLapped_Send, 0, sizeof(g_wsaOverLapped_Send));
while (g_numClient < WSA_MAXIMUM_WAIT_EVENTS)  //当g_numClient等于最大连接数的时候,退出循环
{
//接受新的请求
g_sClient[g_numClient] = accept(g_sServer, (SOCKADDR*)&addrClient, &iLenAddr);
if(g_sClient[g_numClient] == INVALID_SOCKET)
continue;
//创建事件对象
g_event[g_numClient] = WSACreateEvent();
//为该结构的hEvent字段赋值为刚刚创建的事件对象
g_wsaOverLapped_Recv[g_numClient].hEvent = g_event[g_numClient];
//为该结构的hEvent字段赋值为刚刚创建的事件对象
g_wsaOverLapped_Send[g_numClient].hEvent = g_event[g_numClient];
//为每个client的buf分配空间
g_wsaBuf[g_numClient].buf = new char[MAXBUF];
g_wsaBuf[g_numClient].len = MAXBUF;
memset(g_wsaBuf[g_numClient].buf, 0, MAXBUF);
//这个标志一定要放进去,不然会出错(可能是如果没有传一个指针进去,函数内部引用了一个空指针而出错)
DWORD dwFlag = 0;
//投递一个recv事件,将该client的recv事件与接收重叠结构绑定起来,接下来的事就丢给重叠结构去完成,等待完成后的消息即可
if (SOCKET_ERROR == WSARecv(g_sClient[g_numClient], &g_wsaBuf[g_numClient],
1, &g_dwRecvBytes[g_numClient], &dwFlag, &g_wsaOverLapped_Recv[g_numClient], NULL))
{
//返回WSA_IO_PENDING是正常的,说明正在进行I/O操作
if (WSAGetLastError() != WSA_IO_PENDING)
return 0;
}
printf("accept connection from IP: %s,PORT: %d\n", inet_ntoa(addrClient.sin_addr), htons(addrClient.sin_port));
g_numClient++;
}
return 0;
}


ThreadHandle function

//处理数据线程
unsigned int __stdcall ThreadHandleData(void* lparam)
{
DWORD dwFlags = 0;   //这个标志一定要放进去,不然会出错
DWORD dwBytesRecv;   //接收的字节数
DWORD dwBytesSend;   //发送的字节数
while (1)
{
//无client的时候不进行任何处理
if (g_numClient < 1)
{
Sleep(100);
continue;
}
dwBytesRecv = 0;
dwBytesSend = 0;
//等待事件
DWORD dwIndex = WSAWaitForMultipleEvents(g_numClient, g_event, FALSE, 1000, FALSE);
if (dwIndex == WSA_WAIT_FAILED)
{
printf("WSAWaitForMultipleEvents failed with error code:%d\n", WSAGetLastError());
return 1;
}
else if (dwIndex == WSA_WAIT_TIMEOUT)
{
Sleep(100);
continue;
}
//
dwIndex -= WSA_WAIT_EVENT_0;
//重置事件
WSAResetEvent(g_event[dwIndex]);
//获取接收重叠结构操作后的结果
BOOL bRet = WSAGetOverlappedResult(g_sClient[dwIndex],&g_wsaOverLapped_Recv[dwIndex],
&dwBytesRecv, FALSE, &dwFlags);
if (dwBytesRecv == 0)   //如果传输的数据为0,则表示client已经断开连接
{
printf("%d disconnect\n", dwIndex);
continue;
}
else if (bRet == FALSE)    //有三种原因:第一,重叠操作还未完成。第二,重叠操作完成,但存在错误
{                          //第三,由于该函数的一个或者多个参数错误,而导致不能确定重叠操作完成的状态
int i = WSAGetLastError();
continue;
}
else //如果成功返回,则进行数据的处理,然后再投递一次recv事件
{
g_wsaBuf[dwIndex].len = strlen(g_wsaBuf[dwIndex].buf);
if (g_wsaBuf[dwIndex].len != 0)
//printf("Recv%d:%s\n", dwIndex,g_wsaBuf[dwIndex].buf);
//投递send事件
if (SOCKET_ERROR == WSASend(g_sClient[dwIndex], &g_wsaBuf[dwIndex], 1,
&g_dwSendBytes[dwIndex], dwFlags, &g_wsaOverLapped_Send[dwIndex], NULL))
{
if (WSAGetLastError() != WSA_IO_PENDING)
return 1;
}
//投递recv事件
g_wsaBuf[dwIndex].len = MAXBUF;
if (SOCKET_ERROR == WSARecv(g_sClient[dwIndex], &g_wsaBuf[dwIndex], 1,
&g_dwRecvBytes[dwIndex], &dwFlags, &g_wsaOverLapped_Recv[dwIndex], NULL))
{
if (WSAGetLastError() != WSA_IO_PENDING)
return 1;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: