$Revision: 5.0.2.7 $
The document introduction.htm provides an overview of the Allegro CL documentation with links to all major documents. The document index.htm is an index with pointers to every documented object (operators, variables, etc.) The revision number of this document is below the title. These documents may be revised from time to time between releases.
1.0 Introduction and background
2.0 Characteristics
3.0 Stream Sockets
3.1 Connections
3.2 Host Naming
4.0 Variables
5.0 Functions
6.0 Errors
7.0 Examples
Sockets are a mechanism for interprocess communication designed at U.C. Berkeley for use in their version of Unix. Sockets have been added to many other versions of Unix and there is an implementation of sockets for Windows called Winsock. This document describes the Allegro interface to sockets. This interface works on Unix and on Windows (95/98 and NT).
The socket code is found the in package acl-socket which has the nickname socket.
The socket module is not included in all versions of Allegro CL. If it is present, it is (by default) included in a development image (one built with the include-devel-env argument to build-lisp-image specified true). To load the socket module if it is not present in an image, evaluate
(require :sock)
Note that runtime images cannot include the development environment (so include-devel-env must be specified nil when a runtime image is being built). If the socket module is needed, it must be loaded when the image is built. See runtime.htm, building_images.htm and delivery.htm for more information.
There are three independent characteristics of sockets:
type | Valid values: :stream or :datagram.
|
address family | Valid values: :internet or
:file. In order to send to another socket the socket must have a name.
Note that the current version of the socket interface on Windows (Winsock, version 1.1), does not support the :file address family. |
format | Valid values: :text or :binary, or, for :stream sockets only, :bivalent
(see note below) This isn't a property of the Unix socket implementation but is instead something we've added for the Common Lisp implementation since a Lisp stream is either binary (supports read-byte, etc.) or text (supports read-char, etc.). |
Note on bivalent format:
Starting in release 5.0.1, the bivalent format is accepted for stream sockets. Bivalent means that the stream will accept text and binary stream functions. That is, you can write-byte or write-char, read-byte or read-char. A bivalent stream is useful in the http protocol (used between web browsers and web servers) since in that protocol the header data is sent in text format and the body can been binary data (image files, for example).
Internally a bivalent socket stream is configured like a binary socket stream with 8 bit bytes. Character position is not maintained.
Bivalent socket streams have very efficient read-sequence and write-sequence implementations (as long as the sequence is either a vector of element-type charcter, (unsigned-byte 8) or (signed-byte 8)).
Bivalent socket streams also support the chunking protocol found in http/1.1. This protocol allows the sender to signal end of file without closing down the stream.
Stream sockets have a fourth characteristic called connect, with a value :active or :passive. In order to use stream sockets you have to set up a link between two of them. That link is called a connection. You set up a connection in this way:
Machine A: create a passive socket at port port-b:
|
Machine B: create an active socket telling it to connect to Machine A,
port port-b:
|
Machine A: wait for a connect request from anyone and when it occurs
return a stream for I/O:
|
Note that steps 2 and 3 can occur in either order.
Note the asymmetry: a passive socket is not a Lisp stream (you can't do read and write to it). An active socket is a Lisp stream.
When accept-connection is called on a passive socket, it does not return until a connection is made to the passive socket. The value accept-connection returns is a stream.
As long as the passive socket is not closed, new connections can still be made to the port of that socket.
An active socket can be used for only one connection. Once that connection has been made, the socket should be closed and a new active socket created.
Host naming conventions: this package supports three conventions for naming a host:
hostname | A string using the domain naming convention, e.g. "ftp.franz.com". The domain naming system is case-insensitive. |
dotted | A string which is the printed representation of the numeric address: e.g. "192.132.95.84". We also support the non standard Berkeley extensions to this format for class A addresses: "23.3" (which is the same as "23.0.0.3") and class B addresses "128.1.3" (which is the same as "128.1.0.3"). |
ipaddr | An unsigned 32-bit number, representing the IP address in the native byte order for the host. Thus 192.132.95.84 is 192*2^24 + 132*2^16 + 95*2^8 + 84 = 3229900628. |
The variables defined by the interface are:
Please provide the value of this variable when asking for technical support with sockets as it tells us whether you have the latest version.
socket:*print-hostname-in-stream*
This variable controls whether the socket printing code converts the ip address of a socket into a hostname. This is usually what you want, however this can be a slow process (taking up to a minute to accomplish). The default value for this variable is t. See the full description for a discussion of the causes of the possible slowdown when the value is t.
The first table shows general functions defined by the interface and the second shows accessors.
Function | Arguments | Notes (follow function link for full description) |
accept-connection | (sock passive-socket) &key wait | Generic function. Establishes a connection. If wait is nil and no connection is pending, returns nil and does nothing further. If wait is true (the default), waits until a connection is established. When a connection is established, returns the stream that communicates with the socket. |
dotted-to-ipaddr | dotted &key errorp | Function. Converts a string like "192.132.95.84" or similar format to an unsigned 32-bit IP address. |
ipaddr-to-dotted | ipaddr &key values | Function. Convert a 32-bit unsigned IP address, ipaddr, to a string in dotted form. |
ipaddr-to-hostname | ipaddr | Function. Returns, as a string, the hostname of the machine with the given 32-bit IP address, ipaddr. |
lookup-hostname | hostname | Given a string naming a host, a 32-bit IP address, or a string in dotted form, return the 32-bit IP address for the host. |
lookup-port | portname protocol | Function. Finds the port number using the symbolic name and the protocol. |
make-socket | &key type format address-family connect &allow-other-keys | Function. See the full description for details. |
with-pending-connect | &body body | Macro. See the full description for details. |
receive-from | (sock datagram-socket) size &key buffer extract | Generic function. This is used to read from a datagram socket. |
send-to | sock &key | Generic function with methods for internet-datagram-sockets and file-datagram-sockets |
shutdown | sock &key direction | Generic function that closes down the specified half of the bidirectional socket connection. (New on 5.0.1.) |
socket-control | stream &key output-chunking output-chunking-eof input-chunking | This function modifies the state of the socket stream, controling input and output chunking. |
socket-os-fd | sock | Generic function. Return the operating system file descriptor associated with this socket. |
These functions retrieve slot values from socket instances. The values of these slots are set when the socket is created.
Function | Arguments | Notes
(follow function link for full description) |
remote-host | socket | Generic function. Returns an IP address. |
local-host | socket | Generic function. Returns an IP address. |
local-port | socket | All are generic functions. All return the values
of the particular attribute for socket. Note: Both internet stream and internet datagram sockets use 16-bit port numbers. Note that stream (tcp) port N is totally distinct from datagram (udp) port N. |
remote-filename | socket | |
local-filename | socket | |
remote-port | socket | |
socket-address-family | socket | |
socket-connect | socket | |
socket-format | socket | |
socket-type | socket |
When errors are raised by the socket interface, Lisp conditions are signaled. This section describes those conditions.
A condition is a CLOS class and thus fits into the hierarchy of CLOS classes. The condition excl::socket-error is a subclass of the condition error. The condition excl::system-socket-error is a subclass of excl::socket-error.
excl::socket-error is the superclass for all socket related errors. It add no slots to the error condition class. In the future we may add subclasses to excl::socket-error, but at present it is merely a placeholder condition and is never signaled.
excl::system-socket-error denotes operating system detected socket errors. It adds the following slots:
Name | Reader function | What |
excl::identifier | excl::socket-error-identifier | Symbol denoting this error (see table below) |
excl::code | excl::socket-error-code | Operating system dependent error code (if any) |
excl::situation | excl::socket-error-situation | String describing the operation in progress when the the error occurred |
Handling socket error is difficult because the error returned in exceptional situations can depend on the operating system and the address of the other side of the connection. For example, attempting to make a connection to a machine that is down may result in a "Connection Timed Out" or a "Host Unreachable" error, or maybe something else on certain systems.
The error codes assigned to socket errors vary from operating system to operating system We translate a large set of the common error codes from a machine dependent number to a symbol which we call the identifier to make it easier for you to write portable code. Condition handling code should check the identifier field (using excl::socket-error-identifier). If the identifier value is :unknown then this is not a common socket error and the operating system dependent code value of the condition must be used.
Possible identifier values and their meanings:
Identifier | Meaning |
:address-in-use | Local socket address already in use |
:address-not-available | Local socket address not available |
:network-down | Network is down |
:network-reset | Network has been reset |
:connection-aborted | Connection aborted |
:connection-reset | Connection reset by peer |
:no-buffer-space | No buffer space |
:shutdown | Connection shut down |
:connection-timed-out | Connection timed out |
:connection-refused | Connection refused |
:host-down | Host is down |
:host-unreachable | Host is unreachable |
:unknown | Unknown error |
Create an active stream socket connection to a socket that just prints characters to
whomever connects to it. After connecting, read the first five characters and print them
out. USER(1): (let ((s (make-socket :remote-host "vapor" :remote-port "chargen"))) (dotimes (i 5) (print (read-char s))) (close s)) #\space #\! #\" #\# #\$ |
Sending a message from frisky to vapor: on vapor: USER(1): (print (read (accept-connection (make-socket :connect :passive :local-port 9933)))) .. this hangs ... on frisky: USER(1): (let ((s (make-socket :remote-host "vapor" :remote-port 9933))) (format s "Secret-message~%") (close s)) Then you see on vapor: Secret-message Secret-message USER(2): A flaw in this example is that on vapor we've left the socket and the stream open and we lost track of the objects to close them. So, while concise, this is not a good programming style. Another problem with this example is that when we created the port on vapor we used a specific port number (9933). This means our program will fail if port 9933 is already in use. If possible, it is best to let the system choose a port number (this is done by not specifying a :local-port argument) and then using the local-port function to find out which port was chosen. |
If we just want to send a simple message then datagrams might be more appropriate
(although the program must guarantee that the message made it because datagram
communication is unreliable). on vapor: user(2): (setq s (make-socket :type :datagram :local-port 9999)) #<text datagram socket waiting for connection at */9999 @ #x20664e82> user(3): on frisky: user(10): (setq x (make-socket :type :datagram)) #<text datagram socket waiting for connection at */45602 @ #x20717fb2> user(11): (send-to x "foo-the-bar" 11 :remote-host "ultra" :remote-port 9999) 11 user(12): on vapor: user(3): (receive-from s 100 :extract t) "foo-the-bar" 11 ;; length of result 3229900653 ;; frisky's IP address 45602 ;; the port number chosen for the socket by frisky user(4): |
Copyright (C) 1998-1999, Franz Inc., Berkeley, CA. All Rights Reserved.