// Module Name: iocmplt.cpp // // Description: // // This sample illustrates how to develop a simple echo server Winsock // application using the completeion port I/O model. This // sample is implemented as a console-style application and simply prints // messages when connections are established and removed from the server. // The application listens for TCP connections on port 5150 and accepts them // as they arrive. When this application receives data from a client, it // simply echos (this is why we call it an echo server) the data back in // it's original form until the client closes the connection. // // Compile: // // cl -o iocmplt iocmplt.cpp ws2_32.lib // // Command Line Options: // // iocmplt.exe // // Note: There are no command line options for this sample. #pragma comment(lib, "ws2_32") #include <winsock2.h> #include <windows.h> #include <stdio.h> #define PORT 5150 #define DATA_BUFSIZE 8192 typedef struct { OVERLAPPED Overlapped; WSABUF DataBuf; CHAR Buffer[DATA_BUFSIZE]; DWORD BytesSEND; DWORD BytesRECV; } PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA; typedef struct { SOCKET Socket; } PER_HANDLE_DATA, * LPPER_HANDLE_DATA; DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID); void main(void) { SOCKADDR_IN InternetAddr; SOCKET Listen; SOCKET Accept; HANDLE CompletionPort; SYSTEM_INFO SystemInfo; LPPER_HANDLE_DATA PerHandleData; LPPER_IO_OPERATION_DATA PerIoData; int i; DWORD RecvBytes; DWORD Flags; DWORD ThreadID; WSADATA wsaData; DWORD Ret; if ((Ret = WSAStartup(0x0202, &wsaData)) !=0) { printf("WSAStartup failed with error %d\n", Ret); return; } // Setup an I/O completion port. if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL) { printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError()); return; } // Determine how many processors are on the system. GetSystemInfo(&SystemInfo); // Create worker threads based on the number of processors available on the // system. Create two worker threads for each processor. for(i =0; i < SystemInfo.dwNumberOfProcessors *2; i++) { HANDLE ThreadHandle; // Create a server worker thread and pass the completion port to the thread. if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort, 0, &ThreadID)) == NULL) { printf("CreateThread() failed with error %d\n", GetLastError()); return; } // Close the thread handle CloseHandle(ThreadHandle); } // Create a listening socket if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { printf("WSASocket() failed with error %d\n", WSAGetLastError()); return; } InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT); if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) { printf("bind() failed with error %d\n", WSAGetLastError()); return; } // Prepare socket for listening if (listen(Listen, 5) == SOCKET_ERROR) { printf("listen() failed with error %d\n", WSAGetLastError()); return; } // Accept connections and assign to the completion port. while(TRUE) { if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR) { printf("WSAAccept() failed with error %d\n", WSAGetLastError()); return; } // Create a socket information structure to associate with the socket if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL) { printf("GlobalAlloc() failed with error %d\n", GetLastError()); return; } // Associate the accepted socket with the original completion port. printf("Socket number %d connected\n", Accept); PerHandleData->Socket = Accept; if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData, 0) == NULL) { printf("CreateIoCompletionPort failed with error %d\n", GetLastError()); return; } // Create per I/O socket information structure to associate with the // WSARecv call below. if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL) { printf("GlobalAlloc() failed with error %d\n", GetLastError()); return; } ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->BytesSEND =0; PerIoData->BytesRECV =0; PerIoData->DataBuf.len = DATA_BUFSIZE; PerIoData->DataBuf.buf = PerIoData->Buffer; Flags =0; if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags, &(PerIoData->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); return; } } } } DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID) { HANDLE CompletionPort = (HANDLE) CompletionPortID; DWORD BytesTransferred; LPOVERLAPPED Overlapped; LPPER_HANDLE_DATA PerHandleData; LPPER_IO_OPERATION_DATA PerIoData; DWORD SendBytes, RecvBytes; DWORD Flags; while(TRUE) { if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) ==0) { printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError()); return0; } // First check to see if an error has occured on the socket and if so // then close the socket and cleanup the SOCKET_INFORMATION structure // associated with the socket. if (BytesTransferred ==0) { printf("Closing socket %d\n", PerHandleData->Socket); if (closesocket(PerHandleData->Socket) == SOCKET_ERROR) { printf("closesocket() failed with error %d\n", WSAGetLastError()); return0; } GlobalFree(PerHandleData); GlobalFree(PerIoData); continue; } // Check to see if the BytesRECV field equals zero. If this is so, then // this means a WSARecv call just completed so update the BytesRECV field // with the BytesTransferred value from the completed WSARecv() call. if (PerIoData->BytesRECV ==0) { PerIoData->BytesRECV = BytesTransferred; PerIoData->BytesSEND =0; } else { PerIoData->BytesSEND += BytesTransferred; } if (PerIoData->BytesRECV > PerIoData->BytesSEND) { // Post another WSASend() request. // Since WSASend() is not gauranteed to send all of the bytes requested, // continue posting WSASend() calls until all received bytes are sent. ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND; PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND; if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0, &(PerIoData->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSASend() failed with error %d\n", WSAGetLastError()); return0; } } } else { PerIoData->BytesRECV =0; // Now that there are no more bytes to send post another WSARecv() request. Flags =0; ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED)); PerIoData->DataBuf.len = DATA_BUFSIZE; PerIoData->DataBuf.buf = PerIoData->Buffer; if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags, &(PerIoData->Overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != ERROR_IO_PENDING) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); return0; } } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?