cl-async

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.