Moontalk server and client (provided by many parties)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
6.5KB

  1. require unix/socket.fs
  2. require socket-extensions.4th
  3. require connections.4th
  4. require sendbuffer.4th
  5. AF_INET constant SERVER_SOCKET_DOMAIN
  6. SOCK_STREAM
  7. SOCK_NONBLOCK or
  8. constant SERVER_SOCKET_TYPE
  9. 0 constant SERVER_SOCKET_PROTOCOL
  10. 0 constant SERVER_ADDR
  11. 50000 constant SERVER_PORT
  12. 128 constant SERVER_LISTEN_BACKLOG
  13. \ Listening file descriptor.
  14. variable listenfd
  15. 0 listenfd !
  16. \ Idle detection.
  17. variable idle false idle !
  18. : server-idle? ( -- flag ) idle @ ;
  19. : server-idle! ( flag -- ) idle ! ;
  20. \ Temporary structs.
  21. create con /CONNECTION allot
  22. create saddr /sockaddr_in allot
  23. create optval /option_value allot
  24. : (assert-setsockopt) ( result-n -- )
  25. 0< abort" making socket reusable failed" ;
  26. : (optval-true!) ( -- )
  27. 1 optval l! ;
  28. : (make-reusable) ( socket-fd-n -- )
  29. (optval-true!)
  30. SOL_SOCKET SO_REUSEADDR optval /option_value setsockopt (assert-setsockopt) ;
  31. : (saddr!) ( protocol-n sin_addr-n port-n -- )
  32. htons saddr port w!
  33. saddr sin_addr l!
  34. saddr family w! ;
  35. : (assert-socket) ( result-n -- result-n )
  36. dup 0< abort" socket() failed." ;
  37. : (assert-bind) ( result-n -- )
  38. 0< abort" bind failed." ;
  39. : (assert-listen) ( result-n -- )
  40. 0< abort" listen failed." ;
  41. : (erase-saddr) ( -- )
  42. saddr /sockaddr_in erase ;
  43. : (create-socket) ( -- )
  44. SERVER_SOCKET_DOMAIN
  45. SERVER_SOCKET_TYPE
  46. SERVER_SOCKET_PROTOCOL
  47. socket (assert-socket) listenfd ! ;
  48. : (make-socket-reusable) ( -- )
  49. listenfd @ (make-reusable) ;
  50. : (set-saddr) ( -- )
  51. SERVER_SOCKET_DOMAIN SERVER_ADDR SERVER_PORT (saddr!) ;
  52. : (bind-socket) ( -- )
  53. listenfd @ saddr /sockaddr_in bind (assert-bind) ;
  54. : (listen-socket) ( -- )
  55. listenfd @ SERVER_LISTEN_BACKLOG listen() (assert-listen) ;
  56. : (server-info) ( -- )
  57. cr cr ." Server listening at port: " SERVER_PORT . cr ;
  58. : initialize-server ( -- )
  59. (erase-saddr)
  60. (create-socket)
  61. (make-socket-reusable)
  62. (set-saddr)
  63. (bind-socket)
  64. (listen-socket)
  65. (server-info) ;
  66. : (perform-disconnect) ( connection-addr -- )
  67. dup connection.connected false swap !
  68. connection.fd @ close() throw ;
  69. : (close-clients) ( -- )
  70. connections.count 0= IF
  71. EXIT
  72. THEN
  73. connections.count 0 DO
  74. I connections.at connection.connected @ true = IF
  75. I connections.at (perform-disconnect)
  76. THEN
  77. LOOP ;
  78. : (assert-close()) ( result-n -- )
  79. 0<> abort" close failed" ;
  80. : (close-server) ( -- )
  81. listenfd @ close() (assert-close()) ;
  82. : (close-server-info) ( -- )
  83. cr ." Closed server connections." cr ;
  84. : close-server ( -- )
  85. (close-clients) (close-server) (close-server-info) ;
  86. : (queue-disconnect) ( connection-addr -- )
  87. dup (perform-disconnect) EVENT_CONNECTION_CLOSED events.enqueue ;
  88. : (try-accept) ( -- c-result-n )
  89. listenfd @ 0 0 accept() ;
  90. : (accept-error) ( accept-result-n -- )
  91. errno EAGAIN <> abort" accept error" drop ;
  92. : (erase-connection) ( connection-addr -- )
  93. /CONNECTION erase ;
  94. : (set-connection-number) ( -- )
  95. connections.last dup connections.indexOf 1+ swap connection.number ! ;
  96. : (enqueue-new-connection) ( -- )
  97. connections.last EVENT_CONNECTION_NEW events.enqueue ;
  98. : (store-connection) ( connection-addr -- )
  99. dup >r connections.append IF
  100. (set-connection-number)
  101. (enqueue-new-connection)
  102. rdrop
  103. ELSE
  104. ." Warning: failed to store connection, disconnecting client." cr
  105. r> (perform-disconnect)
  106. THEN ;
  107. : (con!) ( fd-n connected-flag -- )
  108. con connection.connected !
  109. con connection.fd ! ;
  110. : (accept-connection) ( fd-n -- )
  111. con (erase-connection)
  112. true (con!)
  113. con (store-connection) ;
  114. : (server-idle-accept) ( -- )
  115. (try-accept) dup 0< IF
  116. (accept-error)
  117. ELSE
  118. (accept-connection)
  119. THEN ;
  120. : (connected?) ( connection-addr -- flag )
  121. connection.connected @ ;
  122. : (try-recv) ( connection-addr -- recv-result-n )
  123. dup connection.fd @
  124. swap connection.buffer
  125. CONNECTION_BUFFER_SIZE
  126. MSG_DONTWAIT
  127. recv ;
  128. : (recv-error?) ( c-result-n -- flag ) 0< ;
  129. : (recv) ( recv-result-n connection-addr -- )
  130. 2dup connection.bufferlen ! swap
  131. 0> IF
  132. EVENT_CONNECTION_RECV events.enqueue
  133. ELSE \ disconnected
  134. (queue-disconnect)
  135. THEN
  136. ;
  137. : (recv-warning) ( -- ) ." Warning: recv failed, disconnecting client." cr ;
  138. : (recv-error) ( recv-result-n connection-addr -- )
  139. errno EAGAIN <> IF
  140. (queue-disconnect) drop
  141. (recv-warning)
  142. ELSE
  143. 2drop
  144. THEN ;
  145. : (connection-recv) ( connection-addr -- )
  146. dup (try-recv) tuck (recv-error?) IF
  147. (recv-error)
  148. ELSE
  149. (recv)
  150. THEN ;
  151. : (server-idle-recv) ( -- )
  152. true server-idle!
  153. connections.count 0= IF
  154. EXIT
  155. THEN
  156. connections.count 0 DO
  157. I connections.at dup (connected?) IF
  158. (connection-recv)
  159. ELSE
  160. drop
  161. THEN
  162. LOOP ;
  163. : server-idle-accept ( eventdata-n eventid-n -- )
  164. 2drop (server-idle-accept) ;
  165. : server-idle-recv ( eventdata-n eventid-n -- )
  166. 2drop (server-idle-recv) ;
  167. : server-connection-new ( connection-addr eventid-n -- )
  168. 2drop ." New client connected!" cr ;
  169. : server-connection-closed ( connection-addr eventid-n -- )
  170. 2drop ." Client disconnected." cr ;
  171. : (to-string) ( n -- addr c ) s>d <# #s #> ;
  172. : (connectionnumber@) ( connection-addr -- c-addr u )
  173. connection.number @ (to-string) ;
  174. : (connectionbuffer@) ( connection-addr -- c-addr u )
  175. dup connection.buffer swap connection.bufferlen @ ;
  176. : (format-sendbuffer) ( from-connection-addr -- )
  177. >r sendbuffer-reset
  178. s" Anon " sendbuffer-append
  179. r@ (connectionnumber@) sendbuffer-append
  180. s" : " sendbuffer-append
  181. r> (connectionbuffer@) sendbuffer-append
  182. sendbuffer-sanitize ;
  183. : (connected?) ( connection-addr -- )
  184. connection.connected @ ;
  185. : (different-connection?) ( from-connection-addr to-connection-addr -- )
  186. <> ;
  187. : (send?) ( from-connection-addr to-connection-addr -- )
  188. tuck (different-connection?) swap (connected?) and ;
  189. : (check-send) ( result-n -- )
  190. 0< IF ." Warning: send failed." cr THEN ;
  191. : (send) ( from-connection-addr to-connection-addr -- )
  192. nip connection.fd @ sendbuffer@ 0 send (check-send) ;
  193. : (try-send) ( from-connection-addr to-connection-addr -- )
  194. 2dup (send?) IF
  195. (send)
  196. ELSE
  197. 2drop
  198. THEN ;
  199. : server-recv ( from-connection-addr eventid-n )
  200. drop dup (format-sendbuffer)
  201. connections.count 0 DO
  202. dup I connections.at (try-send)
  203. LOOP drop ;
  204. ' server-idle-accept EVENT_IDLE eventhandlers.append
  205. ' server-idle-recv EVENT_IDLE eventhandlers.append
  206. ' server-connection-new EVENT_CONNECTION_NEW eventhandlers.append
  207. ' server-connection-closed EVENT_CONNECTION_CLOSED eventhandlers.append
  208. ' server-recv EVENT_CONNECTION_RECV eventhandlers.append
  209. initialize-server