21xrx.com
2025-06-16 17:27:52 Monday
文章检索 我的文章 写文章
C++ IOCP完整代码
2023-07-05 13:10:46 深夜i     20     0
C++ IOCP 完整代码

C++ IOCP全称为Input/Output Completion Port,是一种高性能的网络编程技术。在网络编程领域中,IOCP的应用领域非常广泛,因为它提供了对异步I/O操作的支持。本文将为您介绍C++ IOCP完整代码。

首先,需要在Windows上安装Visual Studio环境。然后,我们可以创建一个控制台应用程序。在代码中引入以下头文件:

#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <windows.h>

接着,我们必须初始化Winsock库:

WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
  printf("WSAStartup failed with error %d\n", WSAGetLastError());
  return 1;
}

接着,创建一个套接字:

SOCKET listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSock == INVALID_SOCKET)
{
  printf("Socket creation failed with error %d\n", WSAGetLastError());
  return 1;
}

为套接字绑定本地地址,然后开始监听:

SOCKADDR_IN localAddr;
ZeroMemory(&localAddr, sizeof(localAddr));
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(4000);
localAddr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenSock, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR)
{
  printf("Bind failed with error %d\n", WSAGetLastError());
  return 1;
}
if (listen(listenSock, 5) == SOCKET_ERROR)
{
  printf("Listen failed with error %d\n", WSAGetLastError());
  return 1;
}

接下来,我们要创建一个完成端口:

HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
if (completionPort == NULL)
{
  printf("CreateCompletionPort failed with error %d\n", GetLastError());
  return 1;
}

然后,我们需要让套接字和完成端口相关联:

HANDLE listenCompletionPort = CreateIoCompletionPort((HANDLE)listenSock, completionPort, 0, 0);
if (listenCompletionPort == NULL)
{
  printf("CreateCompletionPort failed with error %d\n", GetLastError());
  return 1;
}

现在,我们可以创建一个接受请求的线程:

DWORD WINAPI AcceptThread(LPVOID lpParam)
{
  HANDLE completionPort = (HANDLE)lpParam;
  while (true)
  {
    SOCKADDR_IN remoteAddr;
    int remoteAddrLen = sizeof(remoteAddr);
    SOCKET clientSock = accept(listenSock, (SOCKADDR*)&remoteAddr, &remoteAddrLen);
    if (clientSock == INVALID_SOCKET)
    {
      printf("Accept failed with error %d\n", WSAGetLastError());
      continue;
    }
    // 新建套接字和完成端口的关联
    HANDLE clientCompletionPort = CreateIoCompletionPort((HANDLE)clientSock, completionPort, (ULONG_PTR)clientSock, 0);
    if (clientCompletionPort == NULL)
    {
      printf("CreateCompletionPort for client socket failed with error %d\n", GetLastError());
      closesocket(clientSock);
      continue;
    }
    printf("New client connected. IP address: %s, Port: %d\n", inet_ntoa(remoteAddr.sin_addr), ntohs(remoteAddr.sin_port));
    // 开始异步接收请求
    OverlappedContext* context = new OverlappedContext();
    ZeroMemory(&context->overlapped, sizeof(context->overlapped));
    context->operationType = OperationType::Receive;
    DWORD flags = 0;
    int recvBytes = 0;
    WSARecv(clientSock, &(context->wsaBuffer), 1, (LPDWORD)&recvBytes, &flags, &(context->overlapped), NULL);
  }
  return 0;
}

在我们的代码中,OverlappedContext结构体表示了一个操作的上下文。当一个I/O操作完成时,会并发的通知一个完成端口,然后我们就可以在完成端口中获取对应的上下文并进行后续操作。

另外,我们还需要定义OperationType枚举:

enum class OperationType
  Receive;

接下来,我们可以创建一个接收请求的工作线程:

DWORD WINAPI WorkerThread(LPVOID lpParam)
{
  HANDLE completionPort = (HANDLE)lpParam;
  while (true)
  {
    DWORD bytesTransferred = 0;
    ULONG_PTR completionKey = 0;
    LPOVERLAPPED overlapped = NULL;
    BOOL result = GetQueuedCompletionStatus(completionPort, &bytesTransferred, &completionKey, &overlapped, INFINITE);
    if (!result)
    {
      printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
      continue;
    }
    OverlappedContext* context = CONTAINING_RECORD(overlapped, OverlappedContext, overlapped);
    if (bytesTransferred == 0)
    {
      closesocket((SOCKET)completionKey);
      printf("Client disconnected.\n");
      continue;
    }
    if (context->operationType == OperationType::Receive)
    {
      printf("Received %d bytes from client. Message: %s\n", bytesTransferred, context->wsaBuffer.buf);
      // 将收到的数据原封不动返回给客户端
      context->operationType = OperationType::Send;
      ZeroMemory(&context->overlapped, sizeof(context->overlapped));
      DWORD sendBytes = 0;
      WSASend((SOCKET)completionKey, &(context->wsaBuffer), 1, (LPDWORD)&sendBytes, 0, &(context->overlapped), NULL);
    }
    else if (context->operationType == OperationType::Send)
    {
      printf("Sent %d bytes to client. Message: %s\n", bytesTransferred, context->wsaBuffer.buf);
      // 开始下一次异步接收请求
      context->operationType = OperationType::Receive;
      ZeroMemory(&context->overlapped, sizeof(context->overlapped));
      DWORD flags = 0;
      int recvBytes = 0;
      WSARecv((SOCKET)completionKey, &(context->wsaBuffer), 1, (LPDWORD)&recvBytes, &flags, &(context->overlapped), NULL);
    }
  }
  return 0;
}

我们的工作线程将通过GetQueuedCompletionStatus函数获取已完成的I/O操作。如果I/O操作返回0字节,说明客户端已断开连接,我们会关闭套接字并继续等待下一个连接。

最后,我们将在主函数中启动两个线程:

HANDLE acceptThreadHandle = CreateThread(NULL, 0, AcceptThread, completionPort, 0, NULL);
HANDLE workerThreadHandle1 = CreateThread(NULL, 0, WorkerThread, completionPort, 0, NULL);
HANDLE workerThreadHandle2 = CreateThread(NULL, 0, WorkerThread, completionPort, 0, NULL);
WaitForSingleObject(acceptThreadHandle, INFINITE);
WaitForSingleObject(workerThreadHandle1, INFINITE);
WaitForSingleObject(workerThreadHandle2, INFINITE);

现在,我们已经完成了一个使用C++ IOCP的简单的网络服务器。这里提供了完整的代码,您可以放心地进行学习和使用。

  
  

评论区