阅读背景:

用事件通知方式实现的重叠I/O模型_飞叶如刀

来源:互联网 
//服务端源码
//用事件通知方式实现的重叠I/O模型
#include <winsock2.h>
#include <stdio.h>
#define PORT 5566
#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;
	WORD wVersionRequested;
	int err;
	
	wVersionRequested = MAKEWORD( 1, 1);
	
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {                              
		return 0;
	}                                      
	
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ){
		WSACleanup( );
		return 0; 
	}
	/*WSAStartup(0x0202, &wsaData);*/
 // 初始化windows socket库
 SOCKET sListen, sClient;
 SOCKADDR_IN local, client;
 DWORD dwThreadId;
 int iaddrSize = sizeof(SOCKADDR_IN);
 // 创建监听socket
 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //默认设置 WSA_FLAG_OVERLAPPED
 // 绑定
 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(sListen, 3);
 // 创建工作者线程
 CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
 while (TRUE)
 {
   // 接受连接
   sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
   printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
   g_CliSocketArr[g_iTotalConn] = sClient;

   // 分配一个单io操作数据结构
   g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc(
   GetProcessHeap(),
   HEAP_ZERO_MEMORY,
   sizeof(PER_IO_OPERATION_DATA));
   //This function allocates a block of memory from a heap. The allocated memory is not movable. 
   g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;
   g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->szMessage;
   g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();
   g_pPerIODataArr[g_iTotalConn]->Flags=0;
   // 开始一个异步操作
   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);
   //WSARecv的参数中都有一个 Overlapped 参数,我们可以假设是把我们的WSARecv这样的操作"绑定"到这个重叠结构上,
   //提交一个请求,而不是将操作立即完成,其他的事情就交给重叠结构去做,而其中重叠结构又要与Windows的事件对象"绑定"在一起,
   // 这样我们调用完 WSARecv 以后就可以"坐享其成",等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,
  //然后我们就可以来根据重叠操作的结果取得我们想要的数据了。
       /*int WSARecv(
	   SOCKET s, // 当然是投递这个操作的套接字
	   LPWSABUF lpBuffers, // 接收缓冲区,与Recv函数不同
	   // 这里需要一个由WSABUF结构构成的数组
	   DWORD dwBufferCount, // 数组中WSABUF结构的数量
	   LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
	   LPDWORD lpFlags, // 一个指向标志位的指针
	   LPWSAOVERLAPPED lpOverlapped, // “绑定”的重叠结构
	   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为 NULL
	);无错返回0,否则返回-1(SOCKET_ERROR )*/
   g_iTotalConn++;
}

 closesocket(sListen);
 WSACleanup();
 return 0;
}
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
 int ret, index;
 DWORD cbTransferred;
 while (TRUE)
 {
	 //判断出一个重叠 I/O 调用是否完成
   ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
   if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
   {
     continue;
   }
       /*DWORD WSAWaitForMultipleEvents(
	   DWORD cEvents, // 等候事件的总数量
	   const WSAEVENT* lphEvents, // 事件数组的指针
	   BOOL fWaitAll, // 这个要多说两句:
	   // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
	   // FALSE则任何一个事件被传信函数都要返回
	   // 我们这里肯定是要设置为FALSE的
	   DWORD dwTimeout, // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
	   // 如果设置为0,函数会立即返回
	   // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回,
	   //则WSAWaitForMultipleEvents 永远等待,不会出现超时现象。
	   BOOL fAlertable
	   //该值指定线程是否为alertable等待状态,此时系统能执行一些I/O 完成例程。如果值为真,
	   当系统执行I/O //完成例程时线程被处于altertable 等待状态且WSAWaitForMultipleEvents 
	   返回。在这种情况下,返回
	   //WSA_WAIT_IO_COMPLETION ,并且等待的event 不会触发信号状态。程序必须重新调用
	   //WSAWaitForMultipleEvents 函数。如果为false,线程不处于altertable 等待状态,
	   //并且I/O 完成例程不会执行。 
	   );
        返回值:
	   WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
	   WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
	   如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个
	   索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
       注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象
	   定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,
	   如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一
	   个线程池
		If the WSAWaitForMultipleEvents function succeeds, the return value indicates 
		the event object that caused the function to return.*/
 index = ret - WSA_WAIT_EVENT_0;
 WSAResetEvent(g_CliEventArr[index]);//手动设置为未传信
 //https://baike.baidu.com/view/567662.htm
 BOOL bRet=WSAGetOverlappedResult(g_CliSocketArr[index],
 &g_pPerIODataArr[index]->overlap,
 &cbTransferred,
 TRUE,
 &g_pPerIODataArr[g_iTotalConn]->Flags);//判断该重叠调用到底是成功,还是失败
/*if (!bRet)
 {	
	int error=WSAGetLastError();

	continue;
 }
 else
 {*/
	 if(cbTransferred==0)
	 {
		 Cleanup(index);
	 }
	 else
	 {
		 // g_pPerIODataArr[index]->szMessage保存了接受到的数据
		 g_pPerIODataArr[index]->szMessage[cbTransferred] = '
//服务端源码
//用事件通知方式实现的重叠I/O模型
#include <winsock2.h>
#include <stdio.h>
#define PORT 5566
#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;
	WORD wVersionRequested;
	int err;
	
	wVersionRequested = MAKEWORD( 1, 1);
	
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {                              
		return 0;
	}                                      
	
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ){
		WSACleanup( );
		return 0; 
	}
	/*WSAStartup(0x0202, &wsaData);*/
 // 初始化windows socket库
 SOCKET sListen, sClient;
 SOCKADDR_IN local, client;
 DWORD dwThreadId;
 int iaddrSize = sizeof(SOCKADDR_IN);
 // 创建监听socket
 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //默认设置 WSA_FLAG_OVERLAPPED
 // 绑定
 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(sListen, 3);
 // 创建工作者线程
 CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
 while (TRUE)
 {
   // 接受连接
   sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
   printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
   g_CliSocketArr[g_iTotalConn] = sClient;

   // 分配一个单io操作数据结构
   g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc(
   GetProcessHeap(),
   HEAP_ZERO_MEMORY,
   sizeof(PER_IO_OPERATION_DATA));
   //This function allocates a block of memory from a heap. The allocated memory is not movable. 
   g_pPerIODataArr[g_iTotalConn]->Buffer.len = MSGSIZE;
   g_pPerIODataArr[g_iTotalConn]->Buffer.buf = g_pPerIODataArr[g_iTotalConn]->szMessage;
   g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();
   g_pPerIODataArr[g_iTotalConn]->Flags=0;
   // 开始一个异步操作
   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);
   //WSARecv的参数中都有一个 Overlapped 参数,我们可以假设是把我们的WSARecv这样的操作"绑定"到这个重叠结构上,
   //提交一个请求,而不是将操作立即完成,其他的事情就交给重叠结构去做,而其中重叠结构又要与Windows的事件对象"绑定"在一起,
   // 这样我们调用完 WSARecv 以后就可以"坐享其成",等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,
  //然后我们就可以来根据重叠操作的结果取得我们想要的数据了。
       /*int WSARecv(
	   SOCKET s, // 当然是投递这个操作的套接字
	   LPWSABUF lpBuffers, // 接收缓冲区,与Recv函数不同
	   // 这里需要一个由WSABUF结构构成的数组
	   DWORD dwBufferCount, // 数组中WSABUF结构的数量
	   LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
	   LPDWORD lpFlags, // 一个指向标志位的指针
	   LPWSAOVERLAPPED lpOverlapped, // “绑定”的重叠结构
	   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为 NULL
	);无错返回0,否则返回-1(SOCKET_ERROR )*/
   g_iTotalConn++;
}

 closesocket(sListen);
 WSACleanup();
 return 0;
}
DWORD WINAPI WorkerThread(LPVOID lpParam)
{
 int ret, index;
 DWORD cbTransferred;
 while (TRUE)
 {
	 //判断出一个重叠 I/O 调用是否完成
   ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
   if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
   {
     continue;
   }
       /*DWORD WSAWaitForMultipleEvents(
	   DWORD cEvents, // 等候事件的总数量
	   const WSAEVENT* lphEvents, // 事件数组的指针
	   BOOL fWaitAll, // 这个要多说两句:
	   // 如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回
	   // FALSE则任何一个事件被传信函数都要返回
	   // 我们这里肯定是要设置为FALSE的
	   DWORD dwTimeout, // 超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT
	   // 如果设置为0,函数会立即返回
	   // 如果设置为 WSA_INFINITE只有在某一个事件被传信后才会返回,
	   //则WSAWaitForMultipleEvents 永远等待,不会出现超时现象。
	   BOOL fAlertable
	   //该值指定线程是否为alertable等待状态,此时系统能执行一些I/O 完成例程。如果值为真,
	   当系统执行I/O //完成例程时线程被处于altertable 等待状态且WSAWaitForMultipleEvents 
	   返回。在这种情况下,返回
	   //WSA_WAIT_IO_COMPLETION ,并且等待的event 不会触发信号状态。程序必须重新调用
	   //WSAWaitForMultipleEvents 函数。如果为false,线程不处于altertable 等待状态,
	   //并且I/O 完成例程不会执行。 
	   );
        返回值:
	   WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
	   WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
	   如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个
	   索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
       注意:WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象
	   定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,
	   如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一
	   个线程池
		If the WSAWaitForMultipleEvents function succeeds, the return value indicates 
		the event object that caused the function to return.*/
 index = ret - WSA_WAIT_EVENT_0;
 WSAResetEvent(g_CliEventArr[index]);//手动设置为未传信
 //https://baike.baidu.com/view/567662.htm
 BOOL bRet=WSAGetOverlappedResult(g_CliSocketArr[index],
 &g_pPerIODataArr[index]->overlap,
 &cbTransferred,
 TRUE,
 &g_pPerIODataArr[g_iTotalConn]->Flags);//判断该重叠调用到底是成功,还是失败
/*if (!bRet)
 {	
	int error=WSAGetLastError();

	continue;
 }
 else
 {*/
	 if(cbTransferred==0)
	 {
		 Cleanup(index);
	 }
	 else
	 {
		 // g_pPerIODataArr[index]->szMessage保存了接受到的数据
		 g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0';
		 printf("%s\n",g_pPerIODataArr[index]->szMessage);
		 // 进行另一个异步操作
		 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;
}


'; printf("%s\n",g_pPerIODataArr[index]->szMessage); // 进行另一个异步操作 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; } //服务端源码 //用事件通知方式实现的重叠I/O模型 #include <winsoc



你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: