TCP
This section details sending and receving data over TCP, along with how to deal with cl-async sockets. It also goes over conditions/events one might run into while using the TCP system.
- tcp-connect function
- tcp-send function (deprecated)
- init-tcp-socket function
- connect-tcp-socket function
- tcp-server class
- tcp-server function
- close-tcp-server method
- socket class
- socket-c accessor
- socket-data accessor
- write-socket-data function
- set-socket-timeouts function
- enable-socket function
- disable-socket function
- socket-closed-p function
- close-socket method
- tcp-info condition
- tcp-socket accessor
- tcp-error condition
- tcp-eof condition
- tcp-timeout condition
- tcp-refused condition
- tcp-accept-error condition
- tcp-accept-error-listener accessor
- tcp-accept-error-tcp-server accessor
- socket-closed condition
- tcp-server-bind-error condition
tcp-connect
(defun tcp-connect (host port read-cb event-cb
&key data stream
connect-cb write-cb
(read-timeout -1) (write-timeout -1)
(dont-drain-read-buffer nil dont-drain-read-buffer-supplied-p))
=> socket/stream
Open an asynchronous TCP connection to a host (IP or hostname) and port. You can
specify data to be sent once the connection is established via the :data
keyword. All incoming data will be sent to the read-cb,
and any events will be sent to the event-cb.
tcp-connect
returns a socket class, which wraps the C socket
implementation and also allows storing arbitrary data with the socket.
tcp-connect
can also return a stream of type async-io-stream
when the keyword argument :stream
is T
. This allows normal stream
operations on top of a non-blocking socket.
Note that tcp-connect
always opens a new connection. If you want to send data
on and existing connection (and also be able to set new read/write/event
callbacks on it), check out write-socket-data, or in the
case of a stream, you can use write-sequence
to send new data on the stream.
Note that the host
can be an IP address or a hostname. The hostname will
be looked up asynchronously via cl-async’s DNS implementation.
;; example:
(tcp-connect "www.google.com" 80
(lambda (socket data)
(when (pretend-http-package:process-http-stream data)
(close-socket socket))) ; close the socket if done processing
#'my-app-error-handler
:data (format nil "GET /~c~c" #\return #\newline))
See the tcp-stream page for some examples on stream usage.
read-cb definition (default)
(lambda (socket byte-array) ...)
read-cb definition (when tcp-connect’s :stream is t)
(lambda (socket stream) ...)
Note that in this case, stream
replaces the data byte array’s position. Also,
when calling :stream t
in tcp-connect
, the read buffer for the socket is not
drained and is only done so by reading from the stream.
stream
is always the same object returned from tcp-connect
with :stream t
.
It wraps the socket object.
connect-cb definition
(lambda (socket) ...)
The connect-cb
will be fired when the connection from tcp-connect
has been
established. Since sending data over the socket is somewhat transparent (either
via :data
or write-socket-data), you don’t really have
to know when a socket is ready to be written to. In some instances though, it
may be useful to know when the connection has been established, which is why
:connect-cb
is exposed.
write-cb definition
(lambda (socket) ...)
The write-cb
will be called after data written to the socket’s buffer is
flushed out to the socket. If you want to send a command to a server and
immediately disconnect once you know the data was sent, you could close the
connection in your write-cb
.
tcp-send (deprecated)
This function is a deprecated version of tcp-connect. Use
tcp-connect
instead, as tcp-send
may be removed in later versions.
init-tcp-socket
(defun init-tcp-socket (read-cb event-cb
&key data stream
connect-cb write-cb
(read-timeout -1) (write-timeout -1)
(dont-drain-read-buffer nil dont-drain-read-buffer-supplied-p))
=> socket/stream
This function is much like tcp-connect but with a few exceptions:
- It only initializes a socket object, it doesn’t connect it.
- It doesn’t accept host/port arguments.
In other words, init-tcp-socket
is tcp-connect
’s lower-level brother. Once
initialized, an unconnected socket can be connected using connect-tcp-socket.
connect-tcp-socket
(defun connect-tcp-socket (socket/stream host port &key event-cb))
=> socket/stream
This is mean to be used with an unconnected socket created by init-tcp-socket.
If you want to initialize and connect a socket in one function call, use tcp-connect,
however if you want more control over when a socket is connected, you can use
init-tcp-socket along with connect-tcp-socket
.
tcp-server (class)
This is an opaque class which is returned by the function tcp-server to allow closing the server and allowing for future expansion of the server’s abilities. It has no public accessors.
tcp-server
(defun tcp-server (bind-address port read-cb event-cb
&key connect-cb (backlog -1) stream))
=> tcp-server
Bind an asynchronous listener to the given bind address/port and start accepting
connections on it. It takes read and event callbacks (like tcp-connect).
If nil
is passed into the bind address, it effectively binds the listener to
“0.0.0.0” (listens from any address). A connection backlog can be specified when
creating the server via :backlog
, which defaults to -1. A connect-cb
can be passed in as a keyword arg, which sets a callback to be called whenever a
new connection comes in.
tcp-server
accepts a :stream
arg, which when T
will call its read-cb
with an async-io-stream instead of a
byte array.
This function returns a tcp-server object, which allows you to close the server via close-tcp-server.
If binding to an address/port fails, tcp-server
will throw a tcp-server-bind-error
exception. Generally this only happens if the port is already in use or the
port is “privileged.”
;; example
(tcp-server "127.0.0.1" 8080
(lambda (socket data)
(format t "data: ~a~%" data)
(write-socket-data socket "i noticed you have brathes. i have brathes too. uhhhhuhuhuh."
:write-cb (lambda (socket)
(close-socket socket))))
nil) ;; use *default-event-handler* as the event handler for this operation
read-cb definition (default)
(lambda (socket byte-array) ...)
read-cb definition (when tcp-server is called with :stream t)
(lambda (socket stream) ...)
Note that in this case, stream
replaces the data byte array’s position. Also,
when calling :stream t
in tcp-stream
, the read buffer for the connecting
socket is not drained and is only done so by reading from the stream.
connect-cb definition
(lambda (socket) ...)
Called when a client connects (but not necessarily when it has sent data). If present, is always called before the read-cb.
close-tcp-server
(defun close-tcp-server (tcp-server))
=> nil
Takes a tcp-server
object, created by tcp-server and closes the
server it wraps gracefully. This can be useful if you want to shut down a TCP
server without forcibly closing all its connections (via exit-event-loop,
for instance).
If the given server is already closed, this function returns without doing anything.
socket
This class is a wrapper around the libuv socket class. It is passed to tcp callback functions, and allows you to perform certain actions on the socket (such as closing it, setting read/write timeouts, writing data to it, etc).
It also exposes an accessor, socket-data, which allows you to store arbitrary, app-specific data in the socket.
socket-c
This accessor lets you access the underlying libuv stream object for the socket. While this is not immediately useful for any cl-async related purpose (and manipulating it outside of cl-async may make your worst nightmares come true if you aren’t careful), it can be very useful to do your own stream operations through the cl-libuv bindings.
socket-data
This accessor allows you to set arbitrary data into the socket class,
which can be useful if your app needs to match specific data to a socket (for
instance if you are proxying, you could use socket-data
to store a reference
to the outgoing socket inside the incoming socket).
write-socket-data
(defun write-socket-data (socket data &key read-cb write-cb event-cb))
=> nil
Write data to an existing socket (such as one passed into a tcp-connect
read-cb).
Data can be a byte array or string (converted to a byte array via babel).
Supports resetting the callbacks on the given socket. The write-cb
is useful
if you want to close the connection after sending data on the socket but want to
make sure the data sent before closing.
Note that if you call this using a socket that has been closed already, it will throw a socket-closed condition.
;; examples
(write-socket-data socket "thanks for connecting. how are you? (good|bad)"
:read-cb (lambda (socket data)
(my-app:continue-conversation socket data))
:event-cb (lambda (err)
(format t "condition while having convo: ~a~%" err)))
(write-socket-data socket "invalid command, closing connection"
:write-cb (lambda (socket) (close-socket socket)))
If you were to close the socket right after sending the data to the buffer,
there’s no guarantee it would be sent out. Setting a write-cb
guarantees that
the data is sent when called.
Note that write-socket-data
’s callbacks are identical to tcp-connect’s
and if specified, will override those set by tcp-connect.
set-socket-timeouts
(defun set-socket-timeouts (socket read-sec write-sec))
=> nil
Set the read/write timeouts (in seconds) on a socket. If nil, the timeout is cleared, otherwise if a number, the timeout is set into the socket such that when the socket is active and hasn’t been read from/written to in the specified amount of time, it is closed.
nil
for a timeout value unsets the timeout.
Note that if you call this using a socket that has been closed already, it will throw a socket-closed condition.
;; example
(set-socket-timeouts socket 10.5 nil)
enable-socket
(defun enable-socket (socket &key read write))
=> nil
Enable read/write monitoring on a socket. This is done automatically by tcp-connect and write-socket-data so you probably don’t need to worry too much about when to use it. On the other hand, disable-socket will probably be a bit more useful.
;;example
(enable-socket socket :read t :write t) ; enable read and write monitoring on this socket
disable-socket
(defun disable-socket (socket &key read write))
=> nil
Disable read/write monitoring on a socket. This is useful if you get the data you need from a socket, but while you’re processing the data, you don’t want the socket’s read timeout to fire. This will both disable the timeouts and callbacks associated with the socket until enabled again.
socket-closed-p
(defun socket-closed-p (socket))
=> t/nil
Determines if a socket has been closed already.
close-socket
(defun close-socket (socket))
=> nil
Close a socket and free its callbacks.
Note that if you call this using a socket that has been closed already, it will throw a socket-closed condition.
Conditions
These are the conditions the TCP system can signal in event callbacks.
tcp-info
extends event-info
Base TCP condition, says “something” happened on a TCP connection.
tcp-socket
Holds the TCP socket class. Can be used to write to the socket or close it.
tcp-error
extends event-error and tcp-info
Describes a general error on a TCP connection. If this is triggered, the socket will generally be closed by cl-async, and the app doesn’t need to worry about doing this. If the app does want to close the socket, it can do so by getting it from the tcp-socket accessor on the condition and using close-socket.
tcp-eof
extends tcp-info
Triggered when the peer on a TCP connection closes the socket.
tcp-timeout
extends tcp-error
Triggered when a TCP connection times out.
tcp-refused
extends tcp-error
Triggered when a TCP connection is refused by the peer.
tcp-accept-error
extends tcp-error
Passed to a tcp-server’s event-cb
when there is an error
accepting a client connection.
tcp-accept-error-listener
The listener c object. Provided in case your app needs to process it in some way.
tcp-accept-error-tcp-server
The tcp-server
object that the accept error happened on.
socket-closed
extends tcp-error
This exception is thrown by cl-async when the app tries to perform an operation on a socket that has already been closed via close-socket.
tcp-server-bind-error
extends tcp-error
This exception is thrown when tcp-server fails to bind to the address/port it has been given.