1 (in-package :iolib.examples
)
3 ;;;; This file was originally written by Peter Keller (psilord@cs.wisc.edu)
4 ;;;; and this code is released under the same license as IOLib.
6 ;;;; This buffer generator creates a closure which holds an array that can
7 ;;;; hold up to a maximum number of bytes read from the socket. The bytes are
8 ;;;; not inspected and simply stored in an array. If there are bytes to write
9 ;;;; out from the array the write handler is registered and they are written.
10 ;;;; In this manner does the written data "follow" the read data. When the
11 ;;;; end of the buffer is reach via a write, we start over at the beginning
14 ;;;; This io-buffer reads and writes in a nonblocking fashion on the
15 ;;;; socket and stores partial reads and writes in an internal buffer.
17 ;; The event dispatcher
18 (defvar *ex8-event-base
*)
21 (defun make-ex8-io-buffer (socket who port disconnector
&key
(max-bytes 16384))
22 (let ((echo-buf (make-array max-bytes
:element-type
'unsigned-byte
))
25 (read-handler-registered nil
)
26 (write-handler-registered nil
)
31 ;; This is the function responsible for reading bytes from the client.
32 ((read-some-bytes (fd event exception
)
35 ;; Read however much we are able.
36 (multiple-value-bind (buf bytes-read
)
42 ;; Unlike read-ing from a stream, receive-from
43 ;; returns zero on an end-of-file read, so we turn
44 ;; around and signal that condition so our
45 ;; handler-case can deal with it properly like our
47 (when (zerop bytes-read
)
50 (format t
"Read ~A bytes from ~A:~A~%" bytes-read who port
)
51 (incf read-index bytes-read
))
53 ;; Register the write handler if there is data to
56 ;; Then, try to write some data to the socket right
57 ;; away even though it might not be ready simply to
58 ;; avoid another go around. The write-some-bytes
59 ;; function must be able to catch econnreset because
60 ;; this connection may be closed at the time of this
61 ;; call. Normally, if the multiplexer has told me I
62 ;; could write then it'd be ok, but since this write
63 ;; is outside of the multiplexer and an optimization,
65 (when (/= write-index read-index
)
66 (unless write-handler-registered
67 (set-io-handler *ex8-event-base
*
71 (setf write-handler-registered t
))
73 ;; See if I can write it right away!
74 (write-some-bytes fd
:write nil
))
76 ;; If I'm out of room to store more data then remove
77 ;; myself from the io handler. When the write handler
78 ;; notices that it has finished writing everything,
79 ;; all indicies get set back to zero and the write
80 ;; handler removes itself. If write-some-bytes in
81 ;; the call above worked, then read-index might not
82 ;; equal max-bytes when this line of code gets
84 (when (= read-index max-bytes
)
85 (funcall disconnector who port
:read
)
86 (setf read-handler-registered nil
)))
88 (socket-connection-reset-error ()
89 ;; Handle the client sending a reset.
90 (format t
"Client ~A:~A: connection reset by peer.~%" who port
)
91 (funcall disconnector who port
:close
))
94 ;; When we get an end of file, that doesn't necessarily
95 ;; mean the client went away, it could just mean that
96 ;; the client performed a shutdown on the write end of
97 ;; its socket and it is expecting the data stored in
98 ;; the server to be written to it. However, if there
99 ;; is nothing left to write and our read end is closed,
100 ;; we shall consider it that the client went away and
101 ;; close the connection.
102 (format t
"Client ~A:~A produced end-of-file on a read.~%"
104 (if (= read-index write-index
)
105 (funcall disconnector who port
:close
)
107 (funcall disconnector who port
:read
)
108 (setf read-handler-registered nil
)
109 (setf eof-seen t
))))))
113 ;; This is the function responsible for writing bytes to the client.
114 (write-some-bytes (fd event exception
)
117 ;; If there is data to be written, write it. NOTE:
118 ;; There is often no indication of failure to write
119 ;; with send-to. If I'm writing to a closed (by the
120 ;; client) socket, it could be that send-to tells me
121 ;; nothing is wrong and returns the number of bytes
122 ;; wrotten. In this case, nothing was written but we
123 ;; have no way of knowing. Usually in this case, the
124 ;; read handler will get a 0 bytes read on the socket
125 ;; and we can know the connection is broken.
126 (when (> read-index write-index
)
127 (let ((wrote-bytes (send-to socket echo-buf
130 (format t
"Wrote ~A bytes to ~A:~A~%" wrote-bytes who port
)
131 (incf write-index wrote-bytes
)))
133 ;; If we see we're out of data to write and we saw an eof,
134 ;; then close the connection, we're done. If we didn't see an
135 ;; eof, then unregister the write handler and reregister the
136 ;; read handler to get more data. If the buffer indices
137 ;; are at the very end, reset them to the beginning.
138 (when (= read-index write-index
)
140 (funcall disconnector who port
:close
)
143 ;; nothing more to write, so unregister writer
144 (funcall disconnector who port
:write
)
145 (setf write-handler-registered nil
)
147 ;; If we're at the end of the buffer, move to the
148 ;; beginning so there is more room for data.
149 (when (= read-index write-index max-bytes
)
153 ;; Reregister the read handler to get more data
154 (unless read-handler-registered
155 (set-io-handler *ex8-event-base
*
156 (socket-os-fd socket
)
159 (setf read-handler-registered t
))))))
161 (socket-connection-reset-error ()
162 ;; If for somer eaon the client reset the network connection,
163 ;; we'll get this signal.
164 (format t
"Client ~A:~A: connection reset by peer.~%" who port
)
165 (funcall disconnector who port
:close
))
168 ;; Sometimes this happens on a write even though it
169 ;; might have been marked as ready. Also we might have
170 ;; asked to write on an unknown status socket. Ignore
171 ;; it and we will try again later.
172 (format t
"write-some-bytes: ewouldblock~%")
176 ;; In this server, if the client doesn't accept data,
177 ;; it also means it will never send us data again. So
178 ;; close the connection for good.
179 (format t
"Client ~A:~A got hangup on write.~%" who port
)
180 (funcall disconnector who port
:close
)))))
184 ;; This is the function returned from make-ex8-io-buffer which
185 ;; allows us access to the read/writer in the scope of the
186 ;; closure. We will ask for the correct functions when setting
187 ;; up the io handlers. NOTE: By simply asking for the handler,
188 ;; I've assumed it is to be immediately put into an iolib event
189 ;; handler. This is why they are considered registered at this
193 ((equalp msg
:read-some-bytes
)
194 (setf read-handler-registered t
)
196 ((equalp msg
:write-some-bytes
)
197 (setf write-handler-registered t
)
200 (error "make-ex8-buffer: Please supply :read-some-bytes or :write-some-bytes~%")))))))