tserver.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
(HTM) git clone git://bitreich.org/clic/ git://hg6vgqziawt5s4dj.onion/clic/
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Tags
(DIR) LICENSE
---
tserver.lisp (4874B)
---
1 ;;;; See LICENSE for licensing information.
2
3 (in-package :usocket)
4
5 (defvar *server*)
6
7 (defun socket-server (host port function &optional arguments
8 &key in-new-thread (protocol :stream)
9 ;; for udp
10 (timeout 1) (max-buffer-size +max-datagram-packet-size+)
11 ;; for tcp
12 element-type (reuse-address t) multi-threading
13 name)
14 (let* ((real-host (or host *wildcard-host*))
15 (socket (ecase protocol
16 (:stream
17 (apply #'socket-listen
18 `(,real-host ,port
19 ,@(when element-type `(:element-type ,element-type))
20 ,@(when reuse-address `(:reuse-address ,reuse-address)))))
21 (:datagram
22 (socket-connect nil nil :protocol :datagram
23 :local-host real-host
24 :local-port port)))))
25 (labels ((real-call ()
26 (ecase protocol
27 (:stream
28 (tcp-event-loop socket function arguments
29 :element-type element-type
30 :multi-threading multi-threading))
31 (:datagram
32 (udp-event-loop socket function arguments
33 :timeout timeout
34 :max-buffer-size max-buffer-size)))))
35 (if in-new-thread
36 (values (portable-threads:spawn-thread (or name "USOCKET Server") #'real-call) socket)
37 (progn
38 (setq *server* socket)
39 (real-call))))))
40
41 (defvar *remote-host*)
42 (defvar *remote-port*)
43
44 (defun default-udp-handler (buffer) ; echo
45 (declare (type (simple-array (unsigned-byte 8) *) buffer))
46 buffer)
47
48 (defun udp-event-loop (socket function &optional arguments
49 &key timeout max-buffer-size)
50 (let ((buffer (make-array max-buffer-size :element-type '(unsigned-byte 8) :initial-element 0))
51 (sockets (list socket)))
52 (unwind-protect
53 (loop do
54 (multiple-value-bind (return-sockets real-time)
55 (wait-for-input sockets :timeout timeout)
56 (declare (ignore return-sockets))
57 (when real-time
58 (multiple-value-bind (recv n *remote-host* *remote-port*)
59 (socket-receive socket buffer max-buffer-size)
60 (declare (ignore recv))
61 (if (plusp n)
62 (progn
63 (let ((reply
64 (apply function (subseq buffer 0 n) arguments)))
65 (when reply
66 (replace buffer reply)
67 (let ((n (socket-send socket buffer (length reply)
68 :host *remote-host*
69 :port *remote-port*)))
70 (when (minusp n)
71 (error "send error: ~A~%" n))))))
72 (error "receive error: ~A" n))))
73 #+scl (when thread:*quitting-lisp* (return))
74 #+(and cmu mp) (mp:process-yield)))
75 (socket-close socket)
76 (values))))
77
78 (defun default-tcp-handler (stream) ; null
79 (declare (type stream stream))
80 (format stream "Hello world!~%"))
81
82 (defun echo-tcp-handler (stream)
83 (loop
84 (when (listen stream)
85 (let ((line (read-line stream nil)))
86 (write-line line stream)
87 (force-output stream)))))
88
89 (defun tcp-event-loop (socket function &optional arguments
90 &key element-type multi-threading)
91 (let ((real-function #'(lambda (client-socket &rest arguments)
92 (unwind-protect
93 (multiple-value-bind (*remote-host* *remote-port*) (get-peer-name client-socket)
94 (apply function (socket-stream client-socket) arguments))
95 (close (socket-stream client-socket))
96 (socket-close client-socket)
97 nil))))
98 (unwind-protect
99 (loop do
100 (let* ((client-socket (apply #'socket-accept
101 `(,socket ,@(when element-type `(:element-type ,element-type)))))
102 (client-stream (socket-stream client-socket)))
103 (if multi-threading
104 (apply #'portable-threads:spawn-thread "USOCKET Client" real-function client-socket arguments)
105 (prog1 (apply real-function client-socket arguments)
106 (close client-stream)
107 (socket-close client-socket)))
108 #+scl (when thread:*quitting-lisp* (return))
109 #+(and cmu mp) (mp:process-yield)))
110 (socket-close socket)
111 (values))))