Nebula
|
The Nebula Net subsystem offers simple client/server-style communication using the TCP protocol over LAN or internet connections. It is not intended for highlevel game-oriented communication with lobbies, session management and synchronisation of player data. This will be provided in higher level Nebula networking subsystems.
An IpAddress object identifies a communication endpoint by host name or tcp/ip address and a port number. IpAddress objects can be created in a number of ways:
An IpAddress object can be used to lookup a TCP/IP address from a host name:
The Net subsystem provides an easy-to-use TCP-based client/server system implemented in the classes TcpServer and TcpClient using the TCP protocol. Any number of TcpClients can be served by a single TcpServer simultanously.
Setting up a server is done like this:
This will setup the server to listen on port 2352 for incoming client connection requests.
To communicate with the TcpServer, a TcpClient object needs to be setup on the client side:
This assumes that the server is running on the same machine as the client (since the client connects to "localhost").
In a non-blocking scenario as above, the Connect() method will either return with TcpClient::Success (which means the connection is established), or more likely with TcpClient::Connecting, in this case the connection hasn't been established yet, and the application needs to continue calling the Connect() method. In the case of an connection error, the return code TcpClient::Error will be returned.
In a blocking scenario the Connect() method will not return until either the connection has been established (result would be TcpClient::Success) or an error occured (TcpClient::Error).
Once a connection has been established, a TcpClientConnection object will be created on the server side for each connected client. The TcpClientConnection represents the client on the server and is used to receive data from the client and to send responses back to the client.
For sending and receiving data, IO::Stream objects are used. By attaching IO::StreamReader and IO::StreamWriter objects to the communication streams it is very easy to encode and decode data from the stream.
To send some text data from a client to its server, obtain a pointer to the send stream, write data to it and call the Send() method:
To receive client data on the server side, the application needs to poll for TcpClientConnection which contain data from clients frequently (e.g. once per frame). More then one TcpClientConnection may be waiting for processing, thus the processing loop should look like this:
To get server responses on the client side, call the TcpClient::Recv() method which will block until data arrives (in blocking mode), or come back immediately (in non-blocking mode) and return true when data from the server is available:
A client should also check whether the connection is still up by calling the IsConnected() method. If the connection has been dropped for some reason, this method will return false.
For real world scenarios, an application should implement its own robust communication protocol which at least encodes the length of the payload data. If the payload is bigger then some maximum packet size, data may be sent in several packets, and thus may arrive in several packets at the client. The client should decode the length of the payload from the message header to decide whether the received data represents a complete message, or whether more data needs to be received until message is complete.
Servers and clients may run on CPUs with different byte order. If binary data is sent over a network connection, the data must be converted into a "network byte order" which both clients agree on. Nebula offers automatic byte order conversion in the IO::BinaryReader and IO::BinaryWriter classes. Simply call the following methods before reading from or writing to a network communication stream:
The Net subsystem provides a Socket class which wraps the traditional socket functions into a C++ interface. Usually an application doesn't use Socket class directly and instead uses higher level networking classes like TcpServer. But if that's not possible for some reason the Socket class is much more convenient then working directly with socket functions.