21xrx.com
2025-03-31 22:46:21 Monday
文章检索 我的文章 写文章
C++完成端口编程
2023-07-05 00:20:19 深夜i     14     0
- C++ - 完成端口 - 编程 - I/O - 多线程

C++完成端口编程是一种高效的网络编程方式,在现代网络应用程序中被广泛使用。

完成端口 (Completion Port) 是 Windows 操作系统中的一种 I/O 模型,它允许应用程序高效地管理大量的 I/O 操作。通过使用完成端口,应用程序可以异步地向操作系统注册一系列待完成的 I/O 操作。当这些 I/O 操作完成后,操作系统会通知应用程序,应用程序再进行相应的处理。

C++完成端口编程使用的是异步操作方式,可以大大提升网络应用程序的性能和可扩展性。与传统的同步操作模型相比,异步操作模型可以让应用程序更好地处理大量的连接请求和消息处理。

在 C++中,完成端口编程常常用于实现高效的 TCP/UDP 通信,以及其他 I/O 相关的网络应用程序。以下是一个例子:

#include <iostream>
#include <Windows.h>
int main()
{
  HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  if (NULL != hCompletionPort)
  {
    // 启动多个 worker 线程
    for (int i = 0; i < 4; i++)
    {
      DWORD dwThreadId;
      CreateThread(NULL, 0, WorkerThreadProc, hCompletionPort, 0, &dwThreadId);
    }
    // 创建一个 TCP 监听Socket
    SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    if (INVALID_SOCKET != sock)
    {
      SOCKADDR_IN sin;
      memset(&sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_port = htons(8080);
      sin.sin_addr.s_addr = htonl(INADDR_ANY);
      // 绑定 Socket
      if (bind(sock, (SOCKADDR*)&sin, sizeof(sin)) == 0)
      {
        // 开始监听 Socket
        if (listen(sock, SOMAXCONN) == 0)
        {
          while (true)
          {
            // 等待有新的连接
            SOCKET client = accept(sock, NULL, NULL);
            if (INVALID_SOCKET != client)
            {
              // 将新连接的 Socket 注册到完成端口上
              CreateIoCompletionPort((HANDLE)client, hCompletionPort, (ULONG_PTR)client, 0);
              // 向新连接的 Socket 发送欢迎信息
              char buf[] = "Welcome to my server!\r\n";
              WSABUF wbuf;
              wbuf.buf = buf;
              wbuf.len = strlen(buf);
              int ret = WSASend(client, &wbuf, 1, NULL, 0, NULL, NULL);
              if (SOCKET_ERROR == ret)
              {
                // 发送错误,关闭连接
                closesocket(client);
              }
            }
          }
        }
      }
      closesocket(sock);
    }
    // 关闭完成端口句柄
    CloseHandle(hCompletionPort);
  }
  return 0;
}
DWORD WINAPI WorkerThreadProc(LPVOID lpParam)
{
  HANDLE hCompletionPort = (HANDLE)lpParam;
  DWORD dwNumBytes = 0;
  ULONG_PTR dwCompletionKey = 0;
  LPOVERLAPPED lpOverlapped = NULL;
  while (true)
  {
    BOOL ret = GetQueuedCompletionStatus(hCompletionPort, &dwNumBytes, &dwCompletionKey, &lpOverlapped, INFINITE);
    if (ret)
    {
      // 处理 I/O 完成事件
      if (NULL != lpOverlapped)
      {
        // 这里假设所有 I/O 操作返回的数据都是字符串类型
        char* pData = (char*)lpOverlapped->hEvent;
        std::cout << "Received: " << pData << std::endl;
        // 释放 I/O 操作的缓冲区
        delete[] pData;
        // 重新投递一个新的 I/O 操作
        SOCKET sock = (SOCKET)dwCompletionKey;
        char* pBuf = new char[1024];
        WSABUF wbuf;
        wbuf.buf = pBuf;
        wbuf.len = 1024;
        LPOVERLAPPED lpNewOverlapped = new OVERLAPPED;
        memset(lpNewOverlapped, 0, sizeof(OVERLAPPED));
        lpNewOverlapped->hEvent = (HANDLE)pBuf;
        int ret = WSARecv(sock, &wbuf, 1, NULL, 0, lpNewOverlapped, NULL);
        if (SOCKET_ERROR == ret && ERROR_IO_PENDING != WSAGetLastError())
        {
          // 投递失败,关闭连接
          closesocket(sock);
        }
      }
    }
    else
    退出
      break;
    
  }
  return 0;
}

以上代码演示了如何使用 C++ 的完成端口编程实现一个 TCP 服务器。其中,我们通过 `CreateIoCompletionPort` 函数将 Socket 注册到完成端口上,然后在 worker 线程中调用 `GetQueuedCompletionStatus` 函数等待 I/O 完成事件。当 I/O 完成事件发生时,我们可以在 worker 线程中处理完成事件并重新投递一个新的 I/O 操作。

在实际使用中,我们还可以通过使用 C++11 中的 `std::async` 函数来方便地实现异步操作。不过需要注意的是,C++完成端口编程是基于 Windows 平台的,因此在其他操作系统上可能无法直接使用。

  
  

评论区