//服务端源码
//用事件通知方式实现的重叠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