socket.scm 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. ; Copyright (c) 1993-2007 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Server interface
  3. ; (open-socket [socket-number]) -> socket
  4. ; (close-socket socket)
  5. ; (socket-accept socket) -> [input-port output-port]
  6. ; (get-host-name) -> string
  7. ; (socket-port-number socket) -> integer
  8. ; Client interface
  9. ; (socket-client host-name socket-number) -> [input-port output-port]
  10. ; Old calls I would like to get rid off.
  11. ; (socket-listen socket) -> [input-port output-port]
  12. ; (socket-listen-channels socket) -> [input-channel output-channel]
  13. ; (socket-client-channels host-name socket-number) -> [input-channels output-channels]
  14. ;--------------------
  15. ; Socket type
  16. ;
  17. ; A socket has a channel (for accepting connections) and a port number.
  18. ; These are only used for servers and udp sockets; clients don't need them.
  19. (define-record-type socket :socket
  20. (really-make-socket type channel port-number condvar)
  21. socket?
  22. (type socket-type) ; SOCKET, UPD-INPUT-SOCKET, UDP-OUTPUT-SOCKET
  23. (channel socket-channel)
  24. (port-number socket-port-number)
  25. (condvar socket-condvar)) ; for blocking until a connection arrives
  26. (define (make-socket type channel)
  27. (really-make-socket type
  28. channel
  29. (socket-number channel)
  30. (make-condvar)))
  31. (define-record-discloser :socket
  32. (lambda (s)
  33. `(,(socket-type s) ,(socket-port-number s))))
  34. ; Close the channel, notifying any waiters that this has happened.
  35. (define (close-socket socket)
  36. (let ((channel (socket-channel socket))
  37. (close-channel (case (socket-type socket)
  38. ((socket) close-channel)
  39. ((udp-input-socket) close-socket-input-channel)
  40. (else close-socket-output-channel))))
  41. (with-new-proposal (lose)
  42. (or (channel-maybe-commit-and-close channel close-channel)
  43. (lose)))))
  44. ; Makes a server socket.
  45. (define (open-socket . maybe-number)
  46. (let ((channel (new-socket #f #t)))
  47. (bind-socket channel (if (or (null? maybe-number)
  48. (= (car maybe-number) 0)) ; old, crappy spec
  49. #f
  50. (car maybe-number)))
  51. (real-socket-listen channel)
  52. (make-socket 'socket channel)))
  53. (define (socket-accept socket)
  54. (call-with-values
  55. (lambda ()
  56. (socket-listen-channels socket))
  57. (lambda (in out)
  58. (values (input-channel+closer->port in close-socket-input-channel)
  59. (output-channel+closer->port out close-socket-output-channel)))))
  60. (define socket-listen socket-accept)
  61. (define (socket-listen-channels socket)
  62. (let ((input-channel (blocking-socket-op socket real-socket-accept)))
  63. (values input-channel
  64. (dup-socket-channel input-channel))))
  65. ; Keep performing OP until it returns a non-#F value. In between attempts we
  66. ; block on the socket's channel.
  67. (define (blocking-socket-op socket op)
  68. (let ((channel (socket-channel socket))
  69. (condvar (socket-condvar socket)))
  70. (let loop ((retry? #f))
  71. (disable-interrupts!)
  72. (cond ((op channel retry?)
  73. => (lambda (result)
  74. (enable-interrupts!)
  75. result))
  76. (else
  77. (wait-for-channel channel condvar)
  78. (with-new-proposal (lose)
  79. (maybe-commit-and-wait-for-condvar condvar))
  80. (enable-interrupts!)
  81. (loop #t))))))
  82. ; Connect to the socket and return input and output ports.
  83. (define (socket-client host-name port-number)
  84. (call-with-values
  85. (lambda ()
  86. (socket-client-channels host-name port-number))
  87. (lambda (in out)
  88. (values (input-channel+closer->port in close-socket-input-channel)
  89. (output-channel+closer->port out close-socket-output-channel)))))
  90. ; FreeBSD's connect() behaves oddly. If you get told to wait, wait for select()
  91. ; to signal the all-clear, and then try to connect again, you get an `already
  92. ; connected' error. To handle this we pass in a RETRY? flag. If RETRY? is
  93. ; true the `already connected' error is ignored.
  94. (define (socket-client-channels host-name port-number)
  95. (let ((channel (new-socket #f #f)))
  96. (let loop ((retry? #f))
  97. (disable-interrupts!)
  98. (let ((output-channel (real-socket-connect channel
  99. (host-name->byte-vector host-name)
  100. port-number
  101. retry?)))
  102. (cond ((channel? output-channel)
  103. (enable-interrupts!)
  104. (values channel output-channel))
  105. ((eq? output-channel #t)
  106. (error "client socket already connected" host-name port-number))
  107. (else
  108. (let ((condvar (make-condvar)))
  109. (wait-for-channel channel condvar)
  110. (with-new-proposal (lose)
  111. (maybe-commit-and-wait-for-condvar condvar))
  112. (enable-interrupts!)
  113. (loop #t))))))))
  114. ;; #### This needs to be IDNA
  115. (define (host-name->byte-vector host)
  116. (let* ((size (string-length host))
  117. (b (make-byte-vector (+ size 1) 0)))
  118. (do ((i 0 (+ 1 i)))
  119. ((= i size))
  120. (let ((code (char->integer (string-ref host i))))
  121. (if (< code 128)
  122. (byte-vector-set! b i code)
  123. (byte-vector-set! b i #x3f)))) ; ?
  124. b))
  125. ;----------------
  126. ; UDP stuff
  127. ;
  128. ; For UDP messages we need to specify the destination address and receive the
  129. ; sender's address.
  130. (define-record-type udp-address :udp-address
  131. (udp-addresses-are-made-from-c-code)
  132. udp-address?
  133. (address udp-address-address) ; C value
  134. (port udp-address-port) ; port number
  135. (hostname udp-address-hostname)) ; string
  136. (define-record-discloser :udp-address
  137. (lambda (s)
  138. `(udp-address ,(udp-address-hostname s) ,(udp-address-port s))))
  139. ; Export the binding to C for type-checking and making udp-addresses.
  140. (define-exported-binding "s48-udp-address-type" :udp-address)
  141. ; Open a UDP socket, returning the two sides. If a socket port is specified
  142. ; it is given to the input half.
  143. (define (open-udp-socket . maybe-port)
  144. (let* ((input-channel (new-socket #t #t))
  145. (output-channel (dup-socket-channel input-channel)))
  146. (bind-socket input-channel
  147. (if (null? maybe-port)
  148. #f
  149. (car maybe-port)))
  150. (values (make-socket 'udp-input-socket input-channel)
  151. (make-socket 'udp-output-socket output-channel))))
  152. ; Sending and receiving using UPD sockets.
  153. (define (udp-send socket address buffer count)
  154. (if (not (and (socket? socket)
  155. (eq? (socket-type socket)
  156. 'udp-output-socket)))
  157. (call-error "not a UDP output socket" udp-send socket address buffer count))
  158. (blocking-socket-op socket
  159. (lambda (channel retry?)
  160. (real-udp-send channel address buffer count))))
  161. (define (udp-receive socket buffer)
  162. (if (not (and (socket? socket)
  163. (eq? (socket-type socket)
  164. 'udp-input-socket)))
  165. (call-error "not a UDP input socket" udp-receive socket buffer))
  166. (let ((got (blocking-socket-op socket
  167. (lambda (channel retry?)
  168. (real-udp-receive channel buffer)))))
  169. (values (car got) (cdr got))))
  170. (define (lookup-udp-address name port)
  171. (real-lookup-udp-address (host-name->byte-vector name)
  172. port))
  173. ;----------------
  174. ; We need to explicitly close socket channels.
  175. (define (close-socket-input-channel channel)
  176. (close-socket-half channel #t)
  177. (close-channel channel))
  178. (define (close-socket-output-channel channel)
  179. (close-socket-half channel #f)
  180. (close-channel channel))
  181. ;----------------
  182. ; The C calls we use. These are in c/unix/socket.c.
  183. (import-lambda-definition new-socket (upd? input?) "s48_socket")
  184. (import-lambda-definition bind-socket (socket number) "s48_bind")
  185. (import-lambda-definition socket-number (socket) "s48_socket_number")
  186. (import-lambda-definition real-socket-listen (socket) "s48_listen")
  187. (import-lambda-definition real-socket-accept (socket retry?) "s48_accept")
  188. (import-lambda-definition real-socket-connect (socket
  189. machine
  190. port-number
  191. retry?)
  192. "s48_connect")
  193. (import-lambda-definition dup-socket-channel (socket)
  194. "s48_dup_socket_channel")
  195. (import-lambda-definition close-socket-half (socket input?)
  196. "s48_close_socket_half")
  197. (import-lambda-definition get-host-name () "s48_get_host_name")
  198. ; UDP calls
  199. (import-lambda-definition real-udp-send (socket address buffer count)
  200. "s48_udp_send")
  201. (import-lambda-definition real-udp-receive (socket buffer)
  202. "s48_udp_receive")
  203. (import-lambda-definition real-lookup-udp-address (name port)
  204. "s48_lookup_udp_address")