Networking

Beyond reading the connection status (NetworkInfo) and downloading over HTTP (HttpClient), the SDK exposes sockets, so a module can be a network client or a server: a file transfer tool, a small web server, a remote-control listener, or a custom protocol.

Sockets live in SharpProspero.Platform. They are IPv4 and cover TCP and UDP, with a poller for serving many connections from one thread and a resolver for connecting by host name.

Addresses

A SocketAddress is an IPv4 endpoint: four address octets and a port.

var any = SocketAddress.Any(8080);                 // every interface, port 8080
var local = SocketAddress.Loopback(9000);          // 127.0.0.1:9000
var server = SocketAddress.Parse("192.168.1.10", 21);

A TCP client

Connect, send a request, read the reply, and dispose the connection.

using var conn = TcpConnection.Connect(SocketAddress.Parse("192.168.1.10", 80));
conn.SendAll("GET / HTTP/1.0\r\n\r\n"u8);

Span<byte> buffer = stackalloc byte[2048];
int read = conn.Receive(buffer);                   // 0 means the peer closed the connection

SendAll repeats until every byte is accepted. Send sends once and reports how many bytes went, for callers that manage their own buffering.

A TCP server

Bind a listener, accept a client, serve it. This shape handles one client at a time.

using var listener = TcpListener.Listen(SocketAddress.Any(8080));
while (running)
{
    using TcpConnection client = listener.Accept();
    Span<byte> request = stackalloc byte[1024];
    int read = client.Receive(request);
    client.SendAll(response);
}

Serving many clients from one thread

To serve several connections at once without threads, set the sockets to non-blocking and drive them from a SocketPoller. The poller reports which sockets are ready; register each with a token the caller chooses, usually an index into its own table of connections.

using var listener = TcpListener.Listen(SocketAddress.Any(8080));
listener.Blocking = false;

using var poller = SocketPoller.Create();
poller.Add(listener.Handle, PollEvents.Read, token: 0);

Span<PollReady> ready = stackalloc PollReady[32];
while (running)
{
    int count = poller.Wait(ready, timeoutMicroseconds: -1);   // -1 waits until something is ready
    for (int i = 0; i < count; i++)
    {
        if (ready[i].Token == 0)
        {
            TcpConnection client = listener.Accept();
            client.Blocking = false;
            // register client.Handle with its own token and keep it in a table
        }
        else if (ready[i].IsReadable)
        {
            // read from the connection the token maps to
        }
    }
}

SocketPoller.Abort unblocks a thread waiting in Wait, so a server loop can be told to stop.

An HTTP server

HttpServer builds a small HTTP/1.1 server on these sockets, so a module can serve a page or an API to a phone or a computer on the same network — a remote control panel, a status page, or a file browser. Start it on a port and call PollOnce each frame so it never blocks the loop; it answers one waiting request and returns. A handler maps a request to a response.

using var server = HttpServer.Start(8080);

// In the frame loop, once per frame:
server.PollOnce(request => request.Path switch
{
    "/" => HttpServerResponse.Html("<h1>Hello from C#</h1>"),
    "/status" => HttpServerResponse.Json("{\"ok\":true}"),
    _ => HttpServerResponse.NotFound(),
});

HttpServerRequest gives the Method, Path (percent-decoded), Query, Headers and Body. HttpServerResponse has Text, Html, Json, Bytes, NotFound and Redirect builders, and its StatusCode, ContentType, Headers and Body are settable for anything else. Each request is answered and its connection closed, which keeps it simple and robust. To dedicate the loop to serving instead of polling, call Run(handler, keepRunning). Bind to the loopback address only, with Start(port, loopbackOnly: true), for a server just this console reaches.

UDP

Datagrams need no connection. Bind to receive, send to an explicit destination.

using var udp = UdpSocket.Bind(SocketAddress.Any(9000));
Span<byte> buffer = stackalloc byte[1500];
int read = udp.ReceiveFrom(buffer, out SocketAddress sender);
udp.SendTo(reply, sender);

Connecting by host name

When the target is a name rather than an address, resolve it first. A HostResolver owns a small network pool for its lifetime.

using var dns = HostResolver.Create();
SocketAddress address = dns.Resolve("example.com", 80);
using var conn = TcpConnection.Connect(address);

Errors

A failed socket call raises a ProsperoException whose Code carries the network error, so a caller can branch on a specific failure such as a refused connection or a timeout.


Copyright © SvenGDK 2026. Distributed under the GPL-3.0-or-later license.

This site uses Just the Docs, a documentation theme for Jekyll.