内容纲要
#include <winsock2.h> #include <windows.h> #include <stdio.h> #define PORT 5150 #define MSGSIZE 1024 #pragma comment(lib, "ws2_32.lib") typedef enum { RECV_POSTED }OPERATION_TYPE; //枚举,表示状态 typedef struct { WSAOVERLAPPED overlap; WSABUF Buffer; char szMessage[MSGSIZE]; DWORD NumberOfBytesRecvd; DWORD Flags; OPERATION_TYPE OperationType; }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA; //定义一个结构体保存IO数据 DWORD WINAPI WorkerThread(LPVOID); int main() { WSADATA wsaData; SOCKET sListen, sClient; SOCKADDR_IN local, client; DWORD i, dwThreadId; int iaddrSize = sizeof(SOCKADDR_IN); HANDLE CompletionPort = INVALID_HANDLE_VALUE; SYSTEM_INFO systeminfo; LPPER_IO_OPERATION_DATA lpPerIOData = NULL; //初始化Socket WSAStartup(0x0202, &wsaData); // 初始化完成端口 CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // 有几个CPU就创建几个工作者线程 GetSystemInfo(&systeminfo); for(i = 0; i < systeminfo.dwNumberOfProcessors; i++) { CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId); } // 创建套接字 sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 绑定套接字 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); //主进程的这个循环中循环等待客户端连接,若有连接,则将该客户套接字于完成端口绑定到一起 //然后开始异步等待接收客户传来的数据。 while (TRUE) { // 如果接到客户请求连接,则继续,否则等待。 sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize); //client中保存用户信息。 printf("Accepted client:%s:%dn", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); //将这个最新到来的客户套接字和完成端口绑定到一起。 //第三个参数表示传递的参数,这里就传递的客户套接字地址。 //最后一个参数为0 表示有和CPU一样的进程数。即1个CPU一个线程 CreateIoCompletionPort((HANDLE)sClient, CompletionPort, ( ULONG_PTR)sClient, 0); // 初始化结构体 使用堆内存分配 lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA)); lpPerIOData->Buffer.len = MSGSIZE; // len=1024 lpPerIOData->Buffer.buf = lpPerIOData->szMessage; lpPerIOData->OperationType = RECV_POSTED; //操作类型 WSARecv(sClient, //异步接收消息,立刻返回。 &lpPerIOData->Buffer, //获得接收的数据 1, //The number of WSABUF structures in the lpBuffers array. &lpPerIOData->NumberOfBytesRecvd, //接收到的字节数,如果错误返回0 &lpPerIOData->Flags, //参数,先不管 &lpPerIOData->overlap, //输入这个结构体咯。 NULL); } //向每个工作者线程都发送―个特殊的完成数据包。该函数会指示每个线程都“立即结束并退出”. PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL); CloseHandle(CompletionPort); closesocket(sListen); WSACleanup(); return 0; } //工作者线程有一个参数,是指向完成端口的句柄 DWORD WINAPI WorkerThread(LPVOID CompletionPortID) { HANDLE CompletionPort = (HANDLE)CompletionPortID; DWORD dwBytesTransferred; SOCKET sClient = INVALID_SOCKET; LPPER_IO_OPERATION_DATA lpPerIOData = NULL; while (TRUE) { BOOL bRet = GetQueuedCompletionStatus( //遇到可以接收数据则返回,否则等待 CompletionPort, &dwBytesTransferred, //返回的字数 (PULONG_PTR) &sClient, //是响应的哪个客户套接字? (LPOVERLAPPED *)&lpPerIOData, //得到该套接字保存的IO信息 INFINITE); //无限等待咯。不超时的那种。 if (!bRet) { DWORD dwErrCode = GetLastError(); continue; } if (dwBytesTransferred == 0xFFFFFFFF) { return 0; } if(lpPerIOData->OperationType == RECV_POSTED) //如果收到数据 { if (dwBytesTransferred == 0) { //失去客户端连接 closesocket(sClient); HeapFree(GetProcessHeap(), 0, lpPerIOData); //释放结构体 } else { lpPerIOData->szMessage[dwBytesTransferred] = '\0'; send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0); //将接收到的消息返回 // Launch another asynchronous operation for sClient memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA)); lpPerIOData->Buffer.len = MSGSIZE; lpPerIOData->Buffer.buf = lpPerIOData->szMessage; lpPerIOData->OperationType = RECV_POSTED; WSARecv(sClient, //循环接收 &lpPerIOData->Buffer, 1, &lpPerIOData->NumberOfBytesRecvd, &lpPerIOData->Flags, &lpPerIOData->overlap, NULL); } } } return 0; }