Skip to content

v3.1.0

Latest

Choose a tag to compare

@NewbieOrange NewbieOrange released this 02 Oct 08:05

ShadowLAN 3.1.0

A latency and bufferbloat release. Games now hear from each other as fast as the network allows, and bulk transfers still run at
full link speed, matching a plain TCP connection over the same path.

The wire protocol is unchanged (PVER 3 / UVER 3): 3.0.0 and 3.1.0 hooks and relays work together. Deploy the new server.py on
your relay to get the relay-side improvements.

Highlights

  • Low latency on every way a game waits for data. Before, the hook checked for tunneled data on a timer, which added delay
    depending on how the game read its sockets:

    • TCP with a blocking recv: about 10 ms
    • TCP or UDP with select, poll or WSAPoll: about 25 ms (51 ms for blocking UDP receives on Windows)

    Now a game wakes up the moment data arrives: about 0.1 ms round trip on Linux and about 0.5 ms on Windows (measured under Wine),
    not counting the network.

  • No more busy-spinning. Games waiting on a tunneled TCP connection with poll/select used to spin a CPU core at 60–99%.
    They now sleep until data arrives.

  • Much less bufferbloat during bulk transfers. On a 20 Mbit/s link with 40 ms round trip, a message sent on the same connection
    as a running download used to wait about 4 seconds. Now it waits about 240 ms, which is less than a plain kernel TCP connection on
    the same path (about 555 ms). Throughput is unchanged.

  • UDP-over-TCP stays responsive under load. When game traffic exceeds the link, stale datagrams are dropped instead of queuing
    without limit. Latency stays around 200 ms instead of growing past several seconds.

Fixes

  • Linux: blocking UDP receives no longer hang. A plain blocking recvfrom on a tunneled UDP socket could block forever.
  • Startup deadlock when several threads bind at once. Two threads binding sockets at the same time could leave every later
    bind() in the process hanging.
  • Data at process exit. A game that sent data and exited immediately could lose the tail of it. Two causes are fixed: the
    hook's exit flush could silently skip a stream, and the relay closed a departing machine's open connections before their last bytes
    arrived.
  • End of stream is reported to poll/select. When the other side closes a TCP connection, the game's poll/select now
    reports it as readable, as a real kernel does.
  • New hosted UDP sessions get their first reply immediately instead of after up to 100 ms.
  • wclient relay connections now have Nagle's algorithm disabled, avoiding delays on small writes.

Bandwidth

Bulk throughput matches a direct kernel TCP connection over the same links:

Link 3.0.0 3.1.0 Plain kernel TCP
20 Mbit/s, 40 ms RTT 19.1 Mbit/s 19.1 Mbit/s 19.0 Mbit/s
200 Mbit/s, 100 ms RTT 187 Mbit/s 186 Mbit/s 181 Mbit/s

Tunnel connections now let the operating system size their buffers automatically. Before, 3.0.0 requested fixed 4 MB buffers, which
turned that off. On a typical VPS the kernel cuts such a request down to about 425 KB, which limited throughput on long, fast
paths.

Under the hood

  • Every waiting call (recv, recvfrom, connect, send, poll, ppoll, select, pselect, WSAPoll) is woken by the hook
    as soon as data arrives, instead of rechecking on a timer.
  • Unsent data is kept small (TCP_NOTSENT_LOWAT on Linux and the relay). The app-side send queue per connection is 256 KB, down
    from 4 MB. A single larger send is still accepted in parts, as a real socket does.
  • Each control message goes out in one TCP segment instead of three.

Upgrading

  • Relay: replace server.py and restart. Existing 3.0.0 hooks keep working.
  • Players: replace the hook files from the package for your platform. A 3.1.0 hook works with a 3.0.0 relay too, but you only
    get the full improvement with both upgraded.

Assets

  • shadowlan-v3.1.0-windows-amd64.zip: lan_hook64.dll, lan_hook32.dll, injector.exe, relay and client scripts, docs
  • shadowlan-v3.1.0-linux-amd64.tar.gz: lan_hook.so, relay and client scripts, docs