2015-03-05 16:41:06 +00:00
|
|
|
#lang racket/base
|
|
|
|
|
|
|
|
(require racket/match)
|
|
|
|
(require (prefix-in udp: racket/udp))
|
|
|
|
(require "../main.rkt")
|
|
|
|
(require "../demand-matcher.rkt")
|
|
|
|
|
|
|
|
(provide (struct-out udp-remote-address)
|
|
|
|
(struct-out udp-handle)
|
|
|
|
(struct-out udp-listener)
|
|
|
|
udp-address?
|
|
|
|
udp-local-address?
|
|
|
|
(struct-out udp-packet)
|
|
|
|
spawn-udp-driver)
|
|
|
|
|
|
|
|
;; A UdpAddress is one of
|
|
|
|
;; -- a (udp-address String Uint16), representing a remote socket
|
|
|
|
;; -- a (udp-handle Any), representing a local socket on a kernel-assigned port
|
|
|
|
;; -- a (udp-listener Uint16), representing a local socket on a user-assigned port
|
|
|
|
;; Note that udp-handle-ids must be chosen carefully: they are scoped
|
|
|
|
;; to the local VM, i.e. shared between processes in that VM, so
|
|
|
|
;; processes must make sure not to accidentally clash in handle ID
|
|
|
|
;; selection.
|
|
|
|
(struct udp-remote-address (host port) #:prefab)
|
|
|
|
(struct udp-handle (id) #:prefab)
|
|
|
|
(struct udp-listener (port) #:prefab)
|
|
|
|
|
|
|
|
(define (udp-address? x)
|
|
|
|
(or (udp-remote-address? x)
|
|
|
|
(udp-local-address? x)))
|
|
|
|
|
|
|
|
(define (udp-local-address? x)
|
|
|
|
(or (udp-handle? x)
|
|
|
|
(udp-listener? x)))
|
|
|
|
|
|
|
|
;; A UdpPacket is a (udp-packet UdpAddress UdpAddress Bytes), and
|
|
|
|
;; represents a packet appearing on our local "subnet" of the full UDP
|
|
|
|
;; network, complete with source, destination and contents.
|
|
|
|
(struct udp-packet (source destination body) #:prefab)
|
|
|
|
|
|
|
|
;; -> Action
|
|
|
|
;; Spawns a process acting as a UDP socket factory.
|
|
|
|
(define (spawn-udp-driver)
|
|
|
|
(spawn-demand-matcher (observe (udp-packet ? (?! (udp-listener ?)) ?))
|
|
|
|
(advertise (udp-packet ? (?! (udp-listener ?)) ?))
|
|
|
|
spawn-udp-socket))
|
|
|
|
|
|
|
|
;; UdpLocalAddress -> Action
|
|
|
|
(define (spawn-udp-socket local-addr)
|
|
|
|
(define socket (udp:udp-open-socket #f #f))
|
|
|
|
|
|
|
|
(match local-addr
|
|
|
|
[(udp-listener port) (udp:udp-bind! socket #f port)]
|
|
|
|
[(udp-handle _) (udp:udp-bind! socket #f 0)]) ;; kernel-allocated port number
|
|
|
|
|
|
|
|
(define control-ch (make-channel))
|
|
|
|
(thread (lambda () (udp-receiver-thread local-addr socket control-ch)))
|
|
|
|
|
|
|
|
(spawn (lambda (e s)
|
|
|
|
(match e
|
|
|
|
[(? patch? p)
|
|
|
|
(cond [(matcher-empty? (patch-removed p)) #f] ;; peer hasn't quit yet: do nothing.
|
|
|
|
[else (channel-put control-ch 'quit)
|
2015-03-06 11:21:50 +00:00
|
|
|
(quit)])]
|
2015-03-05 16:41:06 +00:00
|
|
|
[(message (at-meta (? udp-packet? p)))
|
|
|
|
(transition s (message p))]
|
|
|
|
[(message (udp-packet _ (udp-remote-address host port) body))
|
|
|
|
(udp:udp-send-to socket host port body)
|
|
|
|
#f]
|
|
|
|
[_ #f]))
|
|
|
|
(void)
|
2015-12-03 20:53:07 +00:00
|
|
|
(patch-seq (sub (udp-packet ? local-addr ?) #:meta-level 1)
|
|
|
|
(sub (udp-packet local-addr (udp-remote-address ? ?) ?))
|
|
|
|
(pub (udp-packet (udp-remote-address ? ?) local-addr ?))
|
|
|
|
(sub (observe (udp-packet (udp-remote-address ? ?) local-addr ?))))))
|
2015-03-05 16:41:06 +00:00
|
|
|
|
|
|
|
;; UdpLocalAddress UdpSocket Channel -> Void
|
|
|
|
(define (udp-receiver-thread local-addr socket control-ch)
|
|
|
|
(define buffer (make-bytes 65536))
|
|
|
|
(let loop ()
|
|
|
|
(sync (handle-evt control-ch (match-lambda ['quit (void)]))
|
|
|
|
(handle-evt (udp:udp-receive!-evt socket buffer)
|
|
|
|
(lambda (receive-results)
|
|
|
|
(match-define (list len source-hostname source-port) receive-results)
|
|
|
|
(send-ground-message
|
|
|
|
(udp-packet (udp-remote-address source-hostname source-port)
|
|
|
|
local-addr
|
|
|
|
(subbytes buffer 0 len)))
|
|
|
|
(loop)))))
|
|
|
|
(udp:udp-close socket))
|