Twin Forces

TF API Calls - Brook networking library

(1997 v2, release 29 October)


Main Page

Document index

The Brook network library creates servers, clients and connections between multiple machines and processes. By encapsulating the tedious OS calls, the objects leave the user to fulfill the communications without overhead. Also included in this library are several testing modules for qualifying new OS.

Document structure:

The library is completely object based, so instead of a procedural programming interface, using the functionality involves creating objects from the library.


At the heart of the server are tfbrook_connection objects which represent ongoing connections to clients.

class	tfbrook_connection
{
	public:
		tfbrook_connection();
		~ tfbrook_connection();
		void	noblock(void);
		...
		int testactive();
		void close();
		int read(void *, long len);
		int forceread(void *, long len);
		int forceread(void *, long len, int time);
		int write(void *, long len);
		virtual void install();
		virtual int data();
		virtual void space();
		virtual void final();
	protected:
		int portnum, portfinal,
			limit;
} ;
Functions install(), data(), and final(). are how the connection responds to network activity:
  • install() is called after the object is setup and connected to the client.
  • data() is called when TFBROOK has detected incoming data or control activity. This function needs to return any error or zero results of reading back to the framework so that the connection can be closed.
  • final() is called as the connection is shutdown.

To create connections, the server must have a listen object which waits at a particular port and creates connections as clients attempt to connect.

class	tfbrook_stone
{
	public:
		tfbrook_stone(int);
		~ tfbrook_stone(void);
		...
	protected:
		int portnum;
	private:
		virtual tfbrook_connection * create(void);
} ;
By defining create() to create your subclass of tfbrook_connection the proper communcation data handling will be linked to the client. IMPORTANT -- your subclass of TFBROOK_STONE must pass the port number through to the tfbrook_stone(int) super-class constructor.

Finally, the top of the server object structure is TFBROOK:

class	tfbrook
{
	public:
		tfbrook();
		~ tfbrook();
		void	listen(tfbrook_stone *);
		void	watch();
		int		watch(int);
	...
} ;
The primary responcibilities of TFBROOK are to listen for connections through listen(TFBROOK_STONE *) and to service created connections (TFBROOK_CONNECTION).


Each server object has various public services used by the application.

Class TFBROOK has two functions. After creation, it first provides an interface for adding listen sites. Secondly it provides two different methods for polling for activity.

void listen(tfbrook_stone *) accepts created tfbrook_stone objects for adding to the listen list. Objects are first tested to confirm that they are successfully listening, and then added to the list. No error is produced if an object is rejected.

void watch() and int watch(int time), watch all attached listen ports for activity. The first version will wait forever for network activity, and returns after any activity. The second version will only wait up to int time seconds and returns a value indicating what transpired: -1 = no connections to watch, 0 time passed with no activity, 1 activity occurred.

Class TFBROOK_STONE does not provide any significant services for application use.

Class TFBROOK_CONNECTION provides a wide range of services for testing and using the connection to the client.

int testactive() returns if the connection object is still alive, as activity from the client side can cause the connection to close.
void noblock() configures the socket not to block on writes so that the server can push data to multiple connections without stopping when one buffer is full. void close() shuts down the connection and calls the final() routine.

int read(void *, long len) reads data from the connection. Up to len bytes are read. read() returns the number of bytes actually read. If zero or a negative number is returned, the connection has closed or returned an error and is no longer usable. final() is automatically called in this case (before read() returns).
int forceread(void *, long len) continues calling read until len bytes are read, or an error occures.
int forceread(void *, long len, int time) continues reading until len bytes are read, or time passes.
int write(void *, long len) writes the data to the connection. An error is returned if the connection fails or closes.


Each client object has a very simple structure since it is method driven, not event driven.

class tfs_point
{
	protected:
		tfs_point();
		tfs_point(const char *, const int);
		~ tfs_point();
		int connect(const char *, const int);
		int testactive();
		void close();
		int test();
		int test(timeval *);
		int read(char *ptr, int n);
		int forceread(char *, int);
		int forceread(char *, int, int);
		int write(char *ptr, int n);
	...
} ;
tfs_point(char *hostname, int portnum) creates a TFS_POINT object and attempts to establish the connection. Because the SGI does not yet support exceptions, the object will be created even if the connection is not established. Use testactive() to determine if a connection is in place.
tfs_point() creates the object without making any connection. The object can then be connected later with the connect() method.
int connect(char *, int) connects an idle object to the server. The return code is 0 if successful, or the error code is returned.
close() cleanly closes the connection to the server.

test() tests for data on the connection, it returns immediately with the answer (true or false).
test(timeval *t) waits up to t for data before returning. It returns immediately if data is already available.
read(), forceread(), and write() behave the same as the methods of TFBROOK_CONNECTION (described above).


Implementation examples:.

The test applications BROOK1 through BROOK3 have been created to demonstrate various applications of this library.

BROOK1

brook1.cpp creates a server that listens on two different ports. The first port accepts messages and prints them, the second accepts messages as a cue to halt the server.

The first connection listener is created with the following definitions:

class	echosite: public tfbrook_connection
{
	void install() { }
	int	data()
	{
		int ival, ...;
		char rawmsg[512], ...;
		ival= read(rawmsg, 512);
		if (ival < 1)
		{
			if (! ival) printf("* closing %d\n", portnum);
				else printf(" >> port error\n");
			return ival;
		}
		...
		return 1;
	}
} ;
class   echoserver: public tfbrook_stone
{
	public:
		echoserver(int n) : tfbrook_stone(n)
			{ }
	private:
		tfbrook_connection * create(void)
			{ return new echosite(); }
} ;
The second connection is so simple, it doesn't even try to get incoming messages:
class	haltsite: public tfbrook_connection
{
	void install(void) { }
	void data()
		{ isrun= 0; }
} ;
class	haltserver: public tfbrook_stone
{
	public:
		haltserver(int n) : tfbrook_stone(n)
			{ }
	private:
		tfbrook_connection * create(void)
			{ return new haltsite(); }
} ;
Using these classes is no more difficult than creating each object and setting TFBROOK to use them:
int isrun;
main()
{
	tfbrook * ha= new tfbrook();
	ha-> listen(new echoserver(8118));
	ha-> listen(new haltserver(9118));
	for (isrun= 1; (isrun); )
		{ ha-> watch(); }
	delete ha;
}

BROOK2

brook2.cpp creates a client that connects to calm.tfe.infomagic.com, sends a message, pauses, and then closes the connection.

main()
{
	int iret;
	tfs_point *ptr;
	
	ptr= new tfs_point("calm.tfe.infomagic.com", 8118);
	if (! ptr-> testactive())
		{ printf(" >> create error\n");  exit(1); }
	iret= ptr-> write("test e", 6);
	if (iret < 0)
		{ printf(" >> write error\n"); }
	sleep(10);
	delete ptr;
}

©1997 Twin Forces, Inc. All rights reserved.
Comments to woolstar@twinforces.com.
ÿ