Skip to content

Replace CAsyncSocketEx custom WinSock wrapper — Boost.Asio or Windows IOCP

Classification

Abandoned by operator decision on 2026-05-21. This historical record preserves the old Boost/POCO analysis only as provenance; do not promote it without a new active item.

Summary

CAsyncSocketEx is ~3,000 lines of custom WinSock2 async infrastructure that dispatches I/O events via hidden Win32 window messages (WM_SOCKETEX_NOTIFY, WM_SOCKETEX_TRIGGER, etc.). This hidden-window message pump approach is fragile, non-testable, and limits concurrency to the UI thread's message rate.

Two viable replacement paths are documented below as idea material; neither is selected for active work.

Current Pain Points

// AsyncSocketEx.h:80 — window-message event dispatch
#define WM_SOCKETEX_TRIGGER  (WM_USER + 0x101 + 0)
#define WM_SOCKETEX_NOTIFY   (WM_USER + 0x101 + 3)
#define MAX_SOCKETS (0xBFFF - WM_SOCKETEX_NOTIFY + 1)

// AsyncSocketEx.h:296 — async DNS via deprecated WinSock 1 callback
char  *m_pAsyncGetHostByNameBuffer;
HANDLE m_hAsyncGetHostByNameHandle;

// UDPSocket.h:78 — raw pointer queue + per-socket lock
CTypedPtrList<CPtrList, SServerUDPPacket*> controlpacket_queue;
CCriticalSection sendLocker;

Files Affected

File Role
srchybrid/AsyncSocketEx.h / .cpp The custom WinSock wrapper — delete under either path
srchybrid/EMSocket.h / .cpp TCP stream layer — migrate
srchybrid/ServerSocket.h / .cpp Server TCP connections — migrate
srchybrid/UDPSocket.h / .cpp UDP datagram layer — migrate
srchybrid/ListenSocket.cpp Incoming connection acceptor — migrate
~65 additional files Consumers of CAsyncSocketEx or raw SOCKET handles

Option A — Boost.Asio

Prerequisites: Boost added as a dependency (any version ≥ 1.74).

#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using boost::asio::ip::udp;

// Shared io_context (replaces hidden helper window + message loop)
boost::asio::io_context io_ctx;

// Async TCP connect
tcp::socket sock(io_ctx);
sock.async_connect(endpoint, [](boost::system::error_code ec) { ... });

// Async DNS (replaces m_hAsyncGetHostByNameHandle)
tcp::resolver resolver(io_ctx);
resolver.async_resolve("host", "4662", [](auto ec, auto results) { ... });

// Async UDP send
udp::socket usock(io_ctx);
usock.async_send_to(boost::asio::buffer(data), remote_ep,
    [](boost::system::error_code ec, std::size_t bytes) { ... });

Pros: Cross-platform; rich async primitives; composable with SSL (Boost.SSL); large community and test coverage.
Cons: Large dependency; significant API surface change across 70+ files; requires io_context threading model decision (single-thread poll vs. thread-pool).

Migration order: 1. Introduce boost::asio::io_context as application singleton. 2. Port CUDPSocket first — simpler than TCP. 3. Port CListenSocket / CServerSocket. 4. Port CEMSocket last — most complex, handles stream framing. 5. Keep CAsyncSocketEx alive in parallel; delete after all consumers are migrated.


Option B — Windows IOCP (no Boost dependency)

Replace the hidden-window message pump with a proper I/O Completion Port (IOCP) thread pool using standard Win32 APIs available on all supported Windows versions.

// Create IOCP
HANDLE hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

// Associate socket with IOCP
CreateIoCompletionPort((HANDLE)sock, hIocp, (ULONG_PTR)pClient, 0);

// Post overlapped receive
WSAOVERLAPPED ov = {};
WSARecv(sock, &wsaBuf, 1, &bytesRecv, &flags, &ov, NULL);

// Worker thread dequeues completed I/O
DWORD bytes; ULONG_PTR key; OVERLAPPED *pOv;
GetQueuedCompletionStatus(hIocp, &bytes, &key, &pOv, INFINITE);
// dispatch based on key / pOv

Pros: Zero new dependencies; highest performance on Windows; natural fit with the Win10-only target; eliminates the hidden window entirely.
Cons: Lower-level API; completion-packet dispatch must be written by hand; no built-in async DNS (use GetAddrInfoEx with completion routine or a dedicated resolver thread).

Async DNS replacement for Option B:

// GetAddrInfoExW with completion routine (Vista+)
ADDRINFOEXW hints = { .ai_family = AF_INET };
GetAddrInfoExW(L"host", L"4662", NS_DNS, NULL, &hints,
               &pResult, NULL, &overlapped, CompletionRoutine, &hCancel);

Recommendation

Criterion Option A (Boost.Asio) Option B (IOCP)
Lines of new code Low (Asio handles plumbing) Medium (IOCP dispatch layer)
External dependency Yes (Boost) No
Cross-platform future Yes Windows-only
API familiarity Moderate Lower
Performance ceiling High (Asio uses IOCP internally on Windows) Identical

If Boost is already adopted (see REF-009), prefer Option A. If staying dependency-free, use Option B.

Acceptance Criteria (both options)

  • [ ] CAsyncSocketEx / AsyncSocketEx.h / .cpp deleted
  • [ ] Hidden helper window (CAsyncSocketExHelperWindow) eliminated
  • [ ] WSAAsyncGetHostByName removed; replaced with async DNS
  • [ ] UDP packet queue uses standard containers with std::mutex
  • [ ] TCP and UDP async I/O verified under load testing
  • [ ] Compile-time: no WM_SOCKETEX_* message defines remain