2. echo (serial)

import std.socket : InternetAddress, Socket, TcpSocket, SocketOptionLevel, SocketOption, Address;
import std.stdio : writefln;
import std.typecons : Unique;

enum port = 4444;

void main() {
    Unique!TcpSocket listener = new TcpSocket;
    listener.blocking = true;
    listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 1);
    listener.bind(new InternetAddress(port));
    listener.listen(10);
    writefln!"Listening on %d."(port);

    ubyte[4096] buf;
    ptrdiff_t len;

    while (true) {
        Unique!Socket client = listener.accept;
        Unique!Address remote = client.remoteAddress;
        writefln!"Received connection from %s."(remote.toString);

        while (0 < (len = client.receive(buf[]))) {
            client.send(buf[0 .. len]);
        }
        writefln!"Lost connection from %s."(remote.toString);
    }
}

This server accepts one connection at a time, receives up to 4k bytes from it and then echoes them back immediately, reusing the buffer. The buffer uses a static array, a value type, and slices it twice: first, so that receive can pass the slice ptr and length to the underlying syscall, and second, so that send can do the same with the exact length of bytes received. When a receive returns 0 (on connection close) or a negative number (on error), the inner loop ends and client is destructed with the end of the outer loop, closing it.

Client interaction

$ ncat localhost 4444
this is a line of input ... followed by
this is a line of input ... followed by
^^ an echo from the server
^^ an echo from the server
$ ncat localhost 4444 < echo > echo.out
$ diff -s echo echo.out
Files echo and echo.out are identical

what's with setting listener.blocking?

Blocking is the default for a TcpSocket, so this is a no-op.

why is the address saved in a local variable?

It's an error to ask for a closed socket's remoteAddress

what's with these Unique types?

In all cases in this code, scope would work just as well. These will show up in a few more servers but at most force Socket destructors to run at the end of a scope.

is this server fit for purpose?

This one's a lot less defensible than the previous server which just responded to connections with a packet and closed them immediately. This server is unavailable for use while a single connection has it open, and a denial-of-service attack is as simple as ncat server 4444 </dev/zero. An echo server really needs to handle concurrent connections.