TCP stream
A TCP stream wraps a socket, which is returned by tcp-connect:
(let ((socket (tcp-connect "google.com" 80 ... :dont-drain-read-buffer t)))
(make-instance 'async-io-stream :socket socket))
tcp-connect
makes this a bit more convenient as well:
(tcp-connect "google.com" 80 ... :stream t)
The stream we just created is a normal lisp stream which can be operated on via
close
, read-sequence
, write-sequence
, etc. One caveat is that the stream
does not block when pulling data from it, so reading from the stream should be
triggered by the read-cb
given to tcp-connect
.
See the notes on buffer draining section for an explanation
of the :dont-drain-read-buffer
parameter.
Classes
async-stream
The base async stream, extended by all other cl-async TCP streams.
async-output-stream
The base output stream, can only be used to send data on a socket.
async-input-stream
The base input stream, can only be used to recieve data on a socket.
async-io-stream
Extends both async-output-stream
and async-input-stream
, and allows both
reading and writing on the underlying socket.
Buffer draining notes
Streams work by sucking the data out of the socket they are set up to wrap.
Normally, when a read-cb
fires on a socket, cl-async will drain the
data received and put it into a byte array it passes to the read-cb
.
In the case of streams, cl-async pushes the data onto the stream’s input buffer
instead of passing the data to the read-cb
directly. The stream is then passed
as the second parameter to the read-cb
(the first still being the socket
object). This is what :dont-drain-read-buffer
does for you. Note that if you
specify :stream t
then dont-drain-read-buffer
is assumed to be T
unless
explicitely stated otherwise.
When the read-cb
fires and dont-drain-read-buffer
is T
, the data
param
will always be the stream and the socket data can be read via read-sequence
or
similar. Note that the socket arg (the first arg) to the read-cb
is still a
socket.
Example usage
(tcp-connect "musio.com" 80
(lambda (sock stream)
(let* ((seq (make-array 4096 :element-type '(unsigned-byte 8)))
(num-bytes (read-sequence seq stream :end 4096)))
(format t "~a" (babel:octets-to-string (subseq seq 0 num-bytes)))))
(lambda (ev) (format t "ev: ~a~%" ev))
:data (format nil "GET /~C~C" #\return #\newline)
:stream t
:read-timeout 5)
Note that you can write the socket returned by (tcp-connect ... :stream t)
with
write-sequence
like any other stream.