diff options
| author | user <user@clank> | 2026-09-22 21:54:03 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 21:54:03 +0200 |
| commit | dcd1f1320c102f9cc4cff7eee387ec538304f7d1 (patch) | |
| tree | e0b48e345c8d34a5f110378e795b8690120ebd55 /usr/sock.h | |
| parent | build: rebuild every object when include/*.inc changes (diff) | |
| download | gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.gz gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.xz gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.zip | |
net: one socket per client - four-tuple demux, spawning accept, bounded close
A server could only ever hold ONE connection, which makes an ircd pointless:
listen() turned the listener into the connection, and net_find_tcp demuxed on
local port alone, so two clients on 6667 were indistinguishable.
- net_find_tcp now matches an established socket on the full four-tuple
(local port + peer IP + peer port) and only falls back to a LISTENing
socket when none matches.
- A SYN at a listener no longer consumes it: tcp_spawn_conn clones a new
socket (local port + owning pid, so exit/kill still reclaims it) and the
listener keeps listening. accept() returns that socket; closing it leaves
the listener alone. A full table drops the SYN, and the client's retransmit
is taken once a slot frees.
- tcp_peer_is_current preserves BC: net_find_tcp scans with the socket index
there, and the compare needs BC for the rx buffer base. Without this the
scan died at the first mismatching socket, so a second client's handshake
ACK never reached its socket and it hung in SYNRCVD - tcpdump showed our
SYN+ACK going out and the client's NICK retransmitted five times into
silence.
- listen() only conflicts with another LISTENER on the port; connections
share it by design now.
- MAX_SOCKS 4 -> 6 (listener + 3 clients + 2 spare; 537 bytes of WRAM0 still
free), and SK_ACC marks a spawned connection as not yet accepted.
tcp_close's settle is also time-boxed to ~250 ms of real time. It counted
PUMPS (8 x 8000), which is ~30s of wall clock when the peer never answers -
invisible with well-behaved peers, but a daemon refusing a client that stayed
connected froze itself, and every other client with it, for half a minute.
httpd keeps its listener open across requests now, so the next client's SYN
is accepted immediately instead of being dropped between close and re-listen:
three parallel clients go 0.1/1.1/2.1s -> 0.4/1.0/0.1s.
Diffstat (limited to 'usr/sock.h')
| -rw-r--r-- | usr/sock.h | 47 |
1 files changed, 33 insertions, 14 deletions
@@ -21,35 +21,47 @@ static struct netreq _nr; /* -> socket id, or 0xFF if the table is full */ static unsigned char net_socket(unsigned char type) { - _nr.op = 0; _nr.sock = type; + _nr.op = 0; + _nr.sock = type; return sys_net(&_nr); } /* set the remote address/port for send()/recv() */ static unsigned char net_connect(unsigned char s, const unsigned char *ip, unsigned int port) { unsigned char i; - _nr.op = 1; _nr.sock = s; + _nr.op = 1; + _nr.sock = s; for (i = 0; i < 4; i++) _nr.ip[i] = ip[i]; - _nr.port_hi = (unsigned char)(port >> 8); _nr.port_lo = (unsigned char)port; + _nr.port_hi = (unsigned char)(port >> 8); + _nr.port_lo = (unsigned char)port; return sys_net(&_nr); } /* send 'len' payload bytes to the connected peer -> 0 ok / 0xFF error */ static unsigned char net_send(unsigned char s, void *buf, unsigned char len) { - _nr.op = 2; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = len; + _nr.op = 2; + _nr.sock = s; + _nr.buf = (unsigned char *)buf; + _nr.len = len; return sys_net(&_nr); } /* receive up to 'max' payload bytes -> length, or 0xFF on timeout */ static unsigned char net_recv(unsigned char s, void *buf, unsigned char max) { - _nr.op = 3; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = max; + _nr.op = 3; + _nr.sock = s; + _nr.buf = (unsigned char *)buf; + _nr.len = max; return sys_net(&_nr); } /* non-blocking recv: one RX pump -> length, 0xFE if nothing buffered yet, 0 on TCP EOF. Loop it with msleep() to own your timeout (see ping). */ static unsigned char net_recv_nb(unsigned char s, void *buf, unsigned char max) { - _nr.op = 8; _nr.sock = s; _nr.buf = (unsigned char *)buf; _nr.len = max; + _nr.op = 8; + _nr.sock = s; + _nr.buf = (unsigned char *)buf; + _nr.len = max; return sys_net(&_nr); } -/* passive open: take connections on a local port (TCP). The listener and the - connection are the same socket, so serve one, close, then listen again. */ +/* passive open: take connections on a local port (TCP). The listener stays a + listener - accept() hands back a separate socket for each client. */ static unsigned char net_listen(unsigned char s, unsigned int port) { _nr.op = 9; _nr.sock = s; @@ -57,8 +69,10 @@ static unsigned char net_listen(unsigned char s, unsigned int port) { _nr.port_lo = (unsigned char)port; return sys_net(&_nr); } -/* -> 0 once a peer has connected, 0xFE if none yet. Never blocks: one RX pump - per call, so loop it with yield()/msleep() and poll the keyboard too. */ +/* -> the new client's socket id, or 0xFE if nobody is waiting. Never blocks: + one RX pump per call, so loop it with yield()/msleep(). Talk to the returned + socket with the ordinary send/recv/close; closing it leaves the listener + alone, so a server can hold several clients at once (see usr/ircd.c). */ static unsigned char net_accept(unsigned char s) { _nr.op = 10; _nr.sock = s; @@ -66,16 +80,21 @@ static unsigned char net_accept(unsigned char s) { } /* set the local (source) port - needed so UDP replies demux back to us */ static unsigned char net_bind(unsigned char s, unsigned int port) { - _nr.op = 5; _nr.sock = s; - _nr.port_hi = (unsigned char)(port >> 8); _nr.port_lo = (unsigned char)port; + _nr.op = 5; + _nr.sock = s; + _nr.port_hi = (unsigned char)(port >> 8); + _nr.port_lo = (unsigned char)port; return sys_net(&_nr); } static void net_close(unsigned char s) { - _nr.op = 4; _nr.sock = s; sys_net(&_nr); + _nr.op = 4; + _nr.sock = s; + sys_net(&_nr); } /* pump the RX path once (answers inbound pings) without blocking */ static void net_poll(void) { - _nr.op = 6; sys_net(&_nr); + _nr.op = 6; + sys_net(&_nr); } /* set our IPv4 address (the DHCP client calls this once it has a lease) */ static void net_setip(const unsigned char *ip) { |
