This is the main documentation for Libnet. Most of it is relevant to
end-users, but some of it is more relevant to driver authors. You
should read the instructions in readme.txt
before this.
All parts of Libnet are Copyright © 1997-1999 Chad Catlett and George Foot.
This is edition 10 of the Libnet documentation, consistent with Libnet version 0.10.2.
Aims of this project:
Such an interface will enable people to write programs with networking capabilities without having to tie them down to particular types of network. The same program code will be able to use any supported network (e.g. Winsock, IPX, serial, ...) neither becoming cluttered nor requiring much effort to adapt.
Restricting the functionality means that more classes of network can be implemented and makes it easy for new users to get used to.
Naturally, writing a new driver will require an amount of research and testing; however, adding new drivers to the library should not be a painful process.
We can test whether the theory is workable, as well as creating sample programs to show people how to use the library. Hopefully they will then contribute missing drivers.
The Libnet library functions fall into the following categories:
These functions interact with the core of the library.
int net_init (void);
This function initialises the library, and should be called before any others.
This function returns 0 on success.
int net_register_driver (int num, NET_DRIVER *driver);
This function is primarily used internally by Libnet to register its own drivers, but it is a public function so you can use it too if you want to register custom drivers.
You should register any custom drivers before calling
net_loadconfig
(see net_loadconfig); otherwise they
won't get an opportunity to read the config file.
num is a unique reference number. Make sure it is unique! You
can check this by seeing whether or not a driver in the driver list
net_drivers_all
has the same number. Values from 0 to
NET_DRIVER_USER - 1
inclusive are reserved for Libnet's
use. You can use values from NET_DRIVER_USER
to
NET_DRIVER_MAX - 1
. If you specify 0, Libnet will allocate a
unique number on your behalf, out of its reserved range (subject to
availability).
driver is a pointer to the new driver's function table.
This function returns the number associated with your driver on success, or 0 on failure. (0 is a reserved driver number.)
int net_loadconfig (char *filename);
This loads a configuration file and invites the various drivers to extract information from it. See Configuration.
filename can be NULL
, a directory name, or a filename
(with or without an explicit directory).
If filename is NULL
the file libnet.cfg
is
read from the program's home directory (as in argv[0]
). If
filename is a directory then the file libnet.cfg
is loaded from that directory. If filename names a file,
that file is loaded.
This function returns 0 on success. Other return values:
errno
variable should be set, so you can
use perror
or make comparisons yourself.
errno
and/or perror
to find out why.
if (net_loadconfig(NULL)) { perror("Error loading config file"); exit (1); }
NET_DRIVERNAME *net_getdrivernames (NET_DRIVERLIST which);
This function gets the names of some or all of the drivers.
The which parameter specifies which drivers to query. The
type NET_DRIVERLIST
is defined in libnet.h
. You can
either pass a list you created yourself using the driver list
functions (see Driver List Functions), or the list
net_drivers_all
which contains all the drivers.
This function returns a pointer to an array of NET_DRIVERNAME
structs which contain the reference numbers and names of the
drivers specified by which, plus the nonet
driver
and a terminating entry with a NULL
pointer for the driver
name. This list is malloced, and should be freed by the caller.
NET_DRIVERNAME *names; int i; NET_DRIVERLIST drivers; /* Get names of all drivers */ names = net_getdrivernames (net_drivers_all); /* Print all entries in the array */ for (i = 0; names[i].name; i++) printf ("%d: %s\n", names[i].num, names[i].name); /* Free the array */ free (names); /* Get names of the Unix sockets and the Winsock driver driver */ /* So, first make a list containing them... */ drivers = net_driverlist_create(); /* creates empty list */ net_driverlist_add (drivers, NET_DRIVER_SOCKS); /* adds sockets driver */ net_driverlist_add (drivers, NET_DRIVER_WSOCK_WIN); /* adds Winsock driver */ /* ... and then pass it to the function */ names = net_getdrivernames (drivers); /* Print the names, as before, and free them */ for (i = 0; names[i].name; i++) printf ("%d: %s\n", names[i].num, names[i].name); free (names); /* We don't need the driver list any more */ net_driverlist_destroy (drivers);
For more real-life examples please refer to the test programs
tests/getdrvnm.c
and tests/gentest.c
, and the
client-server chat example in examples/chat
, where both
client.c
and server.c
use this function in a useful
way.
NET_DRIVERLIST net_detectdrivers (NET_DRIVERLIST which);
This function detects whether or not the given drivers can be used.
which is a driver list, as for net_getdrivernames
- see
net_getdrivernames, and also Driver List Functions. It
indicates which drivers to try to detect. net_drivers_all
can be given to detect all drivers.
The function returns a similar type showing which of the
specified drivers were actually detected. Note that if you
call this function several times (e.g. once for each driver
you want to detect) its return value only shows which of the
drivers you specified in which were detected; it does
not include drivers detected on previous calls. This
contrasts with the behaviour of net_initdrivers
(see net_initdrivers).
Note that the return value is valid only until the next call to this function (or until you shut down the library, of course). You don't need to destroy this list manually.
NET_DRIVERLIST drivers; NET_DRIVERNAME *names; int i; drivers = net_detectdrivers (net_drivers_all); names = net_getdrivernames (drivers); for (i = 0; names[i].name; i++) printf ("%d: %s\n", names[i].num, names[i].name); free (names);
NET_DRIVERLIST net_initdrivers (NET_DRIVERLIST which);
This function operates similarly to the net_detectdrivers
function (see net_detectdrivers), but it initialises the
specified drivers rather than detecting them.
which is a driver list as in net_detectdrivers
(see net_detectdrivers), indicating which drivers to attempt
to initialise. net_drivers_all
can be given to initialise
all drivers. Drivers will not be initialised unless they were
detected in a previous call to net_detectdrivers
. Previously
initialised drivers will not be reinitialised.
The function returns a list in the same format as its
argument, just as net_detectdrivers
does. Note that it
returns the complete list of initialised (i.e. ready-for-use)
drivers, not just those you specified in the call. Again, don't
destroy or modify this list. It is valid until the next call to
this function only.
NET_DRIVERNAME *names; NET_DRIVERLIST drivers, detected, initialised; int i; drivers = net_driverlist_create(); /* create the list for use later */ names = net_getdrivernames (net_drivers_all); for (i = 0; names[i].name; i++) { printf ("%d: %s ", names[i].num, names[i].name); net_driverlist_clear (drivers); net_driverlist_add (drivers, names[i].num); detected = net_detectdrivers (drivers); if (net_driverlist_test (detected, names[i].num)) { printf ("(detected) "); initialised = net_initdrivers (drivers); if (net_driverlist_test (initialised, names[i].num)) printf ("(initialised) "); } printf ("\n"); } free (names); /* Destroy the `drivers' list, but not the other lists. */ net_driverlist_destroy (drivers);
int net_shutdown (void);
Shuts everything down nicely, closing any open channels and
shutting down all initialised drivers. net_init
installs an exit function that calls this, so you don't
normally need to call it. You do need to call it if for
some reason you want to reinitialise the library with a
different driver set, maybe - for example, if you need
to reinitialise the drivers with a new config file.
Returns 0 on success.
These functions work with communication channels. Whenever you send or receive data, you do so through a channel. Each channel has an associated network type (which can't be changed after the channel is created), a local address (which is controlled by the driver) and a target address (which the user can change at will).
Channels are referred to through pointers to NET_CHANNEL
objects.
NET_CHANNEL *net_openchannel(int type, char *binding);
This function opens a communications channel for use over the specified network type.
type is one of the NET_DRIVER_*
constants, for
example it could be one of the set bits returned by
net_initdrivers
, or the num
entry for one of the
elements in a NET_DRIVERNAME
array.
binding determines the local binding for the channel. Pass
NULL
if you don't care. Otherwise, pass a string. The empty string
always causes a default binding to be used; otherwise the string's
meaning depends upon the driver in use.
This function returns a pointer to the NET_CHANNEL
struct it creates, or NULL
on error.
In Libnet versions before 0.9.13 this function did not have the
binding
parameter, and there was another function,
net_openinputchannel
. To make that code work with the new
API you need to change calls to these two functions:
net_openchannel (chan, NULL)
net_openchannel (chan, "")
The meaning of the binding
parameter may seem a bit
misty. As a general rule, if a channel is going to receive
first-contact data from other computers, you must specify
its binding. If it's going to be used to send/receive data
after initial contact has been established then its binding
doesn't matter.
As an analogy, let's consider a group of people who want to communicate through email. The people represent the computers in your game.
First imagine that none of the people know any of the
email addresses. Obviously, nobody can communicate. This
represents a situation where all channels were opened with
binding = NULL
.
Now suppose A knows B's email address. Then A can communicate with B in both directions, because as soon as A sends B an email, B can look at the return address to discover A's address. This represents a situation where computer B initialised a channel with a specific binding. A's channel did not need a specific binding, since he made first contact, not B.
Now for a more accurate analogy. Imagine each person has a whole domain to themself, but nobody knows which users exist at each domain. So nobody can send messages; this is the first scenario again.
In the second scenario, A knows that B has a user called
"default". So A can send email to that user from any of his
own users. And then B can send email back to whichever of A's
users have already sent email to B, from any of his [B's]
users. Again, only one of them needed to have a channel of
known binding. This represents the situation where B initialised
a channel with the empty string as binding
. He got the
default binding (i.e. "default" as username).
So why don't we initialise all channels with the default binding? Well, only one channel could then exist per computer (actually per network type per computer). You can't have two users both called "default".
Next consider the situation where B has two domains, one on net1 and one on net2, while A has only one, on net1, and C has only one, on net2. Assume that A and B are communicating and B and C are communicating; then B knows email addresses for A and C. Can A and C then communicate, if B tells them what each other's addresses are? No, because they're on different networks.
In this situation, B might want to explicitly bind channels to networks net1 and net2, rather than letting the driver make a (possibly) bad choice. This is a reason why you might want to let the user choose the binding. B is a gateway here, and this is a fairly unusual situation for a multiplayer game, but it can be a useful feature. An example of B is a machine on a LAN (which runs Internet Protocol), with a modem connection to the Internet itself. A is out on the Internet and C is on the LAN. In fact, the machine on which I am typing this is in this situation.
int net_closechannel(NET_CHANNEL *channel);
Closes a previously opened channel. This will not necessarily inform the remote machine; it will simply discard the channel record, after inviting the network driver responsible to tidy things up.
channel is the channel to close.
Returns 0 on success.
NET_CHANNEL *chan = net_openchannel (driver, binding); net_closechannel (chan);
int net_assigntarget(NET_CHANNEL *channel, char *target);
Sets the target of the given channel.
channel is the channel whose target address needs changing. target is the new target address. The format of the target address depends upon the network type being used by the channel.
Zero on success, nonzero on error (i.e. address in wrong format). A zero return does not indicate that the target can necessarily be reached.
NET_CHANNEL *chan = net_openchannel (NET_DRIVER_WSOCK, NULL); net_assigntarget (chan, "127.0.0.1:12345");
char *net_getlocaladdress(NET_CHANNEL *channel);
This function is used to discover the local address of a channel.
channel is the channel whose local address is wanted.
The address of channel is returned in the driver's normal address format.
local address means the address of the channel according to this computer. This might not be the address other computers should use; for example, a serial port driver would have no way of knowing what port the other computer should use. The Internet sockets drivers have a bit of trouble with this too, since a computer can have more than one IP address and it's not trivial to find out even one of these.
Because of all this, it's probably best to tell the user this local address and let them figure out what the other computer should use.
NET_CHANNEL *chan; chan = net_openchannel (driver, binding); printf ("Local address of channel: %s\n", net_getlocaladdress (chan));
int net_send(CHANNEL *channel,void *buffer,int size);
Sends data down a channel.
channel is the channel to send the data through. buffer points to the data to send. size is the size of the data in bytes.
Zero on success, non-zero on error.
See net_receive.
int net_receive(CHANNEL *channel,void *buffer,int maxsize,char *from);
Receives data from a channel.
channel is the channel to receive from. buffer
is a buffer to hold the data, of length maxsize. If
from is not NULL
, the address of the source of
the data will be stored in the buffer it points to (which
should be able to hold NET_MAX_ADDRESS_LENGTH characters).
Returns the number of bytes received. 0 is valid; there was no data to read. -1 indicates that an error occured.
NET_CHANNEL *chan; char buffer1[32] = "Data to send"; char buffer2[32] = ""; int x; chan = net_openchannel (NET_DRIVER_WSOCK, ""); net_assigntarget (chan, "127.0.0.1"); net_send (chan, buffer1, strlen (buffer1) + 1); do { x = net_receive (chan, buffer2, 32, NULL); } while (x == 0); if (x > 0) printf ("Received data: %s\n", buffer2); else printf ("Error receiving data.\n");
int net_query(CHANNEL *channel);
This function checks to see if there is data waiting to be read from the channel.
channel is the channel to query.
Returns nonzero if data is waiting, zero if not.
if (net_query (chan)) get_data(chan);
Libnet's channels are unreliable -- there's no guarantee that a packet will arrive at its destination, nor that packets won't get duplicated en route, nor that packets will arrive in the right order. If you bear those facts in mind, channels should be fine for most uses (in particular, cases where data is made redundant very quickly by new incoming data).
Sometimes though you want to be able to send a packet and be sure that it will reach its destination. Libnet's second type of communicator is the connection. A connection is a fixed link between two computers. You can't assign a new target. Packets sent along a connection are guaranteed to arrive precisely once, and in the correct order.
Conns are referred to through pointers to NET_CONN
objects.
NET_CONN *net_openconn (int type, char *binding);
Opens a conn over the specified network type.
type is the type of the network to use. binding can determine the local binding. See net_openchannel, for more information about the binding.
The function returns a pointer to the NET_CONN struct created, or NULL on error.
int net_closeconn (NET_CONN *conn);
Closes a previously opened conn.
conn is the connection to be closed.
Returns zero on success.
int net_listen (NET_CONN *conn);
Makes a conn start listening (waiting for connection attempts). Only works on an idle conn.
conn is the conn that should start listening.
Returns zero on success, nonzero otherwise.
NET_CONN *net_poll_listen (NET_CONN *conn);
Polls a listening channel for incoming connections. If there are any, this function accepts the first one queued and creates a new conn to talk to the connecting computer.
conn is the (listening) conn to poll.
If a new conn is created, this function returns a new NET_CONN * which the user can use to talk to the connecting computer. Otherwise NULL is returned.
int net_connect (NET_CONN *conn, char *addr);
Initiates a connection attempt. See also: net_connect_wait_time, net_connect_wait_cb, net_connect_wait_cb_time.
conn is the conn to connect; addr is the target address.
Returns zero if successful in initiating; nonzero otherwise. If
the return value is zero, the app should keep calling
net_poll_connect
until a connection is established or
refused, or until the app gets bored.
int net_poll_connect (NET_CONN *conn);
Polls a connecting conn to monitor connection progress.
conn is the (connecting) conn to poll.
Returns zero if the connection is still in progress, nonzero if the connection process has ended. A nonzero return value is either positive (connection established) or negative (connection not established).
int net_connect_wait_time (NET_CONN *conn, char *addr, int time);
This function uses net_connect
and net_poll_connect
to establish a connection. It waits until the connection process
is completed or the time runs out.
conn is the conn to connect with. addr is the target address. time is the time in seconds to wait before giving up.
Returns zero if the connection is established, negative if there is an error (e.g. connection refused) and positive if the time ran out.
int net_connect_wait_cb (NET_CONN *conn, char *addr, int (*cb)());
This function uses net_connect
and net_poll_connect
to establish a connection. It waits, calling the callback function
regularly (once per second), until the connection process is completed
or the callback function returns nonzero.
conn is the conn to connect with. addr is the target address. cb is the address of the callback function.
Returns zero if the connection is established, negative if there is an error (e.g. connection refused) and positive if the callback function returned nonzero.
int net_connect_wait_cb_time (NET_CONN *conn, char *addr, int (*cb)(), int time)
This function uses net_connect
and net_poll_connect
to establish a connection. It waits, calling the callback function
regularly (once per second), until the connection process is
completed, the time runs out, or the callback function returns
nonzero.
Note that if the callback function is time consuming, the time limit will be inaccurate.
conn is the conn to connect with. addr is the target address. cb is the callback function and time is the time in seconds to wait before giving up.
Returns zero if the connection is established, negative if there is an error (e.g. connection refused) and positive if either the time ran out or the callback function returned nonzero.
int net_send_rdm (NET_CONN *conn, void *buffer, int size);
Sends data down a conn. Analogous to net_send.
conn is the conn to send the packet down, buffer points to the data to send and size is the number of bytes to send.
Returning zero to indicate success or non-zero if an error occurs.
int net_receive_rdm (NET_CONN *conn, void *buffer, int maxsize);
Receives data from a conn. Analogous to net_receive.
conn is the conn to receive from. buffer points somewhere to store the packet, and maxsize is the maximum number of bytes to store.
Returns the number of bytes received. 0 is a valid return type; there was no data to read. -1 indicates that an error occured.
int net_query_rdm (NET_CONN *conn);
Tests whether data can be read from a conn. Analogous to net_query, but this function actually returns the size of the next queued packet.
conn is the conn to test.
The size of the next queued incoming packet, or 0 if no packets are queued.
if (net_query_rdm (conn)) process_data(conn);
int net_ignore_rdm (NET_CONN *conn);
If there are any incoming packets waiting to be read, this causes the first to be dropped; otherwise nothing happens. Note that the sender isn't notified, and will have received a confirmation of the packet's arrival. This function is intended for use if a large packet is in the queue and you weren't expecting to have to deal with it; call this function to remove the packet.
conn is the conn to operate on.
Non-zero if a packet was removed; zero if no packets were queued, or if an error occured.
char buffer[1024]; int size = net_query_rdm (conn); if (size > 0) { /* got some data */ if (size > sizeof buffer) /* too much data */ net_ignore_rdm (conn); else { net_receive_rdm (conn, buffer, sizeof buffer); ... } }
int net_conn_stats (NET_CONN *conn, int *in_q, int *out_q);
This function fills in *in_q and *out_q with the
numbers of packets in the incoming and outgoing queues for the
conn. If either pointer is NULL
, its will not be filled in.
I'm not entirely sure how useful this information is; maybe somebody can use it to optimise the way their game treats the network.
conn is the conn to test; in_q and out_q are
pointers to integers which, if not NULL
, will be filled
with the lengths of the incoming and outgoing queues respectively.
Zero on success.
int in_queue, out_queue; net_conn_stats (conn, &in_queue, &out_queue);
char *net_getpeer (NET_CONN *conn);
This function gives the address of the peer of this conn,
i.e. the computer at the other end. The conn must be in the
connected state (a return value from net_poll_listen
or
passed to a successful net_poll_connect
).
conn is the conn whose address will be returned.
A pointer to a static array is returned. Do not write through
the pointer. NULL
is returned on error.
printf ("Connection received from %s\n", net_getpeer (conn));
(not written yet)
This is an alphabetic list of all the interface functions of Libnet.
net_assigntarget
: net_assigntarget
net_closechannel
: net_closechannel
net_closeconn
: net_closeconn
net_connect
: net_connect
net_connect_wait_cb
: net_connect_wait_cb
net_connect_wait_cb_time
: net_connect_wait_cb_time
net_connect_wait_time
: net_connect_wait_time
net_conn_stats
: net_conn_stats
net_detectdrivers
: net_detectdrivers
net_getdrivernames
: net_getdrivernames
net_getlocaladdress
: net_getlocaladdress
net_getpeer
: net_getpeer
net_ignore_rdm
: net_ignore_rdm
net_init
: net_init
net_initdrivers
: net_initdrivers
net_listen
: net_listen
net_loadconfig
: net_loadconfig
net_openchannel
: net_openchannel
net_openconn
: net_openconn
net_poll_connect
: net_poll_connect
net_poll_listen
: net_poll_listen
net_query
: net_query
net_query_rdm
: net_query_rdm
net_receive
: net_receive
net_receive_rdm
: net_receive_rdm
net_register_driver
: net_register_driver
net_send
: net_send
net_send_rdm
: net_send_rdm
net_shutdown
: net_shutdown
The drivers fall into the following categories:
Fake drivers: These are dummy drivers that don't do much.
Internet drivers: These communicate over the Internet using UDP.
IPX drivers: These communicate over an IPX network.
Serial drivers: These communicate through a serial link.
Local host driver: This driver lets a program talk to itself.
Future drivers: These haven't been written yet.
NET_DRIVER_NONET
net_assigntarget
.
nonet
driver. It
exists to show implementors how to write new drivers. See the
source code (lib/drivers/template.c
), which is well
commented. If anything is not clear, please contact me so that
I can correct the problem.
NET_DRIVER_SOCKETS
It has been tested at various times on Linux (i386), OSF1
(DEC Alpha) and FreeBSD.
NET_DRIVER_WSOCKWIN
It has been tested with RSXNTDJ+DJGPP and MSVC++.
NET_DRIVER_WSOCK_DOS
It has been tested with DJGPP but this was some time ago (i.e.
before the author used Windows 98).
NET_DRIVER_IPX_DOS
It has been tested on DOS and a DOS box under Windows 95/98.
NET_DRIVER_SERIAL_DOS
NET_DRIVER_LOCAL
It will work on all platforms.
DOS-based Internet drivers via PPP and through network cards were once being worked on by Ove Kaaven. Currently the only way to play over the Internet from plain DOS using Libnet is to run Kali, which emulates IPX over an Internet connection. You still need to have Internet software for DOS. Libnet's IPX driver should be able to use this emulated IPX, but it hasn't been tested yet. If you try it, please let me (gfoot) know how you get on.
Any other useful drivers would be much appreciated. See Contacting the Authors.
The library will work without config files by using hardwired defaults, but if you want to use other settings config files are what you need.
The system for config files was designed to allow other non-Libnet data to be included in the same file. It uses a similar system to many other packages, like Windows' *.ini files.
[
,]
) and ends with the next such
title
In fact what goes on inside the sections is entirely up to the driver that owns the section; the above system is recommended though.
Libnet drivers won't mind at all if you put garbage at the start or end of the file provided you don't duplicate any of its section headings and the trailing garbage has a section name separating it from the last Libnet driver.
For full details of what sections each driver looks for and which settings within those sections it recognises please see the drivers' documentation.
To load a config file you use the net_loadconfig
function,
before calling net_init
,
passing it one of:
NULL
For [b] and [c] a default filename of libnet.cfg
will be
used. For [c] the file will be checked for in various platform-dependent
locations (e.g. the current directory, or the directory containing the
program, or the user's home directory).
libnet.h
declares several structs -- NET_CHANNEL
,
NET_CONN
,
NET_DRIVER
and NET_DRIVERNAME
. NET_CHANNEL
and NET_CONN
are
used by user programs and internally to hold information about
channels and conns; the user doesn't see inside the struct.
NET_DRIVER
is used internally to hold information
about network drivers. NET_DRIVERNAME
is used to hold a
driver's reference number and name; an array of these is returned
by the net_getdrivernames
function.
The definitions of these structs are in lib/include/internal.h
.
Beware that these definitions may change from version to version;
it's best not to use them in user programs. If you really need
something and think the API should publicise it, let me (gfoot) know
and we can talk about extending the API.
The documentation about the structs has not yet been converted to Texinfo format.
Here are some possible enhancements for the future. Other suggestions for improvement are of course always welcome. See Contacting the Authors.
NET_CHANNEL
struct if data is waiting. The
user program could then issue one query call, and afterwards
just check this flag.
Please contact the authors via the netgame mailing list (see below).
The netgame mailing list was created in Spring 1998. You can discuss any aspect of networked game programming. Libnet discussion is on topic, but the list is not just about Libnet. The list does not generate a lot of traffic, but there are people on the list with experience writing games using Libnet and other libraries, including the authors of Libnet, so asking this mailing list is better than asking the authors directly.
To subscribe to the mailing list, please send an email to listserv@canvaslink.com with no subject, putting in the body of the message:
subscribe netgame name
replacing name
with your name. You'll be sent more
information about the mailing list when your subscription is
processed.
The administrator of this mailing list is George Foot.
NET_DRIVER_IPX_DOS
: ipxdos
NET_DRIVER_LOCAL
: localhost
NET_DRIVER_NONET
: nonet
NET_DRIVER_SERIAL_DOS
: serialdos
NET_DRIVER_SOCKETS
: socks
NET_DRIVER_WSOCK_DOS
: wsockdos
NET_DRIVER_WSOCK_WIN
: wsockwin