Hi, USOCKET users

Today I improved exist SOCKET-SERVER function in trunk, made it working for 
both TCP and UDP. This function can create a simple TCP or UDP server, just 
like similar facility found in some implementations. I'd like people try it and 
provide any suggestions on its interface.

First, please update your USOCKET trunk [1] to at least r542. The new 
SOCKET-SERVER has following definition:

(defun socket-server (host port function &optional arguments
                      &key in-new-thread (protocol :stream)
                           ;; for udp
                           (timeout 1) (max-buffer-size 
+max-datagram-packet-size+)
                           ;; for tcp
                           element-type reuse-address multi-threading)
  ...)

[required & optional arguments]

HOST: the local address you want to listen on, it could be something like 
"a.b.c.d" or #(a b c d) or integers. And if you want to listen on 0.0.0.0, you 
can use #(0 0 0 0), "0.0.0.0", or even NIL here.

PORT: a integer, means the port you want to listen. Notice: on UNIX platform, 
you cannot listen on port less than 1024 when you're not using "root" user.

FUNCTION: server handler function (will explain below)

ARGUMENTS: a list, which is used for providing extra arguments to above server 
handler function.

[keywords]

IN-NEW-THREAD: if T is provided, function SOCKET-SERVER will return 
immediately, and new server will be created in a new thread. Notice: you should 
have a CL implementation which support creating threads.

PROTOCOL: server type, could be :STREAM (for TCP, the default) or :DATAGRAM 
(for UDP)

TIMEOUT: UDP server loop interval, no need to be changed.
MAX-BUFFER-SIZE: UDP buffer size, use a smaller value when needed.
ELEMENT-TYPE: TCP handler's stream element type
REUSE-ADDRESS: TCP socket flag

MULTI-THREADING: if T is provided, the created TCP server will create new 
threads on each connection, causing the TCP server handling multiple 
connections at the same time. by default (NIL), only one client is supported. 
Notice: UDP server could always serve as many client as possible, using only 
one thread.



[Usage of TCP server]

TCP server is based on streams, that is, you provide a handler function which 
use "stream" as required argument. For a simple TCP server which output a 
string on connection (and then close), the handler function can be defined like 
this:

(defun simple-output-handler (stream)
  (format stream "Hello, world!~%"))

With above function, the way to create a TCP server (on port 0.0.0.0:10000) is:

(usocket:socket-server "0.0.0.0" 10000 #'simple-output-handler nil 
:in-new-thread t)

Here I use (:in-new-thread t) to make sure the server is created in background 
thread, and since no :multi-threading keyword is supplied, this server could 
only serve one client per time. But consider each connection only exist for a 
very short time (just print a string and end), so this won't be any problem.

Now I can try to connect to it using UNIX "telnet" command:

bin...@binghe-pro:~$ telnet 127.0.0.1 10000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello, world!
Connection closed by foreign host.

See, the server is working!

Sometimes, maybe we are using a general handler function which have additional 
arguments for customization, for example:

(defun general-output-handler (stream string)
  (format stream "~A~%" string))

Now suppose I want to create two different TCP server which different output 
string. How can I do this? See following:

(usocket:socket-server "0.0.0.0" 10001 #'general-output-handler (list "Hello at 
port 10001") :in-new-thread t)
(usocket:socket-server "0.0.0.0" 10002 #'general-output-handler (list "Hello at 
port 10002") :in-new-thread t)

This is how to use the optional ARGUMENTS argument! And let's test them:

bin...@binghe-pro:~$ telnet 127.0.0.1 10001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello at port 10001
Connection closed by foreign host.
bin...@binghe-pro:~$ telnet 127.0.0.1 10002
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello at port 10002
Connection closed by foreign host.

Isn't that perfect?


[Usage of UDP server]

UDP server is based on simple-array buffer. That is, what ever a client send to 
a UDP server, it will be stored into a vector which has type of (SIMPLE-ARRAY 
(UNSIGNED-BYTE 8) *). This vector is the input of your handler function, and 
you should return another vector as the data to send back to clients.

Let's create a simple UDP echo server. This means we don't need to modify 
anything, just return the buffer as is. So, function IDENTITY can be used 
directly:

(usocket:socket-server "0.0.0.0" 10000 #'identity nil :in-new-thread t 
:protocol :datagram)

We don't have a UNIX command to test it, so we use USOCKET:

? (defvar s (usocket:socket-connect "localhost" 10000 :protocol :datagram))
#<USOCKET:DATAGRAM-USOCKET #xC4C398E>
? (defvar buffer (coerce #(1 2 3) '(simple-array (unsigned-byte 8) *)))
BUFFER
? (usocket:socket-send s buffer 3)
3
? (usocket:socket-receive s nil 256)
#(1 2 3)
3
2130706433
10000

Notice: in calling of SOCKET-RECEIVE, last argument (256) means we want to 
receive at most 256 bytes. And see its four return values:

  #(1 2 3) means the return value by the UDP server, 
  3 means return buffer length. This is useful when you provide a bigger buffer 
but NIL to SOCKET-RECEIVE,
  2130706433 means "127.0.0.1", this is the server (remote) address
  10000 means server port.

For bigger example of UDP servers, see my CL-NET-SNMP project. A complicated 
SNMP server is working in the same way!


[Summary]

SOCKET-SERVER completely based on exist success of USOCET on providing a 
universal networking library. It's portable. I hope people could enjoy it.

Any question or suggestion are welcome.

Regards,

Chun Tian (binghe)
USOCKET maintainer

[1] svn://common-lisp.net/project/usocket/svn/usocket/trunk


_______________________________________________
usocket-devel mailing list
usocket-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/usocket-devel

Reply via email to