CONCEPT
Sockets and the socket daemon
DESCRIPTION
If you want to connect to another computer on the internet
you would like to use sockets. Sockets are not part of the
driver so there has to be someting in the lib. Here it
is: the socket daemon.
It helps you to create and maintain state of your connection
with various protocols and different computers.
What sockets are and how you use them is not subject of this
text. Maybe the examples help you but be encouraged to read
some real text on them.
Creating a socket
~~~~~~~~~~~~~~~~~
You could create internet domain sockets with tcp or udp
protocol. Listening and sending of data is possible. Also
there is on protocol overlying tcp available: mudmode.
To create a socket you call the daemon which will return you
a socket object. All further handling is done by that
object:
#include <daemon/socket.h>
sock_ob = (object) SOCKETD->New(...);
The full prototype of New() is as follows:
object New(int type, string host, int port, closure cb, int opts)
The Type is one of:
TCP_OPEN An outgoing tcp connection.
TCP_LISTEN Listen on a tcp port for incoming connections.
UDP An incoming or outgoing udp connections.
The Host of the destination could be specified as an IP address
like "139.18.11.86" or via a DNS name which will be resolved
first "wl.mud.de". Note that it is used only for TCP_OPEN and has
to be zero for the other types.
The Port is an Integer:
TCP_OPEN The port on the remote computer we want to
connect to. Its not possible to choose the packet's
source port, a random number will be used.
TCP_LISTEN The port on which we will await connections.
UDP The port on which we will listen and also the
port to send packets _from_.
It is not possible to send udp packets without
listening at the port we want as source.
The callback closure cb gets all the data and status or error
messages of that socket. It is described further down.
The Opts may be
SOCKET_ASCII which is also the default. Its not possible to
receive data with embedded \000 which are filtered.
You will get the data as strings.
SOCKET_BINARY Here is all data possible. Because its not (yet)
possible to have \000 in strings in lpc you will
get the data as array of chars (ints).
SOCKET_MUDMODE This is a layer over TCP whichs allows you to send
complex LPC data types rather than limited strings.
The receiver is most likely also a mud where the
same data is reconstructed. It is possible to send
mapbe ({ 3, 3.0, "hi" }) over the network and the
receiver will just get that (the mixed* array)
rather than a string. Mind that it is not possible
to send objectpointer. Also be careful with
double references which are not guaranteed to be
restored correctly.
Using a socket
~~~~~~~~~~~~~~
To handle the socket you could call the following functions
in your socket object. They will return 1 if all seems to
be correct but real errors are reported via callback.
int Write(mixed data)
The given data is written to the socket. If nothing goes wrong
it will end up on the far partner's computer. The data may
be a string or an array of integers (which may contain zeros).
If the socket is in SOCKET_MUDMODE any lpc structure may be
written (without object pointer).
Note that SOCKET_ASCII/SOCKET_BINARY is not interpreted here.
int Send(string host, int port, mixed msg)
This is the same but used on UDP sockets only. Because UDP is
connectionless you have to give your destination on each Send().
Host and port are specified like for New().
The UDP packet size is limited to MAX_SEND bytes. If the msg
requires more space (quite unpredictable in MUDMODE) it is
split into several packages. The receiver must handle that
or you should not send that big a message.
Sending MUDMODE UDP is not (yet) possible.
object Accept(closure acc_cb)
Opens a pending connection on a listend to socket. You have to
call it in the main listen socket object.
It returns you a new socket object for that connection.
All data and status messages of the new connection will be
handled via acc_cb, which may be the same function as your
main callback function.
The listening socket could be closed after the new connection
is fully established, if you want to handle only one connection
at a time.
int Close()
The connection is ended and the socket object removed.
If you use remove() on the socket object an emergency Close()
is called but it is better to do this right and never call
remove() on the socket object.
mixed* State()
Returns you an array with Information on that connection.
S_SOCKOBJ The object itself (really useless info, isn't it?)
S_TYPE The type of the socket. Additional to TCP_OPEN,
TCP_LISTEN and UDP there is TCP_ACCEPT. That is the
type of accepted connections.
S_TICKET An integer array holding the ticket of that object
for xerq communication. Not needed in normal cases.
S_STATE The state of the socket:
S_UNCONNECTED No connection (yet) maybe we wait
for the DNS resolving.
S_CONNECTED We are connected (yeah!).
S_LISTEN The socket listens for connections.
S_UDP The socket listens for connections
and we may send packages out.
S_CLOSING The socket is or will be closed in
short time. Don't do anything but
clear up anymore.
S_OPTS The opts as described above. Mind that the opts
are bits and setting SOCKET_MODMODE will set
SOCKET_BINARY automatically.
S_HOST The remote host.
S_PORT The remote port.
S_LPORT The local port.
S_CALLBACK The callback closure.
S_OBJECT The object the callback closure is bound to. This
is the only one which could call the functions in
the socket object.
S_PENDING Pending data or number of data to be sent. Don't use.
(It's the preconnection data buffer.)
S_OWNER The uid of S_OBJECT.
S_LISTEN_FD Used internally on accepted connections. The file
descriptor of the listening socket. Is overwritten
with S_PORT after connect.
S_TO_WRITE How many packets are to be written to the erq.
Note that the erq could handle only small chunks of
data so this is not the number of Write()s or
Send()s.
S_WRITTEN How many of the S_TO_WRITE packets were acknoledged
by the erq with SOCKET_WRITTEN. If both numbers are
equal all data should be in the erq's queue.
S_RECEIVED How many data packets were received from the erq.
S_WAITS The sockets wait state as follows:
S_WAITS_NOT no waiting
S_WAITS_ERQ packet send to erq, waiting for reply
S_WAITS_INCOM erq replied SOCKET_INCOMPLETE,
waiting for socket to unblock
S_WAITS_BLOCK erq replied ERQ_E_WOULDBLOCK
waiting for socket to unblock
The callback system
~~~~~~~~~~~~~~~~~~~
Whenever something happens to your sockets it will be send to
your callback closure. It should have the following prototype:
void cb(object sock_obj, int action, extra1, extra2, extra3)
The sock_obj specifies the connection on which the action took
place. This is handy if you handle multiple sockets over one
callback.
The following actions exist:
SOCKET_READY: The socket is ready to receive or send data.
No extra info is given except when the socket
is one opened by Accept():
extra1: The listening socket object (stupid).
extra2: The remote host (string).
extra3: The remote port (int).
SOCKET_ACCEPT: A listen port detected an incoming connection.
you could accept it with Accept().
SOCKET_READ: Incoming data is ready.
extra1: The data.
extra2: The remote host (UDP only).
extra3: The remote port (UDP only).
SOCKET_WRITTEN: Some data was sent out successful.
Note that your data will be chunkized so that
one Write() may result in several callbacks.
If all pending data is written, extra1 is
set to 1.
extra1: All data written if set.
SOCKET_INCOMPLETE: Not all data could be written. The xerq is
buffering the rest and will notify us with
SOCKET_WRITTEN if the writing is complete.
This is informational only, you do not need
to take any action or notice.
extra1: Amount of data of the actual chunk
which could be written.
extra2: Amount of data of whole LPC chunk
which could be written.
Note that there might be several such callbacks
until the packet is written completely.
SOCKET_CLOSE: This is called when the connection is closed
either from you or the other side.
SOCKET_ERROR: An error occured.
extra1: Short error message (of the xerq)(string)
extra2: The xerq packet with the error or zero
extra3: Description of the error ERQ_E_UNKNOWN.
If extra2 is an array (of int) you will find the
xerq error code in extra2[0]. See erq(C) also.
SOCKET_NOPEND: You tried to accept on a listening socket
where no pending connection is (anymore). This
is sent to the socket object you got from the
Accept(). That object will be destroyed
afterwards.
Principle of operation
~~~~~~~~~~~~~~~~~~~~~~
Here is a raw sketch of how sockets are supposed to work.
All three szenarios are described. Its only a sketch NOT code!
* outgoing connection
socket = New(TCP_OPEN)
callback SOCKET_READY
socket->Write("hello\n")
callback SOCKET_WRITTEN 1
callback SOCKET_READ, "hi you\n"
socket->Close()
callback SOCKET_CLOSE
* incoming connection
socket = New(TCP_LISTEN)
callback SOCKET_READY
callback SOCKET_ACCEPT
sock_2 = socket->Accept()
callbac2 SOCKET_READY
sock_2->Write("hello\n")
callbac2 SOCKET_WRITTEN 1
callback SOCKET_ACCEPT (but we ignore that)
callbac2 SOCKET_READ, "hi you\n"
sock_2->Write("bye\n");
sock_2->Close();
socket->Close();
callbac2 SOCKET_WRITTEN 1 (see the callbacks are
callbac2 SOCKET_CLOSE asynchronous)
callback SOCKET_CLOSE
We could have done sock_3 = socket->Accept() after the second
SOCKET_ACCEPT to handle to connections at a time.
* UDP connection
socket = New(UDP)
callback SOCKET_READY
socket->Send("whos there", host1, port1);
callback SOCKET_WRITTEN 1
socket->Send("and there?\n\n", host2, port2);
callback SOCKET_WRITTEN 1
callback SOCKET_READ, "hi you\n", host1, port1
socket->Close()
callback SOCKET_CLOSE
SEE ALSO
erq(C)
|