socket.scm 9.3 KB

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