#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>
using namespace std;
#pragma comment(lib, "Ws2_32.lib") // Socket编程需用的动态链接库
#pragma comment(lib, "Kernel32.lib") // IOCP需要用到的动态链接库
//#pragma comment(lib,"mswsock.lib")
#include <mswsock.h>//微软扩展库,后面获取AcceptEx指针用到
const int BufSize = 2 * 1024;
CRITICAL_SECTION cs;
//重叠I/O所用到的结构体
typedef struct Per_IO_Context
{
OVERLAPPED overlapped;//每一个重叠I/O都需要有一个
WSABUF dataBuf;//存储数据的缓冲区,用来给重叠操作传递参数
char buffer[BufSize];//对应的WSABUF里的缓冲区
int bufLength;//缓冲区长度
int operationType;//标志这个重叠I/O是做什么的,Accept Recv之类的
SOCKET m_sockAccept;//这个I/O所用的socket,每个连接都是一样的
}PER_IO_CONTEXT;
typedef struct Per_Sock_Context
{
SOCKET m_sock;//每一个客户端连接的socket
SOCKADDR_IN m_addr;//这个客户端对应的地址
}PER_SOCK_CONTEXT;
vector<struct Per_Sock_Context*>clientGroup;//客户端数组
SOCKET server_socket;
int main()
{// 函数声明
DWORD WINAPI ServerWorkThread(LPVOID lpa);
DWORD WINAPI ServerSendThread(LPVOID lpa);
DWORD WINAPI AcceptThread(LPVOID lpa);
HANDLE ThreadHandle;
InitializeCriticalSection(&cs);
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
HANDLE completPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);//创建io内核对象
if (completPort == NULL)
{
cerr << "创建IO内核对象失败!!错误代码: " << GetLastError() << endl;
system("pause");
return -1;
}
SYSTEM_INFO m_sysInfo;
GetSystemInfo(&m_sysInfo);//获取cpu的核心数量
for (DWORD i = 0; i < m_sysInfo.dwNumberOfProcessors * 2; i++)
{ //创建服务器工作线程,并将完成端口传递到该线程
ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completPort, 0, NULL);
if (ThreadHandle == NULL)
{
cerr << "创建线程句柄失败!!错误码: " << GetLastError() << endl;
system("pause");
return -1;
}
}
//建立流式套接字
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(6000);
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
PER_SOCK_CONTEXT* perDandleData;
perDandleData = (PER_SOCK_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_SOCK_CONTEXT));
perDandleData->m_sock = server_socket;
CreateIoCompletionPort((HANDLE)server_socket, completPort, (ULONG_PTR)perDandleData, 0);
//绑定socket
if (bind(server_socket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
cerr << "绑定地址失败!!\n错误码为: " << GetLastError() << endl;
system("pause");
return -1;
}
if (listen(server_socket, 10) == SOCKET_ERROR)
{
cerr << "监听失败!!\n错误码: " << GetLastError << endl;
system("pause");
return -1;
}
cout << "服务端已开启,正在等待客户端连接...\nIP :127.0.0.1\tPort :6000" << endl;
HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);
HANDLE acceptThread = CreateThread(NULL, 0, AcceptThread, completPort, 0, NULL);//连接线程中因为需要绑定完成端口,所以传递句柄
WaitForSingleObject(sendThread, INFINITE);
WaitForSingleObject(acceptThread, INFINITE);
WaitForSingleObject(ThreadHandle, INFINITE);
DeleteCriticalSection(&cs);
DeleteObject(sendThread);
DeleteObject(acceptThread);
DeleteObject(ThreadHandle);
system("pause");
return 0;
}
DWORD WINAPI AcceptThread(LPVOID lpa)
{
HANDLE completPort = (HANDLE)lpa;
while (true)
{
PER_SOCK_CONTEXT *perHandleData = NULL;
PER_IO_CONTEXT *perIOData = NULL;
SOCKADDR_IN remoteAddr;
SOCKET acceptSock;
int len = sizeof(remoteAddr);
/* perIOData = (PER_IO_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_IO_CONTEXT));
ZeroMemory(&(perIOData->overlapped), sizeof(OVERLAPPED));//置0
LPFN_ACCEPTEX m_lpfnAcceptEx; // AcceptEx函数指针
GUID GuidAcceptEx = WSAID_ACCEPTEX; // GUID,这个是识别AcceptEx函数必须的
DWORD dwBytes = 0;
if (0!=WSAIoctl(server_socket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx),//第一个参数只要是一个有效的socket就可以
&m_lpfnAcceptEx, sizeof(m_lpfnAcceptEx), &dwBytes, NULL, NULL))//获取AcceptEx指针
{
cerr << WSAGetLastError() << endl;
return -1;
}
BOOL rc = m_lpfnAcceptEx(server_socket,
perIOData->m_sockAccept,
perIOData->buffer,
perIOData->bufLength - ((sizeof(SOCKADDR_IN) + 16) * 2),
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
&dwBytes,
&(perIOData->overlapped)
);
if(rc == FALSE)
break;
cout << "连入一个客户端!" << endl;
LPFN_GETACCEPTEXSOCKADDRS m_lpfnGetAcceptExSockAddrs; //同上的AcceptEx
GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
WSAIoctl(perHandleData->m_sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs,
sizeof(GuidGetAcceptExSockAddrs), &m_lpfnGetAcceptExSockAddrs,sizeof(m_lpfnGetAcceptExSockAddrs), &dwBytes,NULL,NULL);
SOCKADDR_IN* ClientAddr = NULL;
SOCKADDR_IN* LocalAddr = NULL;
int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);
m_lpfnGetAcceptExSockAddrs(perIOData->dataBuf.buf, perIOData->dataBuf.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&LocalAddr, &localLen, (LPSOCKADDR*)&ClientAddr, &remoteLen);
*/
acceptSock = accept(server_socket, (struct sockaddr*)&remoteAddr, &len);
if (acceptSock == SOCKET_ERROR)
{
cerr << "Accept Socket ERROR!!\nError Code: " << GetLastError() << endl;
system("pause");
return -1;
}
// 创建用来和套接字关联的单句柄数据信息结构
perHandleData = (PER_SOCK_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_SOCK_CONTEXT)); // 在堆中申请指定大小的内存
perHandleData->m_sock =acceptSock;//将新连接的客户端的信息保存下来
memcpy(&perHandleData->m_addr, &remoteAddr, len);
clientGroup.push_back(perHandleData);
//将套接字与完成端口关联
CreateIoCompletionPort((HANDLE)(perHandleData->m_sock), completPort, (DWORD)perHandleData, 0);
// 开始在接受套接字上处理I/O使用重叠I/O机制
// 在新建的套接字上投递一个或多个异步
// WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务
// 单I/O操作数据(I/O重叠)
PER_IO_CONTEXT * PerIOData = NULL;
PerIOData = (PER_IO_CONTEXT*)GlobalAlloc(GPTR, sizeof(PER_IO_CONTEXT));
ZeroMemory(&(PerIOData->overlapped), sizeof(OVERLAPPED));//置0
PerIOData->dataBuf.len = 1024;
PerIOData->dataBuf.buf = PerIOData->buffer;
PerIOData->operationType = 0; //read
DWORD RecvBytes;
DWORD Flags = 0;
//投递第一个I/O请求
WSARecv(perHandleData->m_sock, &(PerIOData->dataBuf), 1, &RecvBytes, &Flags, &(PerIOData->overlapped), NULL);
}
return 0;
}
DWORD WINAPI ServerWorkThread(LPVOID lpa)
{
HANDLE CompletionPort = (HANDLE)lpa;//传递的是上面创建的I/O内核对象
LPOVERLAPPED lpoverlapped;
PER_SOCK_CONTEXT* perHandleData = NULL;
PER_IO_CONTEXT * perIOData = NULL;
int result;
DWORD BytesTransferred;
DWORD Flags = 0;
DWORD receiveBytes;
while (true)
{
/* GetQueuedCompletionStatus 参数含义:
CompletionPort:指定的IOCP,该值由CreateIoCompletionPort函数创建。
lpnumberofbytes:一次完成后的I/O操作所传送数据的字节数。
lpcompletionkey:当文件I/O操作完成后,用于存放与之关联的CK。
lpoverlapped:为调用IOCP机制所引用的OVERLAPPED结构。
dwmilliseconds:用于指定调用者等待CP的时间。
*/
result = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&perHandleData,
(LPOVERLAPPED*)&lpoverlapped, INFINITE);
if (result == 0)
{
cerr << "GetQueuedCompletionStatus Error: " << GetLastError() << endl;
continue;
}
//根据结构体中的某成员的地址来推算出该结构体整体的地址!
perIOData = (PER_IO_CONTEXT*)CONTAINING_RECORD(lpoverlapped, PER_IO_CONTEXT, overlapped);
if (BytesTransferred == 0)//如果有错误发生
{
closesocket(perHandleData->m_sock);//关闭socket
GlobalFree(perHandleData);//释放内存
GlobalFree(perIOData);
continue;
}
// 开始数据处理,接收来自客户端的数据
EnterCriticalSection(&cs);
cout << "Receive: " << perIOData->dataBuf.buf << endl;
LeaveCriticalSection(&cs);
// 为下一个重叠调用建立单I/O操作数据
ZeroMemory(&perIOData->overlapped, sizeof(OVERLAPPED));//清0
perIOData->dataBuf.len = 1024;
ZeroMemory(perIOData->dataBuf.buf, 1024);
perIOData->operationType = 0; // read
WSARecv(perHandleData->m_sock, &(perIOData->dataBuf), 1, &receiveBytes, &Flags, &(perIOData->overlapped), NULL);
}
return 0;
}
DWORD WINAPI ServerSendThread(LPVOID lpa)
{
while (TRUE)
{
char talk[200];
cin >> talk;
int i = 0;
while (talk[i] != '\0')
i++;
talk[i] = '\n';
talk[++i] = '\0';
cout << "Send: " << talk << endl;
EnterCriticalSection(&cs);//互斥访问连接的socket数组
for (int i = 0; i < clientGroup.size(); ++i) {
send(clientGroup[i]->m_sock, talk, 200, 0); // 发送信息
}
LeaveCriticalSection(&cs);
}
return 0;
}
iocp模型
最新推荐文章于 2022-08-26 21:30:00 发布