List of Examples
List of Examples
Sockets are used for interprocess communications by processes running on the same host as well as by processes running on different hosts over a computer network. The most common kind of sockets is Internet stream sockets, and a high-level interface to them is described here. A more low level interface that closely follows the C system calls is also available, see Section 33.17, “Raw Socket Access”.
Two main varieties of sockets are interfaced to:
SOCKET:SOCKET-STREAM
s which are bidirectional
STREAM
sSOCKET:SOCKET-SERVER
s which are a special
kind of objects that are used to allow the other side to initiate
interaction with lisp.Example 32.13. Lisp read-eval-print loop server
Here is a simple lisp read-eval-print loop server that waits for a remote connection and evaluates forms read from it:
(LET
((server (SOCKET:SOCKET-SERVER
))) (FORMAT
t "~&Waiting for a connection on ~S:~D~%" (SOCKET:SOCKET-SERVER-HOST
server) (SOCKET:SOCKET-SERVER-PORT
server)) (UNWIND-PROTECT
;; infinite loop, terminate with Control+C (LOOP
(WITH-OPEN-STREAM
(socket (SOCKET:SOCKET-ACCEPT
server)) (MULTIPLE-VALUE-BIND
(local-host local-port) (SOCKET:SOCKET-STREAM-LOCAL
socket) (MULTIPLE-VALUE-BIND
(remote-host remote-port) (SOCKET:SOCKET-STREAM-PEER
socket) (FORMAT
T
"~&Connection: ~S:~D -- ~S:~D~%" remote-host remote-port local-host local-port))) ;; loop is terminated when the remote host closes the connection or onEXT:EXIT
(LOOP
(WHEN
(EQ
:eof (SOCKET:SOCKET-STATUS
(cons socket :input))) (RETURN
)) (EVAL
(READ
socket)) socket) ;; flush everything left in socket (LOOP
:for c = (READ-CHAR-NO-HANG
socket nil nil) :while c) (TERPRI
socket)))) ;; make sure server is closed (SOCKET:SOCKET-SERVER-CLOSE
server)))
Functions like EXT:SHELL
, EXT:EXECUTE
, EXT:RUN-SHELL-COMMAND
will allow the
remote host to execute arbitrary code with your permissions.
While functions defined in lisp (like EXT:RUN-SHELL-COMMAND
) can be removed
(using FMAKUNBOUND
), the built-in functions (like EXT:SHELL
and EXT:EXECUTE
)
cannot be permanently removed from the runtime, and an experienced
hacker will be able to invoke them even if you FMAKUNBOUND
their names.
You should limit the socket server to local connections
by passing the STRING
"127.0.0.1"
as the :INTERFACE
argument to SOCKET:SOCKET-SERVER
.
Example 32.14. Lisp HTTP client
Here are a couple of simple lisp HTTP clients that fetch a web page and a binary file, and upload a file:
(DEFUN
wget-text (host page file&OPTIONAL
(port 80)) ;; HTTP requires the:DOS
line terminator (WITH-OPEN-STREAM
(socket (SOCKET:SOCKET-CONNECT
port host:EXTERNAL-FORMAT
:DOS
)) (FORMAT
socket "GET ~A HTTP/1.0~2%" page) ;; dump the whole thing - header+data - into the output file (WITH-OPEN-FILE
(out file :direction :output) (LOOP
:for line = (READ-LINE
socket nil nil) :while line :do (WRITE-LINE
line out))))) (DEFUN
wget-binary (host page file&OPTIONAL
(port 80)) (WITH-OPEN-STREAM
(socket (SOCKET:SOCKET-CONNECT
port host:EXTERNAL-FORMAT
:DOS
)) (FORMAT
socket "GET ~A HTTP/1.0~2%" page) (LOOP
:with content-length :for line = (READ-LINE
socket nil nil) ;; header is separated from the data with a blank line :until (ZEROP
(LENGTH
line)) :do (WHEN
(STRING=
line #1="Content-length: " :end1 #2=#.
(LENGTH
#1#)) (SETQ
content-length (PARSE-INTEGER
line :start #2#)) ;; this will not work if the server does not supply the content-length header :finally (RETURN
(LET
((data (MAKE-ARRAY
content-length :element-type '(
))) ;; switch to binary i/o on socket (UNSIGNED-BYTE
8)SETF
(STREAM-ELEMENT-TYPE
socket) '(
) ;; read the whole file in one system call (UNSIGNED-BYTE
8)EXT:READ-BYTE-SEQUENCE
data socket) (WITH-OPEN-FILE
(out file :direction :output:ELEMENT-TYPE
'(
) ;; write the whole file in one system call (UNSIGNED-BYTE
8)EXT:WRITE-BYTE-SEQUENCE
data out)) data)))))) (DEFUN
wput (host page file&OPTIONAL
(port 80)) (WITH-OPEN-STREAM
(socket (SOCKET:SOCKET-CONNECT
port host:EXTERNAL-FORMAT
:DOS
)) (WITH-OPEN-FILE
(in file :direction :inptut:ELEMENT-TYPE
'(
) (UNSIGNED-BYTE
8)LET*
((length (FILE-LENGTH
in)) (data (MAKE-ARRAY
length :element-type '(
))) ;; some servers may not understand the "Content-length" header (UNSIGNED-BYTE
8)FORMAT
socket "PUT ~A HTTP/1.0~%Content-length: ~D~2%" page length) (SETF
(STREAM-ELEMENT-TYPE
socket) '(
) (UNSIGNED-BYTE
8)EXT:READ-BYTE-SEQUENCE
data in) (EXT:WRITE-BYTE-SEQUENCE
data socket))) ;; not necessary if the server understands the "Content-length" header (SOCKET:SOCKET-STREAM-SHUTDOWN
socket :output) ;; get the server response (LOOP
:for line = (READ-LINE
socket nil nil) :while line :collect line)))
(SOCKET:SOCKET-SERVER
&OPTIONAL
port
&KEY
:INTERFACE
:BACKLOG
)
This function creates a passive socket an binds a port to it. The server exists to watch for client connection attempts.
The optional argument is the port to use (non-negative
FIXNUM
, 0
means assigned by the system).
The :BACKLOG
parameter defines maximum
length of queue of pending connections (see
listen
) and defaults to 1.
The :INTERFACE
parameter specifies the
interface(s) on which the socket server will listen, and is either a
STRING
, interpreted as the interface IP
address that will be
bound, or a socket, from whose peer the connections will be made.
Default is (for backward compatibility) to bind to all local
interfaces, but for security reasons it is advisable to bind to
the loopback interface "127.0.0.1"
if
you need only local connections.
(SOCKET:SOCKET-SERVER-CLOSE
socket-server
)
SOCKET:SOCKET-SERVER
s are closed at garbage-collection.
You should not rely on this however, because garbage-collection times are not
deterministic and the port assigned to the server socket cannot be
reused until it is closed.(SOCKET:SOCKET-SERVER-HOST
socket-server
)
(SOCKET:SOCKET-SERVER-PORT
socket-server
)
SOCKET:SOCKET-SERVER
.
(SOCKET:SOCKET-WAIT
socket-server
&OPTIONAL
[seconds
[microseconds
]])
socket-server
(a SOCKET:SOCKET-SERVER
).
Without a timeout argument, SOCKET:SOCKET-WAIT
blocks indefinitely.
When timeout is zero, poll.
Returns T
when a connection is available (i.e., SOCKET:SOCKET-ACCEPT
will
not block) and NIL
on timeout.(SOCKET:SOCKET-ACCEPT
socket-server
&KEY
:ELEMENT-TYPE
:EXTERNAL-FORMAT
:BUFFERED
:TIMEOUT
)
Waits for an attempt to connect to the socket-server
and
creates the server-side bidirectional SOCKET:SOCKET-STREAM
for the connection.
(SOCKET:SOCKET-CONNECT
port
&OPTIONAL
[host
] &KEY
:ELEMENT-TYPE
:EXTERNAL-FORMAT
:BUFFERED
:TIMEOUT
)
SOCKET:SOCKET-STREAM
. Blocks until the server accepts the connection, for
no more than :TIMEOUT
seconds. If it is 0, returns immediately
and (probably) blocks on the next i/o operation (you can use
SOCKET:SOCKET-STATUS
to check whether it will actually block).
(SOCKET:SOCKET-STATUS
socket-stream-or-list
&OPTIONAL
[seconds
[microseconds
]])
Checks whether it is possible to read from or write
to a SOCKET:SOCKET-STREAM
or whether a connection is available on a
SOCKET:SOCKET-SERVER
without blocking.
This is similar to LISTEN
, which checks only one
STREAM
and only for input, and SOCKET:SOCKET-WAIT
, which works only with
SOCKET:SOCKET-SERVER
s.
We define status
for a SOCKET:SOCKET-SERVER
or a SOCKET:SOCKET-STREAM
to be :ERROR
if any i/o operation will cause an ERROR
.
Additionally, for a SOCKET:SOCKET-SERVER
, we define
status
to be T
if a connection is available, i.e.,
is SOCKET:SOCKET-ACCEPT
will not block, and NIL
otherwise.
Additionally, for a SOCKET:SOCKET-STREAM
, we define status
in the
given direction
(one of :INPUT
, :OUTPUT
, and :IO
) to be
Possible status values for various directions:
|
| ||||||
|
| ||||||
|
Possible values of
socket-stream-or-list
:
SOCKET:SOCKET-STREAM
or SOCKET:SOCKET-SERVER
:IO
status for SOCKET:SOCKET-STREAM
)
(SOCKET:SOCKET-STREAM
. direction
)
MAPCAR
)If you want to avoid consing[3] up a fresh list, you can
make the elements of socket-stream-or-list
to be (
or socket-stream
direction
.
x
)(
.
Then socket-server
. x
)SOCKET:SOCKET-STATUS
will destructively modify its argument and replace
x
or NIL
with the status and return the modified list.
You can pass this modified list to SOCKET:SOCKET-STATUS
again.
The optional arguments specify the timeout. NIL
means
wait forever, 0
means poll.
The second value returned is the number of objects with
non-NIL
status, i.e., “actionable” objects.
SOCKET:SOCKET-STATUS
returns either due to a timeout or when this number is
positive, i.e., if the timeout was NIL
and SOCKET:SOCKET-STATUS
did
return, then the second value is positive (this is the reason NIL
is not treated as an empty LIST
, but as an invalid
argument).
Note that SOCKET:SOCKET-STATUS
may SIGNAL
a STREAM-ERROR
.
This happens if the SOCKET:SOCKET-STREAM
receives an RST
packet,
see tests/econnreset.lisp
.
This is the interface to select
(on some platforms, poll
),
so it will work on any CLISP STREAM
which is based on a
file descriptor, e.g., EXT:*KEYBOARD-INPUT*
and file/pipe/socket STREAM
s, as well as
on raw sockets.
(SOCKET:SOCKET-STREAM-HOST
socket-stream
)
(SOCKET:SOCKET-STREAM-PORT
socket-stream
)
SOCKET:SOCKET-STREAM
.(SOCKET:SOCKET-STREAM-PEER
socket-stream
[do-not-resolve-p
])
Given a SOCKET:SOCKET-STREAM
, this function returns the
name of the host on the opposite side of the connection and its port
number; the server-side can use this to see who connected.
When the optional second argument is non-NIL
, the hostname
resolution is disabled and just the IP
address is returned, without
the FQDN.
The socket-stream
argument can also be a
raw socket.
(SOCKET:SOCKET-STREAM-LOCAL
socket-stream
[do-not-resolve-p
])
The dual to SOCKET:SOCKET-STREAM-PEER
- same information,
host name and port number, but for the local host.
The difference from SOCKET:SOCKET-STREAM-HOST
and SOCKET:SOCKET-STREAM-PORT
is that this function
asks the OS (and thus returns the correct trusted values) while the
other two are just accessors to the internal data structure, and
basically return the arguments given to the function which created
the socket-stream
.
The socket-stream
argument can also be a
raw socket.
(SOCKET:SOCKET-STREAM-SHUTDOWN
socket-stream
direction
)
Some protocols provide for closing the connection
in one direction
using shutdown
.
This function provides an interface to this UNIX system call.
direction
should be :INPUT
or :OUTPUT
. Note that you
should still call CLOSE
after you are done with your socket-stream
; this
is best accomplished by using WITH-OPEN-STREAM
.
All SOCKET:SOCKET-STREAM
s are bidirectional STREAM
s (i.e., both INPUT-STREAM-P
and OUTPUT-STREAM-P
return T
for them).
SOCKET:SOCKET-STREAM-SHUTDOWN
breaks this and turns its argument
stream into an input STREAM
(if direction
is :OUTPUT
) or output STREAM
(if
direction
is :INPUT
).
Thus, the following important invariant is preserved: whenever
STREAM
is open
(i.e., OPEN-STREAM-P
returns T
) andSTREAM
is an input STREAM
(i.e., INPUT-STREAM-P
returns T
)
the STREAM
can be read from (e.g., with READ-CHAR
or READ-BYTE
).
The socket-stream
argument can also be a
raw socket.
(SOCKET:SOCKET-OPTIONS
socket-server
&REST
{option
}*)
Query and, optionally, set socket options using
getsockopt
and setsockopt
.
An option
is a keyword, optionally followed by the new value.
When the new value is not supplied, setsockopt
is not called.
For each option the old (or current, if new value was not supplied)
value is returned. E.g., (
returns 2 values: SOCKET:SOCKET-OPTIONS
socket-server
:SO-LINGER 1 :SO-RCVLOWAT)NIL
, the old
value of the :SO-LINGER
option, and 1, the
current value of the :SO-RCVLOWAT
option.
The socket-stream
argument can also be a
raw socket.
(SOCKET:STREAM-HANDLES
stream
)
stream
as multiple values. See Section 33.17, “Raw Socket Access”.
These notes document CLISP version 2.49 | Last modified: 2010-07-07 |