needs tests/harness io/stream com/ip4 com/tcp testbegin $200 const MYPAYLOADSZ create mypayload MYPAYLOADSZ allot0 0 value sentcnt : :readframe abort"readframe shouldn't be called" ; : :beginframe to curlink mypayload to payloadptr MYPAYLOADSZ to payloadsz ; : :replytoframe abort"replytoframe shouldn't be called" ; : :sendframe drop doto sentcnt 1+ | ; : :waitsent drop ; ' :waitsent ' :sendframe ' :replytoframe ' :beginframe ' :readframe newlink const fakelink : inbound ( remoteport localport addr -- ) fakelink newdgram LOCALHOST to DestAddr to SourceAddr ( rport lport ) swap 16 lshift or to Ports fakelink to curlink ; \test accept an initial SYN $1234 $2345 $33445566 inbound SYN Flags! 42 to SeqNum opts[ ]opts 0 0 tcp[]! com/tcp wrap acceptcon dup #true const con1 sentcnt 0 #eq \ nothing is sent directly tcpwork con1 status SynAckSent #eq sentcnt 1 #eq DestAddr $33445566 #eq SourcePort $2345 #eq DestPort $1234 #eq SeqNum 0 #eq \ TODO: some day start with a random initial seqnum FlagsOnly SYN ACK or #eq AckNum 43 #eq -1 con1 readbuf 0 #eq \ nothing to read \test Send back the final ACK before establishing connection $1234 $2345 $33445566 inbound ACK Flags! 42 to SeqNum 1 to AckNum opts[ ]opts 0 0 tcp[]! com/tcp wrap ' handletcp expectevent sentcnt 1 #eq con1 status Established #eq -1 con1 readbuf 0 #eq \ nothing to read \test receive "hello" $1234 $2345 $33445566 inbound 0 Flags! 43 to SeqNum 1 to AckNum opts[ ]opts "hello" c@+ tcp[]! com/tcp wrap ' handletcp expectevent -1 con1 readbuf "hello" #[]s= \ The ACK is not sent directly, it's send through tcpwork after a timeout sentcnt 1 #eq \test don't send an ACK right away tcpwork sentcnt 1 #eq \test send an ACK after MAXINACKMS milliseconds MAXINACKMS 1+ fakeelapsems tcpwork sentcnt 2 #eq AckNum 48 #eq \test don't send more than one ACK tcpwork sentcnt 2 #eq \test send a "hey!" reply "hey!" con1 puts tcpwork sentcnt 3 #eq SeqNum 1 #eq tcp[] "hey!" #[]s= \test don't send it twice tcpwork sentcnt 3 #eq \test ... but after MAXOUTACKMS, yes send it again! MAXOUTACKMS 1+ fakeelapsems tcpwork sentcnt 4 #eq SeqNum 1 #eq tcp[] "hey!" #[]s= \test have the other side acknowledge it $1234 $2345 $33445566 inbound ACK Flags! 42 to SeqNum 5 to AckNum opts[ ]opts 0 0 tcp[]! com/tcp wrap ' handletcp expectevent sentcnt 4 #eq \test and don't ever send that "hey!" again MAXOUTACKMS 1+ fakeelapsems tcpwork sentcnt 4 #eq \test receive another payload which is then ACKed in a reply $1234 $2345 $33445566 inbound 0 Flags! 48 to SeqNum opts[ ]opts "again" c@+ tcp[]! com/tcp wrap ' handletcp expectevent -1 con1 readbuf "again" #[]s= "ho!" con1 puts tcpwork \ Send both an ACK and a payload sentcnt 5 #eq FlagsOnly ACK #eq SeqNum 5 #eq tcp[] "ho!" #[]s= \test receiving a FIN closes the connection $1234 $2345 $33445566 inbound FIN Flags! 53 to SeqNum 5 to AckNum opts[ ]opts 0 0 tcp[]! com/tcp wrap ' handletcp expectevent sentcnt 5 #eq \ nothing is sent right away tcpwork sentcnt 6 #eq FlagsOnly FIN ACK or #eq AckNum 54 #eq con1 status Closed #eq \test clean up after ourselves con1 close restoreticks \test writing to a closed connection is a noop "foo" c@+ con1 write 0 #eq sentcnt 6 #eq testend