Skip to content

Sockets: add the WinSocket ServerSocket - #1794

Merged
Mysticial merged 6 commits into
mainfrom
sockets-server-winsocket
Nov 1, 2026
Merged

Mysticial merged 6 commits into
mainfrom
sockets-server-winsocket

Conversation

@pa-devbot

@pa-devbot pa-devbot commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Based on the PR for the Qt ServerSocket + test harness (merge that one first, then rebase this onto main).

Adds ServerSocket_WinSocket (an accept-loop thread plus one
connection thread at a time, both from the given thread pool via plain
dispatch(), non-blocking sockets) and enables the WinSocket
instantiation of the loopback tests on Windows.

Design (mirrors the POSIX backend)

  • close()/close_connection() only flip the state; the connection
    thread notices within ~10 ms and closes the socket itself
    (close_connection() also calls shutdown(SD_BOTH)), so
    close_connection() is safe to call from within a listener callback.
  • The accept loop keeps running while a connection is active and
    rejects new clients via on_connect_rejected (one connection at a
    time).
  • The connection task is started and published without holding the
    main lock
    (same AsyncTask destructor / task-slot deadlock as the
    POSIX backend).
  • WSAStartup runs once via std::call_once on the thread that calls
    start(), so a ClientSocket_WinSocket created immediately after
    can use Winsock before the listener thread has run.

Windows build fix (reported by the failed Windows CI run):

  • Both WinSocket headers now include <winsock2.h> instead of
    <winsock.h>: the legacy header does not declare SD_BOTH (used by
    close_connection()), which broke the build with
    error C2065: 'SD_BOTH': undeclared identifier.
  • Renamed the inner non_blocking in the accept loop to
    client_non_blocking, fixing warning C4456 (it shadowed the one
    for the listen socket).

Tests: the 7 loopback scenarios now run against the WinSocket
backend on Windows in addition to Qt. The WinSocket code was
functional-tested on Linux against an in-memory mock Winsock loopback
kernel (7 scenario tests + 10-client stress, all passing).

Note (rebuild on current main): the implementation now lives in ServerSocket_WinSocket.cpp, mirroring the client header/.cpp split (#1795). Additionally, TestProgramComputer.cpp defines WIN32_LEAN_AND_MEAN before <Windows.h> so the legacy <winsock.h> is not pulled in ahead of <winsock2.h> (the two are mutually exclusive; that mix broke the Windows CI with sockaddr redefinition errors).

Update (Windows CI): the "Port in use" test failed on both Windows
CIs - the second server's bind() succeeded on a port that was
already in use. Cause: the listen socket set SO_REUSEADDR, which on
Windows (unlike POSIX) allows a second socket to bind to the same
address and port. The listen socket no longer sets it. This is now
covered by the mock Winsock kernel, which models the real Windows
SO_REUSEADDR semantics (a duplicate bind is only allowed when both
sockets set the option).

Update (formatting): fixed ServerSocket_WinSocket.cpp
indentation left over from the header/.cpp split (method bodies had
kept their in-class 8-space indent). The Mutex/ConditionVariable
wrapper migration from #1787 comes in with the rebase.

@pa-devbot
pa-devbot force-pushed the sockets-server-winsocket branch from 09dfc2a to 51297cf Compare November 1, 2026 01:42
@pa-devbot
pa-devbot force-pushed the sockets-server-loopback branch from 82d5b0f to 544b9fb Compare November 1, 2026 02:44
@pa-devbot
pa-devbot force-pushed the sockets-server-winsocket branch from 51297cf to d3048ed Compare November 1, 2026 03:23
@pa-devbot
pa-devbot force-pushed the sockets-server-loopback branch from 544b9fb to 20140b7 Compare November 1, 2026 04:53
@pa-devbot
pa-devbot force-pushed the sockets-server-winsocket branch from cc44056 to e46a260 Compare November 1, 2026 04:59
@pa-devbot
pa-devbot force-pushed the sockets-server-winsocket branch 2 times, most recently from b48c75d to 9c8924f Compare November 1, 2026 09:17
Base automatically changed from sockets-server-loopback to main November 1, 2026 09:35
Implements ServerSocket_WinSocket (an accept-loop thread plus one
connection thread at a time, both from the given thread pool via plain
dispatch(), with non-blocking sockets; the implementation lives in
ServerSocket_WinSocket.cpp, following the split of the client backends)
and enables the WinSocket instantiation of the loopback tests on
Windows.

close()/close_connection() only flip the state: the connection thread
notices within ~10 ms and closes the socket itself
(close_connection() also calls shutdown(SD_BOTH)), so
close_connection() is safe to call from within a listener callback.
The accept loop keeps running while a connection is active and rejects
new clients via on_connect_rejected. The connection task is started
and published without holding the main lock, for the same reason as in
the POSIX backend.

WSAStartup runs once via std::call_once on the thread that calls
start(), so a ClientSocket_WinSocket created immediately after can use
Winsock before the listener thread has run.

Windows build fixes:
- Both WinSocket headers now include <winsock2.h> instead of the legacy
  <winsock.h>: the legacy header does not declare SD_BOTH (used by
  close_connection()), which broke the Windows build.
- TestProgramComputer.cpp defines WIN32_LEAN_AND_MEAN before
  <Windows.h>, so the legacy <winsock.h> is not pulled in ahead of
  <winsock2.h> (the two headers are mutually exclusive).

The WinSocket code was functional-tested on Linux against an
in-memory mock Winsock loopback kernel (all 7 loopback scenarios plus
a multi-client stress test).
On Windows, SO_REUSEADDR allows a second socket to bind to the same
address and port (unlike on POSIX, where it only relaxes the TIME_WAIT
restriction), so the "Port in use" loopback test failed on the Windows
CI: the second server's bind() succeeded instead of failing.

The listen socket no longer sets the option; a port is exclusive to the
server that holds it.
…split

The method bodies kept their in-class 8-space indentation from the
monolithic header; un-dedent them to file scope.
@pa-devbot
pa-devbot force-pushed the sockets-server-winsocket branch from 9c8924f to a83c168 Compare November 1, 2026 09:57
Setting it per source file was a consistency landmine: any TU that
includes <Windows.h> without it pulls in the legacy <winsock.h>,
which is mutually exclusive with the <winsock2.h> the socket layer
uses. Define it for every target on Windows in
pa_apply_base_target_properties() instead, and drop the local
define from TestProgramComputer.cpp.

Nothing in the codebase uses declarations that the lean build
omits (legacy winsock, mmsystem, shellapi); all Winsock usage
includes <winsock2.h> explicitly.
The header/.cpp split left 2-3 blank lines between most members;
normalize to the single-blank style used by the client headers.
@Mysticial
Mysticial merged commit a67257f into main Nov 1, 2026
6 of 7 checks passed
@Mysticial
Mysticial deleted the sockets-server-winsocket branch November 1, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants