Moontalk server and client (provided by many parties)
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

297 wiersze
8.5KB

  1. require unix/socket.fs
  2. require socket-extensions.4th
  3. require connections.4th
  4. require commandline.4th
  5. require motd.4th
  6. require sendbuffer.4th
  7. AF_INET constant SERVER_SOCKET_DOMAIN
  8. SOCK_STREAM
  9. SOCK_NONBLOCK or
  10. constant SERVER_SOCKET_TYPE
  11. 0 constant SERVER_SOCKET_PROTOCOL
  12. 0 constant SERVER_ADDR
  13. 50000 constant SERVER_PORT
  14. 128 constant SERVER_LISTEN_BACKLOG
  15. \ Listening file descriptor.
  16. variable listenfd
  17. 0 listenfd !
  18. \ Idle detection.
  19. variable idle false idle !
  20. : server-idle? ( -- flag ) idle @ ;
  21. : server-idle! ( flag -- ) idle ! ;
  22. \ Temporary structs.
  23. create con /CONNECTION allot
  24. create saddr /sockaddr_in allot
  25. create optval /option_value allot
  26. : (assert-setsockopt) ( result-n -- )
  27. 0< abort" making socket reusable failed" ;
  28. : (optval-true!) ( -- )
  29. 1 optval l! ;
  30. : (make-reusable) ( socket-fd-n -- )
  31. (optval-true!)
  32. SOL_SOCKET SO_REUSEADDR optval /option_value setsockopt (assert-setsockopt) ;
  33. : (saddr!) ( protocol-n sin_addr-n port-n -- )
  34. htons saddr port w!
  35. saddr sin_addr l!
  36. saddr family w! ;
  37. : (assert-socket) ( result-n -- result-n )
  38. dup 0< abort" socket() failed." ;
  39. : (assert-bind) ( result-n -- )
  40. 0< abort" bind failed." ;
  41. : (assert-listen) ( result-n -- )
  42. 0< abort" listen failed." ;
  43. : (erase-saddr) ( -- )
  44. saddr /sockaddr_in erase ;
  45. : (create-socket) ( -- )
  46. SERVER_SOCKET_DOMAIN
  47. SERVER_SOCKET_TYPE
  48. SERVER_SOCKET_PROTOCOL
  49. socket (assert-socket) listenfd ! ;
  50. : (make-socket-reusable) ( -- )
  51. listenfd @ (make-reusable) ;
  52. : (set-saddr) ( -- )
  53. SERVER_SOCKET_DOMAIN SERVER_ADDR SERVER_PORT (saddr!) ;
  54. : (bind-socket) ( -- )
  55. listenfd @ saddr /sockaddr_in bind (assert-bind) ;
  56. : (listen-socket) ( -- )
  57. listenfd @ SERVER_LISTEN_BACKLOG listen() (assert-listen) ;
  58. : (server-info) ( -- )
  59. cr cr ." Server listening at port: " SERVER_PORT . cr ;
  60. : initialize-server ( -- )
  61. (erase-saddr)
  62. (create-socket)
  63. (make-socket-reusable)
  64. (set-saddr)
  65. (bind-socket)
  66. (listen-socket)
  67. (server-info) ;
  68. : (perform-disconnect) ( connection-addr -- )
  69. dup connection.connected false swap !
  70. connection.fd @ close() throw ;
  71. : (close-clients) ( -- )
  72. connections.count 0= IF
  73. EXIT
  74. THEN
  75. connections.count 0 DO
  76. I connections.at connection.connected @ true = IF
  77. I connections.at (perform-disconnect)
  78. THEN
  79. LOOP ;
  80. : (assert-close()) ( result-n -- )
  81. 0<> abort" close failed" ;
  82. : (close-server) ( -- )
  83. listenfd @ close() (assert-close()) ;
  84. : (close-server-info) ( -- )
  85. cr ." Closed server connections." cr ;
  86. : close-server ( -- )
  87. (close-clients) (close-server) (close-server-info) ;
  88. : (queue-disconnect) ( connection-addr -- )
  89. dup (perform-disconnect) EVENT_CONNECTION_CLOSED events.enqueue ;
  90. : (try-accept) ( -- c-result-n )
  91. listenfd @ 0 0 accept() ;
  92. : (accept-error) ( accept-result-n -- )
  93. errno EAGAIN <> abort" accept error" drop ;
  94. : (erase-connection) ( connection-addr -- )
  95. /CONNECTION erase ;
  96. : (set-connection-number) ( -- )
  97. connections.last dup connections.indexOf 1+ swap connection.number ! ;
  98. : (enqueue-new-connection) ( -- )
  99. connections.last EVENT_CONNECTION_NEW events.enqueue ;
  100. : (store-connection) ( connection-addr -- )
  101. dup >r connections.append IF
  102. (set-connection-number)
  103. (enqueue-new-connection)
  104. rdrop
  105. ELSE
  106. ." Warning: failed to store connection, disconnecting client." cr
  107. r> (perform-disconnect)
  108. THEN ;
  109. : (con!) ( fd-n connected-flag -- )
  110. con connection.connected !
  111. con connection.fd ! ;
  112. : (accept-connection) ( fd-n -- )
  113. con (erase-connection)
  114. true (con!)
  115. con (store-connection) ;
  116. : (server-idle-accept) ( -- )
  117. (try-accept) dup 0< IF
  118. (accept-error)
  119. ELSE
  120. (accept-connection)
  121. THEN ;
  122. : (connected?) ( connection-addr -- flag )
  123. connection.connected @ ;
  124. : (try-recv) ( connection-addr -- recv-result-n )
  125. dup connection.fd @
  126. swap connection.buffer
  127. CONNECTION_BUFFER_SIZE
  128. MSG_DONTWAIT
  129. recv ;
  130. : (recv-error?) ( c-result-n -- flag ) 0< ;
  131. : (recv) ( recv-result-n connection-addr -- )
  132. 2dup connection.bufferlen ! swap
  133. 0> IF
  134. EVENT_CONNECTION_RECV events.enqueue
  135. ELSE \ disconnected
  136. (queue-disconnect)
  137. THEN ;
  138. : (recv-warning) ( -- ) ." Warning: recv failed, disconnecting client." cr ;
  139. : (recv-error) ( recv-result-n connection-addr -- )
  140. errno EAGAIN <> IF
  141. (queue-disconnect) drop
  142. (recv-warning)
  143. ELSE
  144. 2drop
  145. THEN ;
  146. : (connection-recv) ( connection-addr -- )
  147. dup (try-recv) tuck (recv-error?) IF
  148. (recv-error)
  149. ELSE
  150. (recv)
  151. THEN ;
  152. : (server-idle-recv) ( -- )
  153. true server-idle!
  154. connections.count 0= IF
  155. EXIT
  156. THEN
  157. connections.count 0 DO
  158. I connections.at dup (connected?) IF
  159. (connection-recv)
  160. ELSE
  161. drop
  162. THEN
  163. LOOP ;
  164. : (to-string) ( n -- addr c ) s>d <# #s #> ;
  165. : (connection.number>string) ( connection-addr -- c-addr u )
  166. connection.number @ (to-string) ;
  167. : (connection.buffer>string) ( connection-addr -- c-addr u )
  168. dup connection.buffer swap connection.bufferlen @ ;
  169. : (format-sendbuffer) ( from-connection-addr -- )
  170. >r sendbuffer-reset
  171. s" Anon " sendbuffer-append
  172. r@ (connection.number>string) sendbuffer-append
  173. s" : " sendbuffer-append
  174. r> (connection.buffer>string) sendbuffer-append
  175. sendbuffer-sanitize ;
  176. : (connected?) ( connection-addr -- )
  177. connection.connected @ ;
  178. : (different-connection?) ( from-connection-addr to-connection-addr -- )
  179. <> ;
  180. : (send?) ( from-connection-addr to-connection-addr -- )
  181. tuck (different-connection?) swap (connected?) and ;
  182. : (check-send) ( result-n -- )
  183. 0< IF ." Warning: send failed." cr THEN ;
  184. : (send-sendbuffer) ( to-connection-addr -- )
  185. connection.fd @ sendbuffer@ 0 send (check-send) ;
  186. : (send) ( from-connection-addr to-connection-addr -- )
  187. (send-sendbuffer) ;
  188. : (try-send) ( from-connection-addr to-connection-addr -- )
  189. 2dup (send?) IF
  190. nip (send-sendbuffer)
  191. ELSE
  192. 2drop
  193. THEN ;
  194. : server-recv ( from-connection-addr eventid-n )
  195. drop dup (format-sendbuffer)
  196. connections.count 0 DO
  197. dup I connections.at (try-send)
  198. LOOP drop ;
  199. : server-idle-accept ( eventdata-n eventid-n -- )
  200. 2drop (server-idle-accept) ;
  201. : server-idle-recv ( eventdata-n eventid-n -- )
  202. 2drop (server-idle-recv) ;
  203. variable (strstart)
  204. variable (strend)
  205. : (>str) ( startindex-n endindex-n str-addr -- c-addr u )
  206. tuck + -rot + tuck - ;
  207. : (newline?) ( char -- flag ) 10 = ;
  208. \ TODO: FIXME: refactor and create words to be able to conveniently
  209. \ TODO: FIXME: send "Server: ..." messages. This will be useful in the repl too.
  210. : (prepare-motd) ( -- )
  211. \ TODO: FIXME: just write a proper parser at this point....
  212. sendbuffer-reset
  213. -1 (strstart) !
  214. -1 (strend) !
  215. motd@ 0 DO
  216. (strstart) @ -1 = IF
  217. I (strstart) !
  218. THEN
  219. dup I + c@ (newline?) IF
  220. I (strend) !
  221. THEN
  222. (strend) @ -1 <> IF
  223. s" Server: " sendbuffer-append
  224. dup (strstart) @ (strend) @ rot (>str) sendbuffer-append
  225. s\" \n" sendbuffer-append
  226. -1 (strstart) !
  227. -1 (strend) !
  228. THEN
  229. LOOP drop ;
  230. : (prepare-empty-line) ( -- )
  231. sendbuffer-reset s\" Server: \n" sendbuffer-append ;
  232. : (prepare-identity) ( connection-addr -- )
  233. sendbuffer-reset
  234. s\" Server: You are now known as \"Anon " sendbuffer-append
  235. (connection.number>string) sendbuffer-append
  236. s\" \".\n" sendbuffer-append ;
  237. : server-connection-new ( connection-addr eventid-n -- )
  238. drop ." New client connected!" cr
  239. dup (prepare-motd) (send-sendbuffer)
  240. dup (prepare-empty-line) (send-sendbuffer)
  241. dup (prepare-identity)
  242. (send-sendbuffer) ;
  243. : server-connection-closed ( connection-addr eventid-n -- )
  244. 2drop ." Client disconnected." cr ;
  245. : server-commandline ( eventdata-n eventid-n -- )
  246. 2drop commandline-ready? IF
  247. space commandline-getline ['] evaluate catch dup 0= IF
  248. drop ." ok"
  249. ELSE
  250. ." error code: " . 2drop
  251. THEN
  252. cr
  253. commandline-reset
  254. ELSE
  255. commandline-key? IF
  256. commandline-key commandline-handlekey
  257. commandline-redraw
  258. THEN
  259. THEN ;
  260. : server-motd-changed ( eventdata-n eventid-n -- )
  261. 2drop (prepare-motd)
  262. connections.count 0 DO
  263. I connections.at (send-sendbuffer)
  264. LOOP ;
  265. ' server-idle-accept EVENT_IDLE eventhandlers.append
  266. ' server-idle-recv EVENT_IDLE eventhandlers.append
  267. ' server-connection-new EVENT_CONNECTION_NEW eventhandlers.append
  268. ' server-connection-closed EVENT_CONNECTION_CLOSED eventhandlers.append
  269. ' server-recv EVENT_CONNECTION_RECV eventhandlers.append
  270. ' server-commandline EVENT_COMMANDLINE eventhandlers.append
  271. ' server-motd-changed EVENT_MOTD_CHANGED eventhandlers.append
  272. initialize-server