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