实现一个IOCP服务器
时间: 2024-01-31 20:04:06 浏览: 29
IOCP(Input/Output Completion Port)是一种高效的异步I/O模型,可用于实现高性能的服务器。在Windows系统中,可以使用IOCP API来创建一个IOCP服务器。
下面是一个简单的IOCP服务器的示例代码:
```c
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#define PORT 12345
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024
typedef struct {
OVERLAPPED overlapped;
SOCKET socket;
WSABUF wsaBuf;
CHAR buffer[BUFFER_SIZE];
DWORD bytesTransferred;
DWORD flags;
} PER_IO_DATA, *LPPER_IO_DATA;
VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD dwBytesTransfered, LPOVERLAPPED lpOverlapped, DWORD dwFlags);
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 创建监听套接字
SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (listenSocket == INVALID_SOCKET) {
printf("Error creating listen socket: %d\n", WSAGetLastError());
return 1;
}
// 绑定端口号
SOCKADDR_IN serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
if (bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
printf("Error binding port: %d\n", WSAGetLastError());
return 1;
}
// 监听连接
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("Error listening: %d\n", WSAGetLastError());
return 1;
}
// 创建IOCP句柄
HANDLE iocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (iocpHandle == NULL) {
printf("Error creating IOCP handle: %d\n", GetLastError());
return 1;
}
// 将监听套接字绑定到IOCP句柄上
if (CreateIoCompletionPort((HANDLE)listenSocket, iocpHandle, (ULONG_PTR)listenSocket, 0) == NULL) {
printf("Error binding listen socket to IOCP handle: %d\n", GetLastError());
return 1;
}
// 创建客户端套接字
SOCKET clientSockets[MAX_CLIENTS];
for (int i = 0; i < MAX_CLIENTS; i++) {
clientSockets[i] = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (clientSockets[i] == INVALID_SOCKET) {
printf("Error creating client socket: %d\n", WSAGetLastError());
return 1;
}
}
// 接受连接
for (;;) {
SOCKADDR_IN clientAddr;
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(listenSocket, (SOCKADDR*)&clientAddr, &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
printf("Error accepting connection: %d\n", WSAGetLastError());
continue;
}
// 将客户端套接字绑定到IOCP句柄上
if (CreateIoCompletionPort((HANDLE)clientSocket, iocpHandle, (ULONG_PTR)clientSocket, 0) == NULL) {
printf("Error binding client socket to IOCP handle: %d\n", GetLastError());
closesocket(clientSocket);
continue;
}
// 分配PER_IO_DATA结构体
LPPER_IO_DATA perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_DATA));
perIoData->socket = clientSocket;
perIoData->wsaBuf.buf = perIoData->buffer;
perIoData->wsaBuf.len = BUFFER_SIZE;
perIoData->flags = 0;
// 开始接收数据
DWORD bytesRecv;
if (WSARecv(clientSocket, &perIoData->wsaBuf, 1, &bytesRecv, &perIoData->flags, &perIoData->overlapped, CompletionRoutine) == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) {
printf("Error starting receive: %d\n", WSAGetLastError());
GlobalFree(perIoData);
closesocket(clientSocket);
continue;
}
}
}
// 关闭监听套接字
closesocket(listenSocket);
// 释放资源
for (int i = 0; i < MAX_CLIENTS; i++) {
closesocket(clientSockets[i]);
}
WSACleanup();
return 0;
}
VOID CALLBACK CompletionRoutine(DWORD dwErrorCode, DWORD dwBytesTransfered, LPOVERLAPPED lpOverlapped, DWORD dwFlags)
{
LPPER_IO_DATA perIoData = (LPPER_IO_DATA)lpOverlapped;
if (dwErrorCode == 0 && dwBytesTransfered > 0) {
printf("Received %d bytes from socket %d\n", dwBytesTransfered, perIoData->socket);
}
else {
printf("Error receiving data: %d\n", dwErrorCode);
}
GlobalFree(perIoData);
closesocket(perIoData->socket);
}
```
在这个示例中,我们首先创建一个监听套接字,绑定端口号并开始监听连接。然后,我们创建一个IOCP句柄,并将监听套接字绑定到IOCP句柄上。
接下来,我们创建一些客户端套接字,并在接受连接时将它们绑定到IOCP句柄上。每当有一个客户端连接到服务器时,我们为其分配一个PER_IO_DATA结构体,并使用WSARecv函数开始接收数据。在WSARecv函数中,我们将PER_IO_DATA结构体的地址传递给了overlapped参数,这样就可以在数据接收完成后得到它。
当数据接收完成后,系统会调用CompletionRoutine回调函数。在这个函数中,我们可以检查数据接收是否成功,并释放PER_IO_DATA结构体和客户端套接字。
需要注意的是,在IOCP服务器中,所有的I/O操作都是异步的,因此我们需要使用OVERLAPPED结构体来描述每一个I/O操作,并在每个I/O操作完成后启动回调函数。在这个示例中,我们使用了PER_IO_DATA结构体来扩展OVERLAPPED结构体,并包含了一些额外的信息,例如套接字、接收缓冲区等。