//把数据从网卡复制到程序的buffer,但是也要时间。。重叠io就是解决这个问题
#include <WinSock2.h>
#include <stdio.h>
#define PORT 6000
#define MSGSIZE 1024
#pragma comment (lib,"Ws2_32.lib")
BOOL WinSockInit()
{
WSADATA data = { 0 };
if (WSAStartup(MAKEWORD(2, 2), &data))
return FALSE;
if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion != 2)){
WSACleanup();
return FALSE;
}
return TRUE;
}
//单io操作。。buf。。
typedef struct
{
WSAOVERLAPPED overlap; //第一个成员。重叠io,必须用OVERLAPPED
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(LPWORD);
void Cleanup(int);
int main()
{
SOCKET sListen, sClient;
SOCKADDR_IN local, client;
DWORD dwThreadId;
int iaddrSize = sizeof(SOCKADDR_IN);
//初始化windowa socket库
WinSockInit();
//创建监听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 = htonl(PORT);
bind(sListen, (struct sockaddr*)&local, sizeof(SOCKADDR_IN));
//监听
listen(sListen, 3);
//创建工作者线程
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)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));
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 = 1;
//开始一个异步操作-不会阻塞程序。。告诉小秘按结构体进行数据操作
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这样的操作绑定到这个重叠结构上
//提交一个请求,而不是将操作立即完成,其他事情交给重叠结构去做,而其中重叠结构要与事件对象绑定在一起
//这样我们调用完WSARecv以后就可以“坐享其成”,等重叠操作完成后,自然会有与之对应的的事件通知我们操作完成
//然后我们就可以根据重叠操作的结果取得我们想要的数据了
g_iTotalConn++;
}
closesocket(sListen);
WSACleanup();
return 0;
}
DWORD WINAPI WorkerThread(LPWORD lpParam)
{
int ret, index;
DWORD cbTransferred;
while (TRUE)
{
//判断出一个重叠IO调用是否完成
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)
Cleanup(index); //客户端连接关闭
else
{
//g_pPerIODataArr[index]->szMessage保存接收到的数据
g_pPerIODataArr[index]->szMessage[cbTransferred] = '\0';
send(g_CliSocketArr[index], g_pPerIODataArr[index]->szMessage,
cbTransferred, 0);
//进行另一个异步操作
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;
}
【Windows网络编程】重叠IO网络模型
最新推荐文章于 2024-07-25 17:09:16 发布