Network API overviewAbstract:This document explains how to write network games with ClanLib's network model. There are currently four different levels of abstraction in clanNetwork:
ClanLib Sockets, the low-level partThe lowest level is CL_Socket. This is platform independent version of the system level socket functions, encapsulated in a class for your convience. A simple example: /* Gets clanlib.org's index.html page: */ try { std::string request_msg = "GET index.html HTTP/1.0\n\n"; CL_Socket sock(CL_Socket::tcp); sock.connect(CL_IPAddress("clanlib.org", 80)); sock.send(request_msg); while (true) { char buffer[16*1024+1]; int received = sock.recv(buffer, 16*1024); if (received == 0) break; // end of stream. server closed connection. buffer[received] = 0; std::cout << buffer; } std::cout << std::endl; } catch (CL_Error err) { std::cout << "Why wont things never work as planned? " << err.message.c_str() << std::endl; } The CL_BufferedSocket class is not completely implemented and should not be used. I'm not even anymore sure it was a good idea and it might be pending for removal. EventTriggers:Additionally to this, CL_Socket can use the CL_EventTrigger and CL_EventListener to wait for socket data. A small example: void wait_for_socket_data(CL_Socket &sock, int timeout) { CL_EventTrigger *read_trigger = sock.get_read_trigger(); read_trigger->wait(timeout); } void wait_for_sockets(std::list<CL_Socket> &sockets, int timeout) { CL_EventListener listener; std::list<CL_Socket>::iterator it; for (it = sockets.begin(); it != sockets.end(); it++) { CL_Socket &sock = *it; listener.add_trigger(sock.get_read_trigger(); } listener.wait(timeout); } OutputSources:CL_OutputSource_Socket is a CL_OutputSource compatible wrapper for CL_Socket. It can be mixed with CL_Socket since the CL_Socket class reference counts the handle to the system level socket. A small example of its usage: void send_init_handshake(CL_Socket &sock) { CL_OutputSource_Socket output(sock); // output.set_big_endian_mode(); // ha! as if that actually worked... output.write_string("Yo dude! You've entered the 1337 socket owned by Cl4nL1b"); output.write_int(42); } You will find a similar CL_InputSource_Socket for reading from sockets. That was the lowest level socket support. It doesnt do much except save you from the trouble of setting up some annoying C structs and filling them with data. ClanLib Netchannels, the mid-level partThe next level of abstraction is CL_NetSession. A netsession serves three main purposes:
A netchannel is analog to a network port. All IP communication is divided into different types, each assigned its own port. HTTP is handled on port 80, FTP on 21, telnet is 23 and so on. The same goes with network communication in ClanLib - here it is just called netchannel instead (to avoid confusion with IP ports). The main difference between a netchannel and a IP port is that all netchannels are packed into the same IP port. A netsession can either operate as a server, or as a client. This is determined at construction time, depending on which constructor you choose to use. If the netsession runs as a server, client netsesions can connect to you, and you will get a list (CL_NetGroup) of computers (CL_NetComputer) connected to the session. If the netsession runs as a client, you will only be able to see one computer: the server. Example: // Create a server CL_NetSession server("MyGame", 5555); // Create a client CL_NetSession client("MyGame", localhost, 5555); Network events:Events in the network code are computers joining, leaving, closure of the game and access changes. These events are stored in the netsession, and needs to be polled. There are currently no way to get these events as signal callbacks (CL_NetSession::sig_computer_join()), but this will be changed sometime in the near future, as polling pretty much sucks.CL_NetSession::receive_computer_join() for instance return the first joined computer, next time it called the next is returned, and when all joined computers has been reported it throws an exception. Sending data on a netchannel:In order to send data to computer, you need assign a netchannel for that kind of data you want to send. For instance, use netchannel 0 for system messages, 1 for intercom, 2 for game objects. If then you want to send a message on the intercom channel, do the following: netsession.send(1, netcomputer, message); On the receiving computer, use the CL_NetSession::receive() and CL_NetSession::peek() function to read the message. Access restrictions:ClanLib supports access restrictions on its netchannels. By default, the server doesn't allow clients to speak on any of the channels. The server must explicit allow each and every client access where it is supposed have it. This is done for security. Furthermore a client can have read access, write access or both, so it is very easy to keep a good eye on the clients - this is good, especially in Internet games. The channel access stuff is currently not implemented fully, and it has some design problems and will most likely be either totally removed, or replaced by something a little more thought through. ClanLib NetObjects, the high-level partThe third level of abstraction is the netobject interface. The purpose of the netobject system is to allow objects to send data to its other object on the receiving machine, using one netchannel. The netobjects API consists of two classes: CL_NetObject_Channel and CL_NetObject. The channel class is the controlling class that listens to a netchannel and dispatches the messages to the correct netobject. It also manages the global list of IDs that identify a netobject across the network. The netobject class represents an object. When constructed, it will register itself at the netobject channel and get an unique ID assigned. The application can then use CL_NetObject::send() to send data to the listening CL_NetObject object on an other computer. When the other computer receives this initial message, it will notice that there is no CL_NetObject for that object. This causes the netobject channel object to invoke CL_NetObject::sig_create_object, which allows the receiving computer to create an object based on the message (or ignore it or whatever it wants with it). If the receiving computer decides to keep the CL_NetObject assigned to it in CL_NetObject::sig_create_object, any further messages sent to this object to be routed to callbacks connected to the object. Also, if the receiving computer wants to send data back to the owner object, it should use CL_NetObject::talkback(). ClanLib World-template, experimental game network engine thingyFinally there is something called a "world template" that works on top of this all. Its a kind of game network engine thingy built into a template so that common stuff doesnt have to be written - still at a pretty experimental stage. The NetObjects example uses this template, so have a look there if you need to live on the bleeding edge of network development :) |