IN: udp USING: kernel alien io-internals sequences strings generic namespaces threads io-internals arrays errors ; USE: unix-internals LIBRARY: libc FUNCTION: ssize_t recv ( int s, void* buf, size_t nbytes, int flags ) ; FUNCTION: ssize_t recvfrom ( int s, void* buf, size_t nbytes, int flags, sockaddr-in* from, socklen_t* fromlen ) ; FUNCTION: ssize_t sendto ( int s, void* buf, size_t len, int flags, sockaddr-in* to, socklen_t tolen ) ; : SOCK_DGRAM 2 ; : udp-socket-fd ( -- socket ) PF_INET SOCK_DGRAM 0 socket dup io-error dup init-handle ; : do-recv ( socket -- data ) 1500 "char" dup >r 1500 0 recv r> swap >string [ swap char-nth ] map-with ; : do-recvfrom ( socket -- data addr port ) "sockaddr-in" dup >r 1500 "char" dup >r rot r> 1500 0 r> "sockaddr-in" c-size recvfrom dup -1 = [ drop 2drop f ] [ >string [ swap char-nth ] map-with swap dup sockaddr-in-addr inet-ntoa swap sockaddr-in-port ntohs ] if ; : do-sendto ( socket data addr port -- ? ) client-sockaddr ! socket data sockaddr >r dup length swap string>char-alien swap 0 r> "sockaddr-in" c-size sendto -1 = not ; ! ---------------------------------------------------------------- TUPLE: datagram packet addr port ; : datagram-socket ( port -- fd ) server-sockaddr udp-socket-fd dup SOL_SOCKET SO_REUSEADDR sockopt swap dupd "sockaddr-in" c-size bind drop ; C: datagram ( port -- datagram ) [ >r datagram-socket f r> set-delegate ] keep datagram over set-port-type ; ! ---------------------------------------------------------------- TUPLE: recv-task ; C: recv-task ( port -- task ) [ >r r> set-delegate ] keep ; M: recv-task do-io-task io-task-port dup port-handle do-recv swap set-datagram-packet t ; M: recv-task task-container drop read-tasks get ; : wait-receive ( stream -- ) [ swap add-io-task stop ] callcc0 drop ; : receive ( stream -- packet ) dup wait-receive dup pending-error datagram-packet ; ! ---------------------------------------------------------------- TUPLE: recvfrom-task ; C: recvfrom-task ( port -- task ) [ >r r> set-delegate ] keep ; M: recvfrom-task do-io-task io-task-port dup >r port-handle do-recvfrom r> swap over set-datagram-port swap over set-datagram-addr set-datagram-packet t ; M: recvfrom-task task-container drop read-tasks get ; : wait-receive-from ( stream -- ) [ swap add-io-task stop ] callcc0 drop ; : receive-from ( stream -- packet addr port ) dup wait-receive-from dup pending-error dup datagram-packet over datagram-addr rot datagram-port ; ! ---------------------------------------------------------------- TUPLE: sendto-task packet addr dport ; C: sendto-task ( port -- task ) [ >r r> set-delegate ] keep ; M: sendto-task do-io-task dup dup io-task-port port-handle over sendto-task-packet rot dup sendto-task-addr swap sendto-task-dport [ do-sendto drop ] [ >r 2drop 2drop io-task-port r> swap set-port-error ] recover t ; M: sendto-task task-container drop write-tasks get ; : wait-send-to ( stream packet addr port -- ) [ >r >r rot r> over set-sendto-task-dport swap over set-sendto-task-addr swap over set-sendto-task-packet r> swap add-io-task stop ] callcc0 2drop 2drop ; : send-to ( stream packet addr port -- ) 2swap over >r 2swap wait-send-to r> pending-error ; ! ---------------------------------------------------------------- : >2array< ( a -- x y ) dup first swap second ; : (udp-server) ( quot daragram -- ) [ tuck receive-from 2array rot call >2array< send-to ] 2keep (udp-server) ; : udp-server ( port quot -- ) swap (udp-server) ; : (test-udp-server) ( -- ) [ 9999 [ swap "-ok" append swap ] udp-server ] in-thread 9998 [ "ay" "localhost" 9999 send-to ] keep [ receive-from ] keep [ "bi" "localhost" 9999 send-to ] keep [ receive-from ] keep drop ; USE: test : test-udp-server ( -- ) { t } [ 0 "world" over set-sendto-task-dport "localhost" over set-sendto-task-addr "packet" over set-sendto-task-packet do-io-task ] unit-test { "ay-ok" "127.0.0.1" 9999 "bi-ok" "127.0.0.1" 9999 } [ (test-udp-server) ] unit-test { { kernel-error 3 0 "world" } } [ [ 0 "hello" "localhost" "world" send-to ] catch ] unit-test ;