This is libnet-HOWTO.info, produced by makeinfo version 4.0 from
libnet-HOWTO.texi.
File: libnet-HOWTO.info, Node: Top, Next: About this document, Prev: (dir), Up: (dir)
This is a draft version of tutorial for Libnet. 17 March 2001.
Copyright (C) 2001 Peter Wang. Distribute freely.
* Menu:
* About this document::
* Introduction to Libnet::
* Using Libnet::
* Channels::
* Conns::
* An example:: Doesn't exist yet
* Network limitations::
* What now?::
* Function Index::
File: libnet-HOWTO.info, Node: About this document, Next: Introduction to Libnet, Prev: Top, Up: Top
About this document
*******************
This document is a introductory tutorial for using the networking
library Libnet , by George Foot et
al. It is designed supplement the Libnet documentation, as some people
have trouble figuring out where to start. Most of it was lifted from
the Libnet documentation, which is very good anyway.
The difference is that this text is designed to be read from start to
finish, like a book. It is organised into chapters, with a small
example at the end of each to demonstrate the functions described. It
is hoped that this rearrangement will make it easier to learn the Libnet
API. Once you have read this, you should consult the Libnet
documentation for reference. Also, many aspects of Libnet will be
completely missed by this tutorial.
As always, suggestions and improvements are welcome. Questions about
this tutorial are also welcome, but questions about Libnet itself should
be sent to the Libnet mailing list.
My email address is
Note: You are reading the first draft. Right now, this document is
probably pretty worthless as a tutorial. Please help it improve by
sending me any questions you want answered, or which parts you think
require more explaination, and whatever else you can think of.
File: libnet-HOWTO.info, Node: Introduction to Libnet, Next: Using Libnet, Prev: About this document, Up: Top
Introduction to Libnet
**********************
Libnet is a low-level networking library, designed in particular for
adding network support to games. In essence, it is one large
abstraction layer, sitting atop the networking capabilities of the
operating system.
The question is then, why use Libnet if it just wraps around other
libraries? The simplest answer is, of course, that it is
cross-platform. If you write a cross-platform program under one
operating system, you can port it easily to another operating system.
If you do not care about that, you are an idiot, and you should not be
reading this.
Another reason is that Libnet can provide one single programming
interface to many different types of network interfaces. For example,
right now there are drivers for networking over the Internet, IPX
networks, serial ports, as well as a special "localhost" driver.
Others can be added quite easily.
File: libnet-HOWTO.info, Node: Using Libnet, Next: Channels, Prev: Introduction to Libnet, Up: Top
Using Libnet
************
Before we can use Libnet, there are some things to know.
* Libnet is a C library;
* the header file to include is `libnet.h';
* the library to link with is `libnet.a' or `libnet.lib', etc.
depending on your compiler's conventions.
I will assume Libnet is compiled and installed properly on your system.
* Menu:
* Initialising::
* Shutting down::
* Using Libnet example::
File: libnet-HOWTO.info, Node: Initialising, Next: Shutting down, Prev: Using Libnet, Up: Using Libnet
Initialising
============
Before we can use Libnet, we must initialise it.
- Function: int net_init (void)
This function initialises the library, and should be called before
any others. It returns zero on success.
Usually, we want to load some configuration data as well.
- Function: int net_loadconfig (const char *FILENAME)
Loads configuration data from a file.
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.
The function returns zero on success.
Libnet supports many network drivers. There are fairly complicated ways
to initialise the drivers you want to use (which I will not describe).
The simplest way, however, is to call:
net_detectdrivers (net_drivers_all);
net_initdrivers (net_drivers_all);
This will detect all the network drivers that available for use on your
particular system, and initialise them all, which is what you usually
want to do.
File: libnet-HOWTO.info, Node: Shutting down, Next: Using Libnet example, Prev: Initialising, Up: Using Libnet
Shutting down
=============
`net_init' installs an exit function, so you do not usually need to
shut Libnet down explicitly. However, if you want to reinitialise it
for any reason, you must call `net_shutdown' before calling `net_init'
again.
- Function: int net_shutdown (void)
Shuts everything down nicely, closing any open channels and
shutting down all initialised drivers.
File: libnet-HOWTO.info, Node: Using Libnet example, Prev: Shutting down, Up: Using Libnet
Chapter example
===============
#include
int main ()
{
/* Errors in initialisation are very unlikely,
* so we don't check for it. */
net_init ();
net_loadconfig (NULL);
/* Detect and initialise drivers. */
net_detectdrivers (net_drivers_all);
net_initdrivers (net_drivers_all);
/* Do nothing. */
/* Quit. */
return 0;
}
File: libnet-HOWTO.info, Node: Channels, Next: Conns, Prev: Using Libnet, Up: Top
Channels
********
Communications "channels" in Libnet are unreliable connections between
two machines. This means that a message sent through a channel may be
lost during transmission, be duplicated, or arrive in incorrect order.
Despite all that, there are many cases where channels will be
sufficient.
* Menu:
* Opening a channel::
* Assigning a target::
* Getting the local address::
* Sending data on channels::
* Receiving data on channels::
* Closing a channel::
* Channels example::
File: libnet-HOWTO.info, Node: Opening a channel, Next: Assigning a target, Prev: Channels, Up: Channels
Opening a channel
=================
Channels are represented with opaque objects of type `NET_CHANNEL', and
are created with the following function:
- Function: NET_CHANNEL * net_openchannel (int TYPE, char *BINDING)
Opens a communications channel.
TYPE is one of the `NET_DRIVER_*' constants, and determines the
network driver you want to use. You may use hard-coded constants,
or the network driver classes system (recommended, but you'll need
to look in the `classes.c' example, as it is not documented yet).
BINDING determines the local binding for the channel. This
requires some explanation. It can take three types of arguments:
* You may specify `NULL', in which case, you are saying: "I
don't care what address I get, since I will be initiating the
contact sequence, and I can tell the remote side my address
later". This is designed for a client.
* You may specify `""' (i.e. the empty string), in which case,
you are saying "Give me the default address for this driver
type, as I want other people to be able to find me easily."
This is designed for a server.
* You may specify a specially formatted string (the format
depends on the driver and network type you are using). In
this case, it is used to say "I want this channel to be at
address _XYZ_, so that when other people want to contact me,
they can point at _XYZ_."
If you find all that confusing, the analogy in the Libnet
documentation may help. Here it is again, in less accurate but
less confusing terms:
Imagine you have just received an email account at your new ISP.
Other people cannot contact you yet, because they do not know your
email address. You must send the first message. Once you have
done that, the receiver of the message may reply by looking at the
`From' header in the email message. This is like the first case
above.
That is fine if you know the other person's address, but what if
nobody knows anyone else's email address? The answer to this is
to have a "default" address. If you don't know where to send a
message, then the default address is a good guess. This is like
the second case.
The analogy is quite broken already, so I will not try to explain
the third case here. Don't worry, you'll hardly ever use it.
The function returns a pointer to the `NET_CHANNEL' struct it
creates, or `NULL' on error.
File: libnet-HOWTO.info, Node: Assigning a target, Next: Getting the local address, Prev: Opening a channel, Up: Channels
Assigning a target
==================
When a channel is opened, it does not point anywhere. If you try and
send data through the channel, it will not know where it it supposed to
go. The following function assigns a target for a channel:
- Function: 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 TARGET depends on
the network type being used by the channel.
Returns zero on success, non-zero on error (i.e. address in wrong
format). A zero return does not indicate that the target can
necessarily be reached.
Example
-------
NET_CHANNEL *chan = net_openchannel (NET_DRIVER_WSOCK, NULL);
net_assigntarget (chan, "127.0.0.1:12345");
File: libnet-HOWTO.info, Node: Getting the local address, Next: Sending data on channels, Prev: Assigning a target, Up: Channels
Getting the local address
=========================
Sometimes it is useful to get the local address of a channel. The
following function will do that:
- Function: char * net_getlocaladdress (NET_CHANNEL *CHANNEL)
Returns the local address of CHANNEL.
Note that "local address" means the address of the channel
according to this computer. For example, the Internet sockets
drivers have a bit of trouble with this, 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.
File: libnet-HOWTO.info, Node: Sending data on channels, Next: Receiving data on channels, Prev: Getting the local address, Up: Channels
Sending data
============
Once the channel is opened and a target is assigned, we can now send
some data.
- Function: int net_send (CHANNEL *CHANNEL, void *BUFFER, int SIZE)
Sends some data through 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. It
returns zero on success, but this does not mean that the data was
received on the remote side.
File: libnet-HOWTO.info, Node: Receiving data on channels, Next: Closing a channel, Prev: Sending data on channels, Up: Channels
Receiving data
==============
We need a way to check if there is data waiting to be received on a
particular channel. The following function does just that:
- Function: int net_query (CHANNEL *CHANNEL)
Returns non-zero if there is data pending in CHANNEL.
We also need a way to actually receive the data.
- Function: int net_receive (CHANNEL *CHANNEL, void *BUFFER, int
MAXSIZE, char *FROM)
Receive some 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 means there was no data to
read. -1 indicates that an error occured.
File: libnet-HOWTO.info, Node: Closing a channel, Next: Channels example, Prev: Receiving data on channels, Up: Channels
Closing a channel
=================
When you are done with a channel, you must close it.
- Function: int net_closechannel (NET_CHANNEL *CHANNEL)
Closes the specified channel.
File: libnet-HOWTO.info, Node: Channels example, Prev: Closing a channel, Up: Channels
Channels example
================
/* Demonstrates the channel functions. This example is
* a lot like one found in the Libnet package. */
#include
#include
#include
/* Use the driver of your choice.
* e.g. NET_DRIVER_WSOCK_WIN under Windows. */
#define DRIVER NET_DRIVER_SOCKETS
int main (int argc, char *argv[])
{
NET_CHANNEL *chan;
char *binding;
int server = 0;
char buf[256];
/* Search command-line arguments for a "-s" option. If
* found then we will run as the server, otherwise as the
* client. (Not that it makes much difference here). */
for (argv++; *argv; argv++)
if (strcmp (*argv, "-s") == 0)
server = 1;
net_init ();
net_loadconfig (NULL);
net_detectdrivers (net_drivers_all);
net_initdrivers (net_drivers_all);
/* If we are the server we want to have the "default"
* address (i.e. binding should be the empty string).
* If we are the client, our address doesn't matter
* (i.e. the binding should be NULL). */
if (server)
binding = "";
else
binding = NULL;
/* Open the channel. */
chan = net_openchannel (DRIVER, binding);
if (!chan) {
puts ("Error opening channel.");
return 1;
}
/* Show the local address. */
printf ("Local address: %s\n", net_getlocaladdress (chan));
/* Assign a target. */
puts ("Enter target address:");
fgets (buf, sizeof buf, stdin);
if (net_assigntarget (chan, buf) != 0) {
puts ("Could not use that address; quitting.");
return 1;
}
puts ("Enter text to send. Enter `.' to quit.");
while (1) {
/* Check if the remote side sent us any messages. */
while (net_query (chan))
/* If so, receive them and print them out. */
if (net_receive (chan, buf, sizeof buf, NULL) > 0)
printf ("Received: %s\n", buf);
/* Let user enter something. */
if (!fgets (buf, sizeof buf, stdin))
break;
buf[strlen (buf) - 1] = 0; /* strip newline */
/* Quit condition. */
if (strcmp (buf, ".") == 0)
break;
/* Send the text if it is not a blank line. */
if (buf[0])
net_send (chan, buf, strlen (buf) + 1);
}
/* Close the channel. */
net_closechannel (chan);
/* Quit. */
return 0;
}
File: libnet-HOWTO.info, Node: Conns, Next: An example, Prev: Channels, Up: Top
Conns
*****
"Conns" are like channels, but they are reliable. Messages will reach
the destination precisely once, and in the correct order. If a cable
is disconnected, however, there's nothing we can do about it.
* Menu:
* Opening a conn::
* Connecting conns::
* Sending data on conns::
* Receiving data on conns::
* Ignoring data::
* Closing a conn::
* Conns example::
File: libnet-HOWTO.info, Node: Opening a conn, Next: Connecting conns, Prev: Conns, Up: Conns
Opening a conn
==============
Like channels, we must open conns before we can use them. Conns are
represented by the opaque data type `NET_CONN'.
- Functions: 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. *Note `net_openchannel': bindings.
Returns a pointer to the `NET_CONN' struct created, or `NULL' on
error.
File: libnet-HOWTO.info, Node: Connecting conns, Next: Sending data on conns, Prev: Opening a conn, Up: Conns
Connecting conns
================
Unlike channels, conns have a defined connection process. You cannot
send data from one conn to another until they have gone through the
handshaking process, and afterwards cannot connect to another conn
without disconnecting first.
The connection process is very different for servers and clients, so
they will be discussed separately.
The server
----------
The server is the one waiting for clients to connect to it. To do this,
we open a new conn, then set it up to be a "listening" conn, using this
function:
- Function: int net_listen (NET_CONN *CONN)
Makes a conn start listening (waiting for connection attempts).
Only works on an idle conn. Returns zero on success, nonzero
otherwise.
We should check regularly if a client is trying to contact us through
this listening conn, using this function:
- Function: 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. If a new conn is
created, it is returned. Otherwise `NULL' is returned.
Once the new conn is returned, we can begin to send and receive data
through it. The listening conn will continue to listen for new
connections.
The client
----------
The client needs to contact the listening conn of the server, using the
following functions:
- Function: int net_connect (NET_CONN *CONN, char *ADDR)
Initiates a connection attempt.
CONN is the conn to connect; ADDR is the target address (which is
network driver dependent).
Returns zero if successful in initiating; non-zero 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.
- Function: int net_poll_connect (NET_CONN *CONN)
Polls a connecting conn to monitor connection progress.
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).
File: libnet-HOWTO.info, Node: Sending data on conns, Next: Receiving data on conns, Prev: Connecting conns, Up: Conns
Sending data
============
Once the connection process is complete, data can be sent with the
following function.
Note: RDM stands for "Reliably Delivered Message".
- Function: int net_send_rdm (NET_CONN *CONN, void *BUFFER, int SIZE)
Sends data down a conn. Analogous to *Note net_send::.
File: libnet-HOWTO.info, Node: Receiving data on conns, Next: Ignoring data, Prev: Sending data on conns, Up: Conns
Receiving data
==============
We also need a way to check if data has arrived in a conn, and then to
retrieve it.
- Function: int net_query_rdm (NET_CONN *CONN)
Tests whether data can be read from a conn. Analogous to *Note
net_query::, but this function actually returns the size of the
next queued packet.
- Function: int net_receive_rdm (NET_CONN *CONN, void *BUFFER, int
MAXSIZE)
Receives data from a conn. Analogous to *Note net_receive::.
File: libnet-HOWTO.info, Node: Ignoring data, Next: Closing a conn, Prev: Receiving data on conns, Up: Conns
Ignoring data
=============
Sometimes data will arrive in a conn, but we don't actually want it.
- Function: 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.
Returns non-zero if a packet was removed; zero if no packets were
queued, or if an error occured.
File: libnet-HOWTO.info, Node: Closing a conn, Next: Conns example, Prev: Ignoring data, Up: Conns
Closing a conn
==============
After you are done with a conn, you must close it with `net_closeconn'.
- Functions: int net_closeconn (NET_CONN *CONN)
Closes a previously opened conn.
File: libnet-HOWTO.info, Node: Conns example, Prev: Closing a conn, Up: Conns
Conns example
=============
/* Demonstrates the conn functions. This example was adapted
* from the channel example. */
#include
#include
#include
/* Use the driver of your choice.
* e.g. NET_DRIVER_WSOCK_WIN under Windows. */
#define DRIVER NET_DRIVER_SOCKETS
int main (int argc, char *argv[])
{
NET_CONN *listen;
NET_CONN *conn;
int server = 0;
char buf[256];
/* Search command-line arguments for a "-s" option. If
* found then we will run as the server, otherwise as the
* client. (Not that it makes much difference here). */
for (argv++; *argv; argv++)
if (strcmp (*argv, "-s") == 0)
server = 1;
net_init ();
net_loadconfig (NULL);
net_detectdrivers (net_drivers_all);
net_initdrivers (net_drivers_all);
if (server) {
/* If we are the server, open a listening conn and wait
* for a client. */
listen = net_openconn (DRIVER, "");
if (!listen) {
puts ("Error opening conn.");
return 1;
}
if (net_listen (listen) != 0) {
puts ("Error making conn listen.");
return 1;
}
puts ("Awaiting connection...");
do conn = net_poll_listen (listen);
while (!conn);
/* We could keep the listening conn around and connect
* to many clients at once, if we wanted to. */
net_closeconn (listen);
}
else {
int status;
/* If we are the client, open a conn and connect to the
* server's listening conn. */
conn = net_openconn (DRIVER, NULL);
if (!conn) {
puts ("Error opening conn.");
return 1;
}
puts ("Enter target address:");
fgets (buf, sizeof buf, stdin);
buf[strlen (buf) - 1] = 0; /* strip newline */
if (net_connect (conn, buf) != 0) {
puts ("Error initiating connection.");
return 1;
}
puts ("Connecting...");
do status = net_poll_connect (conn);
while (status == 0);
if (status < 0) {
puts ("Error connecting.");
return 1;
}
}
puts ("Enter text to send. Enter `.' to quit.");
while (1) {
/* Check if the remote side sent us any messages. */
while (net_query_rdm (conn))
/* If so, receive them and print them out. */
if (net_receive_rdm (conn, buf, sizeof buf) > 0)
printf ("Received: %s\n", buf);
/* Let user enter something. */
if (!fgets (buf, sizeof buf, stdin))
break;
buf[strlen (buf) - 1] = 0; /* strip newline */
/* Quit condition. */
if (strcmp (buf, ".") == 0)
break;
/* Send the text if it is not a blank line. */
if (buf[0])
net_send_rdm (conn, buf, strlen (buf) + 1);
}
/* Close the conn. */
net_closeconn (conn);
/* Quit. */
return 0;
}
File: libnet-HOWTO.info, Node: An example, Next: Network limitations, Prev: Conns, Up: Top
An example
**********
[ Should I write a small Allegro + Libnet example here? I'm not sure it
is necessary. ]
File: libnet-HOWTO.info, Node: Network limitations, Next: What now?, Prev: An example, Up: Top
Network limitations
*******************
Once you figure out the Libnet API, there is still the network to
contend with. Just like a computer's processor speed, memory capacity,
etc. are limitations you must work around, the bandwidth and latency of
a network are limitations you will have to overcome.
Let's say a game is using a pure client-server model (e.g. Quake) - all
game processing occurs on the server, the client sends keystrokes for
interpretation to the server, and receives back the locations of objects
from the server. If it takes a quarter of a second for a message to
reach its destination (say, on the Internet) then, at best, a key
pressed on the client machine will only have a visible effect on the
client's machine half a second later:
1. client sends keystroke to server (250 msec)
2. server processes action (x msec)
3. server sends game object locations back to client (250 msec)
Hopefully your Internet connection is better than mine, but it
illustrates a good point. You may not be able to implement exactly the
game you want, or implement in the way that you really want to (e.g. the
above example is a clean design, but the game would be unplayable on
such a connection). You will need to deal with all the multitude of
problems that crop up in networks.
For real-time games this is an especially difficult task. But, being
games, we can take shortcuts. As long as all the gamers _think_ they
are interacting in approximately the same world, no-one will care. And
that's as far as I'll go here, as I know nothing about this area.
Perhaps you could enlighten me?
File: libnet-HOWTO.info, Node: What now?, Next: Function Index, Prev: Network limitations, Up: Top
What now?
*********
* Go and read the Libnet documentation. It describes the API in
much more detail.
* If you need help, try the Libnet mailing list. See the Libnet
documentation for more.
File: libnet-HOWTO.info, Node: Function Index, Prev: What now?, Up: Top
Function Index
**************
* Menu:
* net_assigntarget: Assigning a target.
* net_closechannel: Closing a channel.
* net_closeconn: Closing a conn.
* net_connect: Connecting conns.
* net_getlocaladdress: Getting the local address.
* net_ignore_rdm: Ignoring data.
* net_init: Initialising.
* net_listen: Connecting conns.
* net_loadconfig: Initialising.
* net_openchannel: Opening a channel.
* net_openconn: Opening a conn.
* net_poll_connect: Connecting conns.
* net_poll_listen: Connecting conns.
* net_query: Receiving data on channels.
* net_query_rdm: Receiving data on conns.
* net_receive: Receiving data on channels.
* net_receive_rdm: Receiving data on conns.
* net_send: Sending data on channels.
* net_send_rdm: Sending data on conns.
* net_shutdown: Shutting down.
Tag Table:
Node: Top85
Node: About this document503
Node: Introduction to Libnet1930
Node: Using Libnet2959
Node: Initialising3484
Node: Shutting down4815
Node: Using Libnet example5326
Node: Channels5895
Node: Opening a channel6479
Ref: bindings7134
Node: Assigning a target9167
Node: Getting the local address10152
Node: Sending data on channels10988
Ref: net_send11240
Node: Receiving data on channels11595
Ref: net_query11891
Ref: net_receive12047
Node: Closing a channel12601
Node: Channels example12913
Node: Conns15876
Node: Opening a conn16339
Node: Connecting conns16917
Node: Sending data on conns19265
Node: Receiving data on conns19691
Node: Ignoring data20297
Node: Closing a conn21081
Node: Conns example21379
Node: An example25061
Node: Network limitations25272
Node: What now?26986
Node: Function Index27303
End Tag Table