21xrx.com
2024-09-19 09:58:02 Thursday
登录
文章检索 我的文章 写文章
C++ IOCP完整代码
2023-07-05 13:10:46 深夜i     --     --
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的简单的网络服务器。这里提供了完整的代码,您可以放心地进行学习和使用。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复