Previous: Guile servers, Up: Writing servers [Contents][Index]
All of the servers listed in Existing servers are builtin servers. The following sections describe in detail how to setup a new builtin server type. This kind of server will be part of the Serveez executable. That is why you should make it configurable in the configure script via a ‘--enable-xxxserver’ argument.
Serveez is configured and built via automake and autoconf. That is why you are not supposed to write your own Makefiles but simplified Makefile.ams. Automake will automatically generate dependencies and compiler/linker command lines. Here are the steps you basically need to follow:
AC_OUTPUT
statement in configure.ac which is in the top
level directory. You have to put at least the following into the newly
created Makefile.am:
noinst_LIBRARIES = libfoo.a libfoo_a_SOURCES = foo-proto.h foo-proto.c INCLUDES = $(SERVEEZ_CFLAGS) -I$(top_srcdir)/src CLEANFILES = *~ MAINTAINERCLEANFILES = Makefile.in
This file contains at least your server’s extern
declaration of your
server definition which must be available from the outside.
The foo
server implements all kinds of
configuration items which can be integers, integer arrays, strings, string
arrays, port configurations, booleans and hash maps. Every item of the server
configuration can later be manipulated from the configuration file.
If you want to define default values for your configuration you have
to define them somewhere and put them into the default configuration
structure. This structure will be used to instantiate your server. For
this example we simply called it simply foo_config
.
In order to associate the configuration items in a server configuration
to keywords within the configuration file you have to define an
array of key-value-pairs. This is done in the foo_config_prototype
field. There are several macros which make different associations. These
are the SVZ_REGISTER_*
macros which take three arguments. The first
argument is the keyword which will occur in the configuration file, the
second is the associated item in your default configuration structure
and the last argument specifies if this item is defaultable or not.
The server definition is in a way the ‘class’ of your server. Together
with the default values (foo_config_prototype
) it serves as a
template for newly instantiated servers. The structure contains a long and a
short description of your server. The short name is used as the prefix for
all server instances of this specific type. The long description is
used in the control protocol (See Control Protocol Server.). The server
definition also contains the callbacks your server (mandatorily) provides.
There are several callback routines, which get called in order to instantiate the server and for describing the actual behaviour of your server. Here are the description of all of these callbacks. Some of them have to be implemented. Others have reasonable default values.
global initializer (optional)
This callback is executed once for every type of server. Here you can initialize data or whatever is shared between all instances of your server. For instance the HTTP server initializes its file cache here.
global finalizer (optional)
If you want to free shared resources which were possibly allocated within the global initializer you can do so here. The foo server frees its default hash previously allocated in the global initializer.
instance initializer (mandatory)
Within this routine you can initialize everything you might need for one instance of your server. The foo server does not do anything in this callback.
instance finalizer (optional)
The server instance finalizer gets its instance representation as argument. You have to free all resources used by this server instance.
protocol detection (mandatory)
Because it is possible to have more than one server listening on one network port we need to detect the type of client which is connecting to this port. The foo server checks if the first five bytes the client was sending is identifying it as a foo client. This routine is getting two arguments where the first one is a pointer to this servers instance and the second is the client socket object containing all information of the client connection. This structure is described a bit later. Be patient. For successful client detection return non-zero value.
socket connection (mandatory)
If the client detection signaled success this routine is called to assign
the client connection to the server instance. The arguments are just
the same as in the detection routine. In this callback you can assign
all the connection specific callbacks for your server and do some initial
things. The foo server sets the check_request
callback to the default
svz_sock_check_request
which is using the packet delimiter
information to find whole packets. When a client sent such a packet the
handle_request
callback is executed. That is why the foo
server assigns the handle_request
method.
client info (optional)
If this callback is given the control protocol (See Control Protocol Server.) can give information about a specific client if requested with ‘stat id NUM’. The first argument given is the server instance and the second one the client’s socket structure. You have to return a static single line character string.
server info (optional)
This function is called when listing the server instances via
‘stat all’ from the control protocol (See Control Protocol Server.).
The returned character string might be multilined separated by
\r\n
(no trailing separator). Usually you will return all the
server configuration information.
notifier (optional)
If this callback is not NULL
it is called whenever there is some time
left. It gets the server instance itself as argument. Actually it gets
called every second.
handle request (mandatory for UDP and ICMP servers)
The arguments to this callback are the client’s socket structure, the address of the packet data and its length. When implementing a UDP or ICMP server you need to return non-zero if your server could process the packet. Thus it is possible that there are multiple UDP servers on a single port.
You distribute your server by editing the cfgfile.c file in the
src/ directory. There you have to include the servers header
file and add the server definition by calling svz_servertype_add
The client connection information is stored within the svz_socket_t
object. All of the client connection specific callbacks get this object as
first argument. Following is a description of the most important elements
of this object.
int id
The socket id is a unique id for a client connection.
int version
This item validates this socket structure. If you pass the id
and
version
to a coserver you can check if the delivered socket
structure is the original or not within the coserver callback.
int proto
The proto
flag determines a server sockets protocol type which can
be PROTO_PIPE
, PROTO_TCP
, PROTO_UDP
,
PROTO_ICMP
or PROTO_RAW
.
int flags
The flag field of the client connection contains informations about the state of this connection. See socket.h in the src/libserveez/ directory for more information. Basically this bitfield specifies how this object is handled by the main server loop.
int userflags
This bitfield could be used for protocol specific information. You can use it for any information.
char *boundary, int boundary_size
If you are going to write a packet oriented protocol server you can use
the svz_sock_check_request
method to parse packets. These two
properties describe the packet delimiter.
char *send_buffer, int send_buffer_size, int send_buffer_fill
This is the outgoing data for a client connection object.
char *recv_buffer, int recv_buffer_size, int recv_buffer_fill
Within the receive buffer all incoming data for a connection object is stored. This buffer is at least used for the client detection callback.
int read_socket (svz_socket_t)
This callback gets called whenever data is available on the socket.
Normally, this is set to a default function which reads all available
data from the socket and feeds it to check_request
, but specific
sockets may need other policies.
int write_socket (svz_socket_t)
This routine is called when data is is valid in the output buffer and the socket gets available for writing. You normally leave this callback untouched. It simply writes as much data as possible to the socket and removes the data from the send buffer.
int disconnected_socket (svz_socket_t)
This gets called whenever the socket is lost for some external reason.
int connected_socket (svz_socket_t)
If some piece of code tries to connect to another host via
svz_tcp_connect
this connection might be established some time later.
This callback gets called when the socket is finally connected.
int kicked_socket (svz_socket_t, int)
We call this whenever the socket gets closed by us. The second argument specifies a reason.
int check_request (svz_socket_t)
This gets called whenever data was read from the socket. Its purpose is to check whether a complete request was read, and if it was, it should be handled and removed from the input buffer.
int handle_request (svz_socket_t, char *, int)
This gets called when the check_request
got a valid packet. The
request arguments contains the actual packet and the second argument is the
length of this packet including the packet delimiter.
int idle_func (svz_socket_t)
This callback gets called from the periodic task scheduler. Whenever
idle_counter
(see below) is non-zero, it is decremented and
idle_func
gets called when it drops to zero. idle_func
can
reset idle_counter
to some value and thus can re-schedule itself
for a later task.
int idle_counter
Counter for calls to idle_func
.
void *data
Miscellaneous field. Listener keeps array of server instances here.
This array is NULL
terminated. Some servers store server specific
information here.
void *cfg
When the final protocol detection
has been done cfg
should contain a pointer to the actual
configuration hash map taken from the server instance object.
Coservers are designed to complete blocking tasks. Each coserver runs in
its own thread/process. There are several coservers implemented: the dns,
reverse dns and ident coserver. You need to implement the
callback which gets called when a coserver completed its task. This
routine must be a svz_coserver_handle_result_t
. The first argument is
the actual coserver result which might be NULL
if the request
could not be fulfilled and the latter two arguments are the arguments
you specified yourself when issuing the request. To invoke a coserver
you use one of the svz_coserver_*
macros. The foo server uses
the reverse dns coserver to identify the host name of the remote client.
Previous: Guile servers, Up: Writing servers [Contents][Index]