start-up-server &key function announce service process-name wait error => process , startup-condition
A function name.
An output stream,
t
,
nil
or a function.
An integer, a string or
nil
.
A symbol or expression.
A boolean.
A boolean.
The
start-up-server
function starts a TCP server. Use
mp:process-kill
to kill the server, and
comm:open-tcp-stream
to send messages from another client to the server.
The
function
argument provides the name of the function that processes connections. When a connection is made
function
is called with the connected socket handle, at which point you can make a stream using
make-instance
and communicate with the client. The server does not accept more connections until
function
returns, so normally it should create another light-weight process to handle the connection. However, the operating system typically provides a small queue of partially accepted connections, which prevents connection failure for new clients until the server is ready to accept more connections. If
function
is not specified the built-in Lisp listener server is used. See the examples section below.
If
announce
is a stream or
t
(denoting
*standard-output*
), a message appears on the stream when the server is started.
If
announce
is a function it is called when the server is started.
announce
should take two arguments:
socket
and
condition
.
socket
is the socket used by the server:
announce
can therefore be used to record this socket.
condition
describes the error if there is one.
announce
can be called with
socket
nil
and a condition only if
error
is
nil
. If the process is killed,
announce
is called with
socket
nil
and
condition
nil
.
The default for
announce
is
nil
, meaning there is no message.
If
service
is a string or positive integer, it specifies the name of the service. The location of the file specifying the names of services available varies, but typically on Windows 95 it is called
SERVICES
and is stored in the
Windows
directory, and on Windows NT it is the file
%SystemRoot%\system32\drivers\etc\SERVICES
If
service
is
nil
or 0, then
start-up-server
chooses a free port. The default value for
service
is "
lispworks
".
The process-name specifies the process name. The default is constructed from the service name in the following fashion:
(format nil "~S server" service)
The
wait
argument controls whether
start-up-server
waits for the server to start or returns immediately. When
wait
is non-
nil
and an error was signalled,
process
is
nil
and the error is returned in
startup-condition
Otherwise just one value, the server process, is returned. The default for
wait
is
nil
.
The
error
argument controls what happens if an error is signalled in the server thread. If
error
is
nil
then the thread is terminated. If
error
is non-
nil
then the debugger is entered. The default value for error is
(not
wait
)
.
Note: some versions of MS Windows fail to detect the case where more than one server binds a given port, so an error will not be raised in this situation.
The following example uses the built-in Lisp listener server:
(comm:start-up-server :service 10243)
It makes a Lisp listener server on port 10243 (check with local network managers that this port number is safe to use). When a client connects to this, Lisp calls
read
. The client should send a string using Common Lisp syntax followed by a newline. This string is used to name a new light-weight process that runs a Lisp listener. When this has been created, the server waits for more connections.
The next example illustrates the use of the
function
argument. For each line of input read by the server it writes the line back with a message. The stream generates
EOF
if the other end closes the connection.
(defvar *talk-port* 10244) ; a free TCP port number
(defun make-stream-and-talk (handle)
(let ((stream (make-instance 'comm:socket-stream
:socket handle
:direction :io
:element-type
'base-char)))
(mp:process-run-function (format nil "talk ~D"
handle)
'()
'talk-on-stream stream)))
(defun talk-on-stream (stream)
(unwind-protect
(loop for line = (read-line stream nil nil)
while line
do
(format stream "You sent: '~A'~%" line)
(force-output stream))
(close stream)))
(comm:start-up-server :function 'make-stream-and-talk
:service *talk-port*)
This is a client which uses the talk server:
(defun talking-to-myself ()
(with-open-stream
(talk (comm:open-tcp-stream "localhost"
*talk-port*))
(dolist (monolog
'("Hello self."
"Why don't you say something original?"
"Talk to you later then. Bye."))
(write-line monolog talk)
(force-output talk)
(format t "I said: \"~A\"~%"
monolog)
(format t "Self replied: \"~A\"~%"
(read-line talk nil nil)))))
(talking-to-myself)
=>
I said: "Hello self."
Self replied: "You sent: 'Hello self.'"
I said: "Why don't you say something original?"
Self replied: "You sent: 'Why don't you say something original?'"
I said: "Talk to you later then. Bye."
Self replied: "You sent: 'Talk to you later then. Bye.'"
This example illustrates a server which picks a free port and records the socket. The last form queries the socket for the port used.
(defvar *my-socket* nil)
(defun my-announce-function (socket condition)
(if socket
(setf *my-socket* socket)
(my-log-error condition)))
(comm:start-up-server :service nil
:error nil
:announce 'my-announce-function)
(multiple-value-bind (address port)
(comm:get-socket-address *my-socket*)
port)