This document assumes a certain degree of knowledge of TCP/IP and socket programming.
To use the NET library, you'll need to '#include
Otherwise, use either the machines' network name - or it's IP address
(as an ASCII string).
The handle_buffer_read() method looks at the input
stream for the current 'terminator' (usually '\r\n'
for single-line responses, '\r\n.\r\n' for multi-line
Symbol Conventions.
Pegasus follows the same conventions for symbols and tokens that
are used by OpenGL and GLUT. All Pegasus symbols for classes and
functions start with net
and all #define
tokens start with NET
. Words within a class or function
name are Capitalised and NOT separated with underscores. Words
within #define
tokens may be separated with underscores
to make them readable.
Initialisation.
The first NET call in any program must always be netInit().
Classes
The following class hierarchy makes up the core package - which
can be extended to add functionality or to change some underlying
mechanisms.
class netAddress
class netBuffer
|__ class netMessage
class netGuid
class netSocket
|__ class netChannel
|__ class netBufferChannel
| |__ class netChat
| |__ class netMessageChannel
|
|__ class netMonitorServer
A simple buffer class.
class netBuffer
class netBuffer
{
public:
netBuffer ( int max_length ) ;
~netBuffer () ;
int getLength() const ;
int getMaxLength() const ;
char* getData() ;
void remove () ;
void remove (int pos, int n) ;
bool append (const char* s, int n) ;
bool append (int n) ;
} ;
A message buffer used to transfer binary data and handle byte swapping.
class netMessage
class netMessage : public netBuffer
{
public:
netMessage ( const char* s, int n ) ;
netMessage ( int type, int to_id, int from_id ) ;
int getType () const ;
int getToID () const ;
int getFromID () const ;
void geta ( void* a, int n ) const ; // a=array; array should already be serialized
void puta ( const void* a, int n ) ;
int getch () const ;
void putch ( int c ) ;
bool getb () const ;
void putb ( bool b ) ;
int getw () const ;
void putw ( int i ) ;
int geti () const ;
void puti ( int i ) ;
void gets ( char* s, int n ) const ;
void puts ( const char* s ) ;
void print ( FILE *fd = stderr ) const ;
};
This is the representation of an Internet-style machine address.
class netAddress
class netAddress
{
public:
netAddress () ;
netAddress ( const char* host, int port ) ;
void set ( const char* host, int port ) ;
const char* getHost () const ;
int getPort () const ;
static const char* getLocalHost () ;
bool getBroadcast () const ;
} ;
When reading from the network using one of the higher level classes,
you can use a netAddress with an empty string as the 'host' to mean
"accept data from any host". When writing to the network, the
reserved host name '<broadcast>' (the < and > are literally
there) will cause the message to be broadcast to all machines on your
subnet.
netSocket is the low-level socket class.
class netSocket
class netSocket
{
public:
netSocket () ;
virtual ~netSocket () ;
int getHandle () const ;
void setHandle (int handle) ;
bool open ( bool stream=true ) ;
int bind ( cchar* host, int port ) ;
int listen ( int backlog ) ;
int accept ( netAddress* addr ) ;
int connect ( cchar* host, int port ) ;
int send ( const void * buffer, int size, int flags = 0 ) ;
int sendto ( const void * buffer, int size, int flags, const netAddress* to ) ;
int recv ( void * buffer, int size, int flags = 0 ) ;
int recvfrom ( void * buffer, int size, int flags, netAddress* from ) ;
void close ( void ) ;
void setBlocking ( bool blocking ) ;
} ;
Example:
This example is stripped of error checking for clarity:
This code produces a 'Datagram' connection - which is fast but unreliable
(using UDP protocol). Passing a 'true' to sock->open() would produce a
'Stream' connection (using TCP).
Sender:
netInit () ;
netSocket *sock = new netSocket () ;
sock -> open ( false ) ;
sock -> setBlocking ( false ) ;
sock -> connect ( host, port ) ;
while ( !done )
sock -> send ( msg, len, 0 );
sock -> close () ;
Reciever:
netInit () ;
netSocket *sock = new netSocket () ;
sock -> open ( false ) ;
sock -> setBlocking ( false ) ;
sock -> bind ( host, port ) ;
while ( !done )
if ( (len = sock -> recv(msg, maxlen, 0)) >= 0 )
...use the data...
sock -> close () ;
netChannel adds event-handling to the low-level
netSocket class. Otherwise, it can be treated as
a normal non-blocking socket object.
The direct interface between the poll loop and
the channel object are the handleReadEvent and
handleWriteEvent methods. These are called
whenever a channel object 'fires' that event.
The firing of these low-level events can tell us whether
certain higher-level events have taken place, depending on
the timing and state of the connection.
class netChannel
class netChannel : public netSocket
{
public:
netChannel () ;
virtual ~netChannel () ;
void setHandle (int s, bool is_connected = true);
bool isConnected () const ;
bool isClosed () const ;
void shouldDelete () ;
// --------------------------------------------------
// socket methods
// --------------------------------------------------
bool open ( bool stream=true ) ;
int listen ( int backlog ) ;
int connect ( cchar* host, int port ) ;
int send ( const void * buf, int size, int flags = 0 ) ;
int recv ( void * buf, int size, int flags = 0 ) ;
void close ( void ) ;
// poll() eligibility predicates
virtual bool readable (void) ;
virtual bool writable (void) ;
// --------------------------------------------------
// event handlers
// --------------------------------------------------
void handleReadEvent (void);
void handleWriteEvent (void);
// These are meant to be overridden.
virtual void handleConnect (void) ;
virtual void handleRead (void) ;
virtual void handleWrite (void) ;
virtual void handleClose (void) ;
virtual void handleAccept (void) ;
virtual void handleError (int error) ;
static bool poll (u32 timeout = 0 ) ;
static void loop (u32 timeout = 0 ) ;
};
netBufferChannel is a netChannel with I/O buffering.
Clients and servers built on top of netBufferChannel
automatically support pipelining where you can send
multiple commands without waiting for the response
to each command before you send the next.
class netBufferChannel
class netBufferChannel : public netChannel
{
public:
netBufferChannel (int in_buffer_size = 512, int out_buffer_size = 4096) ;
void closeWhenDone (void) ;
virtual bool bufferSend (const char* msg, int msg_len) ;
virtual void handleBufferRead (netBuffer& buffer) ;
};
netChat adds support for 'chat' style protocols -
where one side sends a 'command', and the other sends
a response (examples would be the common internet
protocols - smtp, nntp, ftp, etc..).
class netChat
class netChat : public netBufferChannel
{
public:
netChat () ;
void setTerminator (const char* t);
const char* getTerminator (void);
bool push (const char* s) ;
virtual void collectIncomingData (const char* s, int n) ;
virtual void foundTerminator (void) ;
};
A channel for binary messages. Takes care of packing and unpacking them in/out of the buffers.
class netMessageChannel
class netMessageChannel : public netBufferChannel
{
public:
netMessageChannel () ;
bool sendMessage ( const netMessage& msg ) ;
virtual void handleMessage ( const netMessage& msg ) ;
};
The monitor server gives you remote, 'back-door' access to your server while it is running. netMonitor is a telnet command port with
password authorization. It can be paired with and used to remotely admin another server.
Once connected via any telnet client to the monitor, you can issue commands. The monitor can be hooked up to a python interpreter for added power. Since an ordinary telnet session is not secure, the password could be intercepted, but that level of security is usually not needed for games.
class netMonitorServer
class netMonitorServer : public netChannel
{
public:
netMonitorServer( cchar* _name, int port ) ;
~netMonitorServer() ;
cchar* getPassword () const ;
void setPassword ( cchar* string ) ;
void setPrompt ( cchar* string ) ;
void setCommandFunc ( void (*func)(cchar*, netMonitorChannel*) ) ;
} ;
Dave McClurg
<dpm@efn.org>