diff options
| author | user <user@clank> | 2026-09-22 17:12:41 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 17:12:41 +0200 |
| commit | e23fb93141be593cfd45c2bf8669f71e5aac9bbd (patch) | |
| tree | a7f16c8670b8e0a7d52a50b6087ee1ae59aff0a9 /usr | |
| parent | kernel: per-process sleep - two sleepers used to deadlock each other (diff) | |
| download | gbos-e23fb93141be593cfd45c2bf8669f71e5aac9bbd.tar.gz gbos-e23fb93141be593cfd45c2bf8669f71e5aac9bbd.tar.xz gbos-e23fb93141be593cfd45c2bf8669f71e5aac9bbd.zip | |
net: passive open (listen/accept) + own sockets + close promptly
The stack could only ever dial out. Two new ops give it an ear:
- listen(sock, port): sockets demux on LOCAL PORT alone, so the listener IS
the connection - the SYN fills in the peer and the socket becomes the
connection. One at a time, re-armed by listening again after close, which
is all a 4-socket table can honestly promise. A second client's SYN is
dropped in silence; its retransmit is taken once we are free (~1s), so
three parallel clients complete in 0.1/1.1/2.1s.
- accept(sock): one RX pump per call, never blocks (like recv_nb), so a
server keeps its own main loop and stays killable.
- TCP_LISTEN/TCP_SYNRCVD in tcp_in: answer a SYN with SYN+ACK, become
established on the handshake's ACK, and process any data that ACK carries.
tcp_peer_is_current tells a peer's retransmitted SYN from a newcomer's.
Without it, "any SYN in SYNRCVD is a retransmission" let a second client
*hijack* a connection mid-request: the first client got its SYN+ACK, sent
its GET twice, and was never answered (found in a tcpdump of two parallel
curls).
listen() also refuses a port another socket already holds - two sockets on
one port silently black-hole the second listener, since demux takes the
first match.
Sockets now record the pid that opened them (SK_OWNER) and exit *and* kill
release them: with 4 slots, a few killed irc/nc/httpd runs used to leave the
box unable to open a socket at all until reboot.
tcp_close stops settling the moment the peer's FIN/ACK arrives instead of
burning its whole pump budget. It otherwise sits in FINWAIT for about a
second, and a server that closes and re-listens drops the next client's SYN
in that window: every request after the first paid a SYN retransmit.
1.1s -> 0.1s per request.
Diffstat (limited to 'usr')
| -rw-r--r-- | usr/sock.h | 16 |
1 files changed, 16 insertions, 0 deletions
@@ -48,6 +48,22 @@ 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; 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. */ +static unsigned char net_listen(unsigned char s, unsigned int port) { + _nr.op = 9; + _nr.sock = s; + _nr.port_hi = (unsigned char)(port >> 8); + _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. */ +static unsigned char net_accept(unsigned char s) { + _nr.op = 10; + _nr.sock = s; + return sys_net(&_nr); +} /* 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; |
