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-STREAMs which are bidirectional
STREAMsSOCKET:SOCKET-SERVERs 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))) (FORMATt "~&Waiting for a connection on ~S:~D~%" (SOCKET:SOCKET-SERVER-HOSTserver) (SOCKET:SOCKET-SERVER-PORTserver)) (UNWIND-PROTECT;; infinite loop, terminate with Control+C (LOOP(WITH-OPEN-STREAM(socket (SOCKET:SOCKET-ACCEPTserver)) (MULTIPLE-VALUE-BIND(local-host local-port) (SOCKET:SOCKET-STREAM-LOCALsocket) (MULTIPLE-VALUE-BIND(remote-host remote-port) (SOCKET:SOCKET-STREAM-PEERsocket) (FORMATT"~&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(READsocket)) socket) ;; flush everything left in socket (LOOP:for c = (READ-CHAR-NO-HANGsocket nil nil) :while c) (TERPRIsocket)))) ;; make sure server is closed (SOCKET:SOCKET-SERVER-CLOSEserver)))
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:
(DEFUNwget-text (host page file&OPTIONAL(port 80)) ;; HTTP requires the:DOSline terminator (WITH-OPEN-STREAM(socket (SOCKET:SOCKET-CONNECTport host:EXTERNAL-FORMAT:DOS)) (FORMATsocket "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-LINEsocket nil nil) :while line :do (WRITE-LINEline out))))) (DEFUNwget-binary (host page file&OPTIONAL(port 80)) (WITH-OPEN-STREAM(socket (SOCKET:SOCKET-CONNECTport host:EXTERNAL-FORMAT:DOS)) (FORMATsocket "GET ~A HTTP/1.0~2%" page) (LOOP:with content-length :for line = (READ-LINEsocket nil nil) ;; header is separated from the data with a blank line :until (ZEROP(LENGTHline)) :do (WHEN(STRING=line #1="Content-length: " :end1 #2=#.(LENGTH#1#)) (SETQcontent-length (PARSE-INTEGERline :start #2#)) ;; this will not work if the server does not supply the content-length header :finally (RETURN(LET((data (MAKE-ARRAYcontent-length :element-type '())) ;; switch to binary i/o on socket (UNSIGNED-BYTE8)SETF(STREAM-ELEMENT-TYPEsocket) '() ;; read the whole file in one system call (UNSIGNED-BYTE8)EXT:READ-BYTE-SEQUENCEdata socket) (WITH-OPEN-FILE(out file :direction :output:ELEMENT-TYPE'() ;; write the whole file in one system call (UNSIGNED-BYTE8)EXT:WRITE-BYTE-SEQUENCEdata out)) data)))))) (DEFUNwput (host page file&OPTIONAL(port 80)) (WITH-OPEN-STREAM(socket (SOCKET:SOCKET-CONNECTport host:EXTERNAL-FORMAT:DOS)) (WITH-OPEN-FILE(in file :direction :inptut:ELEMENT-TYPE'() (UNSIGNED-BYTE8)LET*((length (FILE-LENGTHin)) (data (MAKE-ARRAYlength :element-type '())) ;; some servers may not understand the "Content-length" header (UNSIGNED-BYTE8)FORMATsocket "PUT ~A HTTP/1.0~%Content-length: ~D~2%" page length) (SETF(STREAM-ELEMENT-TYPEsocket) '() (UNSIGNED-BYTE8)EXT:READ-BYTE-SEQUENCEdata in) (EXT:WRITE-BYTE-SEQUENCEdata socket))) ;; not necessary if the server understands the "Content-length" header (SOCKET:SOCKET-STREAM-SHUTDOWNsocket :output) ;; get the server response (LOOP:for line = (READ-LINEsocket 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-SERVERs 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-SERVERs.
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 STREAMs, 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-STREAMs are bidirectional STREAMs (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 |