4. chat (socketset)
import std.socket : InternetAddress, Socket, TcpSocket, SocketOptionLevel,
SocketOption, Address, SocketSet;
import std.stdio : writefln;
import std.typecons : Unique, Nullable;
import std.algorithm : remove;
enum PORT = 4444;
enum MAX_CONNECTIONS = 40;
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);
scope sockets = new SocketSet(MAX_CONNECTIONS + 1);
Socket[] reads;
Address[] addrs;
ubyte[4096] buf;
ptrdiff_t len;
while (true) {
sockets.reset;
sockets.add(listener.handle);
foreach (sock; reads)
sockets.add(sock.handle);
Socket.select(sockets, null, null);
for (size_t i = 0; i < reads.length; i++) {
if (!sockets.isSet(reads[i].handle))
continue;
if (0 < (len = reads[i].receive(buf[]))) {
foreach (j; 0 .. reads.length)
if (j != i)
reads[j].send(buf[0 .. len]);
} else {
writefln!"Lost connection from %s."(addrs[i]);
reads[i].close;
reads = reads.remove(i);
addrs = addrs.remove(i);
i--; // restart loop at same i
}
}
if (sockets.isSet(listener.handle)) {
Socket client = listener.accept;
if (reads.length < MAX_CONNECTIONS) {
writefln!"Received connection from %s."(client.remoteAddress.toString);
addrs ~= client.remoteAddress;
reads ~= client;
} else {
writefln!"Rejected connection from %s; too many connections (%d)."(
client.remoteAddress.toString, reads.length);
client.close;
}
}
}
}
This server is almost exactly like echo socketset in how it functions, except that it repeats all incoming traffic back to all connected clients, resulting in a crude chat environment.
With the connection limit, are those dynamic arrays as memory-efficient as static arrays?
No, they reallocate on every append that follows a.remove. This kind of stack-like use of dynamic arrays needs to make careful use of assumeSafeAppend to avoid unnecessary reallocations.