### Windows下C++使用IOCP实现UDP服务端
以下是一个简单的使用IOCP实现UDP服务端的示例代码:
```cpp
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 1024
// 定义重叠结构
typedef struct _PER_IO_CONTEXT {
OVERLAPPED overlapped;
WSABUF wsaBuf;
char buffer[BUFFER_SIZE];
sockaddr_in clientAddr;
int operationType;
} PER_IO_CONTEXT, *LPPER_IO_CONTEXT;
// 定义服务端上下文
typedef struct _PER_SOCKET_CONTEXT {
SOCKET socket;
} PER_SOCKET_CONTEXT, *LPPER_SOCKET_CONTEXT;
// 完成端口句柄
HANDLE hCompletionPort;
// 工作线程函数
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD dwBytesTransferred;
LPPER_SOCKET_CONTEXT pSocketContext;
LPPER_IO_CONTEXT pIoContext;
while (true) {
if (GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (PULONG_PTR)&pSocketContext, (LPOVERLAPPED*)&pIoContext, INFINITE) == 0) {
// 处理错误
std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl;
continue;
}
// 处理完成的IO操作
if (dwBytesTransferred > 0) {
// 处理接收到的数据
std::cout << "Received from " << inet_ntoa(pIoContext->clientAddr.sin_addr) << ":" << ntohs(pIoContext->clientAddr.sin_port)
<< ": " << std::string(pIoContext->buffer, dwBytesTransferred) << std::endl;
// 可以在这里添加发送响应的逻辑
}
// 发起新的接收操作
DWORD flags = 0;
int addrLen = sizeof(pIoContext->clientAddr);
if (WSARecvFrom(pSocketContext->socket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->clientAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl;
delete pIoContext;
}
}
}
return 0;
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl;
return 1;
}
// 创建完成端口
hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hCompletionPort == NULL) {
std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl;
WSACleanup();
return 1;
}
// 创建工作线程
const int threadCount = 2;
std::vector<HANDLE> threads(threadCount);
for (int i = 0; i < threadCount; ++i) {
threads[i] = CreateThread(NULL, 0, WorkerThread, (LPVOID)hCompletionPort, 0, NULL);
if (threads[i] == NULL) {
std::cerr << "CreateThread failed: " << GetLastError() << std::endl;
CloseHandle(hCompletionPort);
WSACleanup();
return 1;
}
}
// 创建UDP套接字
SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
CloseHandle(hCompletionPort);
for (HANDLE h : threads) {
CloseHandle(h);
}
WSACleanup();
return 1;
}
// 绑定端口
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(12345);
serverAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "bind failed: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
CloseHandle(hCompletionPort);
for (HANDLE h : threads) {
CloseHandle(h);
}
WSACleanup();
return 1;
}
// 关联套接字到完成端口
LPPER_SOCKET_CONTEXT pSocketContext = new PER_SOCKET_CONTEXT;
pSocketContext->socket = serverSocket;
if (CreateIoCompletionPort((HANDLE)serverSocket, hCompletionPort, (ULONG_PTR)pSocketContext, 0) == NULL) {
std::cerr << "CreateIoCompletionPort for socket failed: " << GetLastError() << std::endl;
closesocket(serverSocket);
delete pSocketContext;
CloseHandle(hCompletionPort);
for (HANDLE h : threads) {
CloseHandle(h);
}
WSACleanup();
return 1;
}
// 发起第一个接收操作
LPPER_IO_CONTEXT pIoContext = new PER_IO_CONTEXT;
ZeroMemory(&(pIoContext->overlapped), sizeof(OVERLAPPED));
pIoContext->wsaBuf.len = BUFFER_SIZE;
pIoContext->wsaBuf.buf = pIoContext->buffer;
pIoContext->operationType = 0;
DWORD flags = 0;
int addrLen = sizeof(pIoContext->clientAddr);
if (WSARecvFrom(serverSocket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->clientAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
delete pIoContext;
delete pSocketContext;
CloseHandle(hCompletionPort);
for (HANDLE h : threads) {
CloseHandle(h);
}
WSACleanup();
return 1;
}
}
// 等待用户输入退出
std::cout << "Press Enter to exit..." << std::endl;
std::cin.get();
// 清理资源
closesocket(serverSocket);
CloseHandle(hCompletionPort);
for (HANDLE h : threads) {
CloseHandle(h);
}
WSACleanup();
return 0;
}
```
### Windows下C++使用IOCP实现UDP客户端
以下是一个简单的使用IOCP实现UDP客户端的示例代码:
```cpp
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 1024
// 定义重叠结构
typedef struct _PER_IO_CONTEXT {
OVERLAPPED overlapped;
WSABUF wsaBuf;
char buffer[BUFFER_SIZE];
sockaddr_in serverAddr;
int operationType;
} PER_IO_CONTEXT, *LPPER_IO_CONTEXT;
// 定义客户端上下文
typedef struct _PER_SOCKET_CONTEXT {
SOCKET socket;
} PER_SOCKET_CONTEXT, *LPPER_SOCKET_CONTEXT;
// 完成端口句柄
HANDLE hCompletionPort;
// 工作线程函数
DWORD WINAPI WorkerThread(LPVOID lpParam) {
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD dwBytesTransferred;
LPPER_SOCKET_CONTEXT pSocketContext;
LPPER_IO_CONTEXT pIoContext;
while (true) {
if (GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (PULONG_PTR)&pSocketContext, (LPOVERLAPPED*)&pIoContext, INFINITE) == 0) {
// 处理错误
std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl;
continue;
}
// 处理完成的IO操作
if (dwBytesTransferred > 0) {
// 处理接收到的数据
std::cout << "Received from server: " << std::string(pIoContext->buffer, dwBytesTransferred) << std::endl;
}
// 可以在这里发起新的接收操作
}
return 0;
}
int main() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl;
return 1;
}
// 创建完成端口
hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (hCompletionPort == NULL) {
std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl;
WSACleanup();
return 1;
}
// 创建工作线程
HANDLE hThread = CreateThread(NULL, 0, WorkerThread, (LPVOID)hCompletionPort, 0, NULL);
if (hThread == NULL) {
std::cerr << "CreateThread failed: " << GetLastError() << std::endl;
CloseHandle(hCompletionPort);
WSACleanup();
return 1;
}
// 创建UDP套接字
SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
CloseHandle(hCompletionPort);
CloseHandle(hThread);
WSACleanup();
return 1;
}
// 初始化服务器地址
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(12345);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 关联套接字到完成端口
LPPER_SOCKET_CONTEXT pSocketContext = new PER_SOCKET_CONTEXT;
pSocketContext->socket = clientSocket;
if (CreateIoCompletionPort((HANDLE)clientSocket, hCompletionPort, (ULONG_PTR)pSocketContext, 0) == NULL) {
std::cerr << "CreateIoCompletionPort for socket failed: " << GetLastError() << std::endl;
closesocket(clientSocket);
delete pSocketContext;
CloseHandle(hCompletionPort);
CloseHandle(hThread);
WSACleanup();
return 1;
}
// 发起第一个接收操作
LPPER_IO_CONTEXT pIoContext = new PER_IO_CONTEXT;
ZeroMemory(&(pIoContext->overlapped), sizeof(OVERLAPPED));
pIoContext->wsaBuf.len = BUFFER_SIZE;
pIoContext->wsaBuf.buf = pIoContext->buffer;
pIoContext->serverAddr = serverAddr;
pIoContext->operationType = 0;
DWORD flags = 0;
int addrLen = sizeof(serverAddr);
if (WSARecvFrom(clientSocket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->serverAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl;
closesocket(clientSocket);
delete pIoContext;
delete pSocketContext;
CloseHandle(hCompletionPort);
CloseHandle(hThread);
WSACleanup();
return 1;
}
}
// 等待用户输入并发送数据
std::string input;
while (std::getline(std::cin, input)) {
LPPER_IO_CONTEXT pSendIoContext = new PER_IO_CONTEXT;
ZeroMemory(&(pSendIoContext->overlapped), sizeof(OVERLAPPED));
pSendIoContext->wsaBuf.len = input.length();
pSendIoContext->wsaBuf.buf = const_cast<char*>(input.c_str());
pSendIoContext->serverAddr = serverAddr;
pSendIoContext->operationType = 1;
DWORD bytesSent;
if (WSASendTo(clientSocket, &(pSendIoContext->wsaBuf), 1, &bytesSent, 0, (sockaddr*)&(pSendIoContext->serverAddr), sizeof(pSendIoContext->serverAddr), &(pSendIoContext->overlapped), NULL) == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
std::cerr << "WSASendTo failed: " << WSAGetLastError() << std::endl;
delete pSendIoContext;
}
}
}
// 清理资源
closesocket(clientSocket);
CloseHandle(hCompletionPort);
CloseHandle(hThread);
WSACleanup();
return 0;
}
```
### 代码解释
#### 服务端
1. **初始化Winsock**:调用`WSAStartup`初始化Winsock库。
2. **创建完成端口**:使用`CreateIoCompletionPort`创建完成端口。
3. **创建工作线程**:创建多个工作线程来处理完成端口上的IO操作。
4. **创建UDP套接字**:使用`socket`函数创建UDP套接字。
5. **绑定端口**:使用`bind`函数将套接字绑定到指定的地址和端口。
6. **关联套接字到完成端口**:使用`CreateIoCompletionPort`将套接字关联到完成端口。
7. **发起接收操作**:使用`WSARecvFrom`函数发起异步接收操作。
8. **处理完成的IO操作**:在工作线程中使用`GetQueuedCompletionStatus`获取完成的IO操作,并处理接收到的数据。
#### 客户端
1. **初始化Winsock**:调用`WSAStartup`初始化Winsock库。
2. **创建完成端口**:使用`CreateIoCompletionPort`创建完成端口。
3. **创建工作线程**:创建一个工作线程来处理完成端口上的IO操作。
4. **创建UDP套接字**:使用`socket`函数创建UDP套接字。
5. **关联套接字到完成端口**:使用`CreateIoCompletionPort`将套接字关联到完成端口。
6. **发起接收操作**:使用`WSARecvFrom`函数发起异步接收操作。
7. **发送数据**:使用`WSASendTo`函数发送数据到服务器。
8. **处理完成的IO操作**:在工作线程中使用`GetQueuedCompletionStatus`获取完成的IO操作,并处理接收到的数据。