Introduction
============

The objective of this tutorial is to step through a simple example 
of a working tcp/ip client and server. In this case, the application 
is a "virtual blackboard" which allows users to connect to a server and
then draw in a shared window; any drawing done in any user's window
also appears on all other users' windows. The only other thing it does
is provide a "clear" command on the application menu, which clears all
blackboards. For the most part, we will ignore the user interface and 
focus on the networking parts of the program.

One important note first; there are some problems with DR8 networking.
See the "Other Notes" section at the end for more details. However, the
socket code used here should compile with no required changes in DR9.
(It does so with the current build, at least)

If you're not at least a little familiar with threads and the BeOS' 
BLooper and BMessage objects, please read the sections of The Be Book 
on these. 

Not on a network? Don't worry! You can use the built-in loopback address,
127.0.0.1, as your address, and everything will be just fine.

Code Organization
=====================
The foundation of all the networking in the tutorial is two classes,
BTSSocket and BTSAddress. BTSSocket objects represent socket connections,
both client and server. BTSAddress objects represent network addresses.

Both the client and the server are constructed in two layers; one layer 
consists of a generic BMessage-based client and server. The server operates 
by receiving data from clients, converting it into BMessage objects, and 
posting the resulting message to whatever the assigned receiver is. To 
send messages to clients, BMessages are posted to this server object. 
Messages can be targeted at a specific client or sent to all clients at 
once. Similarly, the message clients convert data received from the server 
into BMessages and post them, to an assigned message receiver, and any 
messages locally posted to a client are sent to the server.

On top of this is the drawing server and the drawing client code. These operate 
by creating instances of the BMessage server or client, repectively, and 
specifying themselves as the message recipients.

Here is a summary of the generic classes used in the program.

BTSAddress.cpp		 --	Definition of network address object.
BTSSocket.cpp		 -- Definition of network socket object. Implements
						low-level network functions (bind, send, recv,etc)
BTSNetMsgUtils.cpp	 --	Utility functions used by both client and server
						to send and receive BMessages over the network.
BTSNetMsgServer.cpp	 --	BMessage-based server definition
BTSServerMonitor.cpp --	Simple server UI representation, allows you to 
						start a server and displays the number of 
						connections. Also allows for selection of the
						loopback.
BTSNetMsgClient.cpp	 --	BMessage-based client definition
BTSPrefDlog.cp		 -- A generic client interface, that lets you specify what
						host you want to connect to.


In addition to these, the following classes exist to create the draw server 
and client:

BTSNetDrawServer.cpp -- Creates an instance of BTSNetMsgServer and manages it.
BTSNetDrawClient.cpp -- Creates an instance of BTSNetMsgClient and manages it.
BTSNetDrawWindow.cpp -- Client drawing window.
BTSNetDrawView.cpp	 -- Client drawing view.

Making a Server
===============
First, let's examine the generic message server.

The server is a BLooper, which means you can communicate with it by posting 
messages to it. Threads are only used where necessary to avoid blocking on 
network functions (such as accept()). There is only one thread to manage all 
incoming client data, and outgoing data is serialized through the 
MessageReceived() method of the message server. There are 3 threads total in 
the messaging server:

1. Created by subclassing the server from BLooper. This is the one we
	post to when we want to send a message to a client or clients.
	
2. Connection handler. Continuously waits for client connection requests.

3. Client listener. Continuously waits for incoming client data.

The drawing server also creates a BApplication and BWindow thread, as
usual.


Preparing to start the server
-----------------------------
Servers operate by attaching themselves to a certain port number, then
waiting for clients to connect. Clients must have prior knowledge of the 
port number (or the service name, which is just an alias to a port number)
so that they can connect to the server.

In order for a server to be officially attached, it has to jump through
several hoops. This is encapsulated in the BTSNetMsgServer::Run() method.
The server requires the following information before it can attach to a
port:


port	 --	number used as rendezvous point for clients and servers.
address	 --	address of network interface to use for server (there may
			be multiple interfaces). There are problems in DR8 with
			attaching to multiple interfaces simultaneously. See The
			Be Book for more details.
family	 --	socket's network address format. Currently must be AF_INET.
type	 -- either SOCK_STREAM or SOCK_DGRAM, for stream or datagram sockets.
			(TCP/IP is a stream socket).
protocol -- "Messaging" protocol for socket. If you set it to 0,
			it will pick the "natural" one for the socket type; for 
			SOCK_STREAM, this is IPPROTO_TCP.

So, this stuff says "I want to connect on this port using this network
interface, communicating in this way.". For the purposes of this app,
the communication is always TCP/IP.

At a low-level, the steps required to connect using this information are:

1. Create a socket using socket().
2. Bind the socket to an address using bind().
3. Start listening for clients by calling listen().

The specifics of doing these things are hidden inside of the BTSSocket class;
They are found, respectively, in the constructor and the methods Bind() and
Listen(). If you'd l ike to see details on this, check out the code now and
the discussions in the Network Kit. 

The BTSSocket object used by the server for accepting client connections is
created in the constructor for BTSNetMsgServer:

BTSNetMsgServer::BTSNetMsgServer(const unsigned short port, 
						BLooper* messageReceiver,
						const long priority, 
						const int maxConnections, 
						const unsigned long address, 
						const int family, 
						const int type, 
						const int protocol  ) :
						fSocket(type,protocol, family),
						fAddress(family,port, address),
						fPriority(priority),
						fMaxConnections(maxConnections)
{
	
	if (messageReceiver == NULL)
	{
		messageReceiver = be_app;
	}
	fIsExiting = FALSE;
	fSocketListSem = ::create_sem(1, "Client Socket List Sem");
	
	::acquire_sem(fSocketListSem);
	fMessageReceiver = messageReceiver;
	return;
}


A BTSAddress object is also created; this is just a convenience class that encapsulates
family, port, and hostname, and does some conversions, like giving us the host entry of the machine the address is associated with.
The socket created is the socket the server will listen for clients on.

Now that we have a socket, we have to bind and start listening for clients.
This is done when the Server's thread is started by the Run() method:

thread_id
BTSNetMsgServer::Run()
{
	thread_id 	theID = -1;		// To return thread id of this thread
	int 		result = 0;		// Results of socket function calls
	
	
	// Bind the socket to the port/address specified in sockAddr
	result = fSocket.BindTo(fAddress);
	
	if (result >= 0)							// Was bind successful?
	{
		// Start listening for connections.
		result = fSocket.Listen(fMaxConnections);



Assuming the server has a valid socket, the socket is bound to the network 
address that clients will communicate with it on. Then Listen() is called 
to start listening for clients, and is passed the maximum number of 
connections the server can handle.

The remaining steps are to accept clients and receive client data. However,
if no clients are connecting or no clients are sending data to the server, 
attempts to accept clients or receive data will block. For this reason, the
server spawns one thread for each of these. Continuing on in 
BTSNetMsgServer::Run():

		if (result >= 0)
		{
			//Start the main server thread.
			theID = BLooper::Run();
			if (theID > B_NO_ERROR)				// Did server thread start?
			{
				// Start separate thread to handle conn. requests.
				fConnectionRequestHandlerID = 
				::spawn_thread(HandleConnectionRequests, 
							kConnectionRequestHandlerName, 
							fPriority, 
							(void*)this);
				if (fConnectionRequestHandlerID > B_NO_ERROR)
				{
					::resume_thread(fConnectionRequestHandlerID);
				}
				
				// Start separate thread to listen for incoming client data.
				fClientListenerID = ::spawn_thread(ListenToClients, 
												kClientListenerName,
												fPriority,
												(void*)this);
				if (fClientListenerID > B_NO_ERROR)
				{
					::resume_thread(fClientListenerID);
				}
			}
		}

		
				
That's it for Run().Lets look at HandleConnectionRequests(). It's a static
private method of the BTSNetMsgServer class.

long
BTSNetMsgServer::HandleConnectionRequests(void* arg)
{
	BTSNetMsgServer* 	thisServer = (BTSNetMsgServer*)arg;
	BTSSocket 			acceptSocket = thisServer->Socket();
	int 				clientSocket;
	sockaddr_in 		clientInterface;
	int					clientIntfSize = sizeof(sockaddr_in);
	long				result = 0;
	PRINT( ("HandleConnectionRequests - THREAD ENTER\n"));
	// Thread blocks here, on accept().
	while ((clientSocket = ::accept(acceptSocket.ID(), 
									(struct sockaddr*)&clientInterface,
									&clientIntfSize)) >= 0)
	{		
		PRINT(( "Connection request, new socket is %d\n", clientSocket));
		// A client has requested a connection. Make a handler for the
		// client socket.

		if (clientSocket >= 0)
		{
			BTSSocket* newClient = new BTSSocket(clientSocket);
			// Tell the server about the new client.
			BMessage* aMessage = new BMessage(NEW_CLIENT_MSG);
			if (aMessage != NULL)
			{
				if (aMessage->Error() == B_NO_ERROR)
				{
					aMessage->AddObject(SOURCE_SOCKET, (BObject*)newClient);
					if (aMessage->Error() == B_NO_ERROR)
					{
						thisServer->PostMessage(aMessage);
					}
					else delete aMessage;
				}
				else delete aMessage;
			}
		}
		else break;
		clientIntfSize = sizeof(sockaddr_in);
	}
	PRINT( ("HandleConnectionRequests - THREAD EXIT\n"));
	exit_thread(result);
}


	
As noted earlier, this thread is constantly blocked on the accept() function,
which only returns when a client has requested a connection. Each time it returns,
we get the client's local socket number. (The socket number on the client's machine
may be different) When this occurs we create a new BTSSocket object representing
the client, place it in a BMessage and post the message to the server. The server
maintains a list of socket objects.

Now that the client is connected, the server may receive data from it, which would
arrive in the thread running the server's ListenToClients() method. Let's look
at the main loop for ListenToClients:

long
BTSNetMsgServer::ListenToClients(void* arg)
{
	BTSNetMsgServer*	thisServer = (BTSNetMsgServer*)arg;
	BList*				serverSocketList = thisServer->SocketList();
	BLooper*			messageReceiver = thisServer->MessageReceiver();
	BMessage*			newMessage = NULL;
	sem_id				socketListSem = thisServer->SocketListSem();
	long				result = B_NO_ERROR;
	struct timeval 		tv;
	struct fd_set		readBits;
	BTSSocket* 			socket;
	int					i;
	
	// Set delay to wait for client data before returning.
	tv.tv_sec = 1;
	tv.tv_usec = 0;

	PRINT(("BTSNetMsgServer::ListenToClients - THREAD ENTER\n"));
	
	for (;;)
	{
		// Set the select bits for the known sockets
		FD_ZERO(&readBits);
		PRINT(("Acquiring socket list semaphore\n"));
		::acquire_sem(socketListSem);
		BList socketList(*serverSocketList);
		::release_sem(socketListSem);
		
		if (socketList.IsEmpty()) goto LOOPEND;
		for (i = 0; i < socketList.CountItems(); i++)
		{
			socket = (BTSSocket*)socketList.ItemAt(i);
			FD_SET(socket->ID(), &readBits);
		}
		
		// Blocks here until data arrives on any socket.
		if (::select(32, &readBits, NULL, NULL, &tv) <= 0) goto LOOPEND;
			
	
The fd_set structure, readBits, acts like a mask that tells which sockets
we are interested in looking at. It is first cleared by FD_ZERO. Then, one by
one the client sockets are added to the mask. Once all the sockets are added,
we pass readBits as a parameter to select(). What select() does is just wait
until either data arrives on a socket or it times out (based on the tv
struct value passed as the last parameter). In this case the timeout value
is one second. Select can also be used to wait until it safe to write to a
socket (ie, no other operations are occuring on the socket) and sideband
communication. Don't worry about these now, because they're not in DR8! 
(support for writeBits is already in DR9)
			
			
OK, so if select returns a non-negative value, data has arrived. We want to
take the data, turn it into a BMessage, and post it to the server's assigned
message receiver. But first, we have to figure out which of the sockets in
the mask has data waiting. When select() returns, it turns off bits relating
to sockets that don't have data waiting to be read, and leaves on bits for
those that do. A call to FD_ISSET for each socket is all we need to check for
data on every client socket. If it's set, create the BMessage and post it:		
			
			
			
		PRINT(("Server received data\n"));
		for (i = 0; i < socketList.CountItems(); i++)
		{
			socket = (BTSSocket*)socketList.ItemAt(i);
			
			// Did data arrive on this socket?
			if (!(FD_ISSET(socket->ID(), &readBits))) goto SOCKETEND;
		
			// Yes..assume flattened BMessage format and go get it
			result = ReceiveNetMessage(*socket, &newMessage);
			
			if ( result != B_NO_ERROR ) goto SOCKETEND;
			
			// Post it to the message receiver.
			messageReceiver->PostMessage(newMessage);
		

ReceiveNetMessage is a utility function that receives the data and converts
it to a BMessage. We'll look at that in a minute. OK, so what if something
goes wrong? ReceiveNetMessage is designed to return the error ECONNABORTED
if it determines that client socket is dead. If this occurs, it posts a
message to the server informing it of this problem:
					
			// Clean up before checking next socket.
			SOCKETEND:
							
			if (result == ECONNABORTED)
			{
				// Inform the server that a client went away.
				BMessage*	deadMessage = new BMessage(DEAD_CONNECTION_MSG);
				BMessenger* messenger = new BMessenger(thisServer);
				BMessage* 	reply = NULL;
				
				PRINT(("Connection aborted\n"));
				if (deadMessage != NULL && messenger != NULL)
				{
					if (deadMessage->Error() == B_NO_ERROR)
					{
						deadMessage->AddObject(SOURCE_SOCKET, (BObject*)socket);
						if (deadMessage->Error() == B_NO_ERROR)
						{
							messenger->SendMessage(deadMessage, &reply);
						}
						else delete deadMessage;
					}
					else delete deadMessage;
				}
				
				if (messenger != NULL) delete messenger;
				if (reply != NULL) delete reply;
			}
			result = B_NO_ERROR;
		}



Note that the message indicating the client's exit is sent via a messenger, 
instead of just being posted. This makes the sending a synchronous call, to 
ensure that the server has removed the socket from the socket list before 
the ListenToClients() thread scans the socket list again. Using dead sockets
when doing network functions is a sure-fire way to mess up your application.

Another note on receiving data this way; if select() is called with a mask
containing more that one socket, it spawns threads, so this makes this method
a little inefficient. A better implementation would create threads for each
socket, all posting data back to the same message receiver and server.

Sending data to clients
-----------------------

To send data to a client, we post is to the server object, where it is handled in
the MessageReceived() method. If a message received by the server is not a control
message, it is either sent to a specific client or to all clients, based on whether 
the message contains a TARGET_SOCKET:

		default:
			BTSSocket* socket; 
			if (inMessage->HasObject(TARGET_SOCKET))
			{
				socket = (BTSSocket*)inMessage->FindObject(TARGET_SOCKET);
				SendNetMessage(*socket, inMessage);
			}	
			else if (!fClientSocketList.IsEmpty())
			{
				long result = SendToClients(inMessage);
			}
		break;

We'll look at SendNetMessage() in the next section. SendToClients() just calls
SendNetMessage() for each currently-connected client.

That's it for the generic server. The draw server creates an instance of this 
server, receives draw messages from clients, and just repeats them back out to
all other clients. In addition, it accrues a local bitmap of the drawing so that
when new clients connect, they get a copy of the current bitmap from the server.
If you like, check out BTSDrawServer.cpp now.




Making a Client
===============

Clients also need to create sockets. Unlike servers, the don't bind to a local 
port, they bind to a remote port where the server is connected. And they don't
listen or accept connections, they just connect to a single server. So, the 
procedure for bringing a client to life is:

1. Create a socket.
2. Connect to the server.

Creating a socket is just like with the server; just create a BTSSocket object,
which BTSNetMsgClient does in the constructor.

Connecting to the server
------------------------
Instead of binding to a local address, clients connect to a remove address. 
BTSSocket implements this in its Connect() method, which calls the network
function connect(). connect() is a blocking function, so instead of calling
it directly, when the client is run, a separate thread is started to connect
the client to the server. If the connect attempt is successful, the client
is notified via a BMessage, and the thread exits:

long 
BTSNetMsgClient::ConnectToServer(void* arg)
{
	// Static function that runs in a separate thread. His whole purpose
	// is to connect to a server, then he goes away. This prevents the
	// main client thread from blocking if a server isn't immediately
	// available.
	BTSNetMsgClient* 	thisClient = (BTSNetMsgClient*)arg;
	BTSAddress			address = thisClient->Address();
	BTSSocket			socket = thisClient->Socket();
	int					result;
		
	// Specify server connection info.
	result = socket.ConnectToAddress(address);	
	if (result >= 0 && !(thisClient->IsExiting()))
	{
		// Since we connected ok, create a socket handler for the 
		// client socket. Also, notify client that we are connected.
		thisClient->PostMessage(CONNECT_MSG);
		PRINT(("connected to server!\n"));		
	}
	exit_thread(result);
}


Listening to the server
-----------------------
As with the server, the client runs a separate thread to listen for incoming data.
This occurs in BTSNetMsgClient::MessageReceived(), after the client receives a message 
indicating that the socket connected successfully. This thread runs the ListenToServer()
static method:

long 
BTSNetMsgClient::ListenToServer(void* arg)
{
	BTSNetMsgClient* 	thisClient = (BTSNetMsgClient*)arg;
	BLooper*			messageReceiver = thisClient->MessageReceiver();
	BMessage*			newMessage = NULL;
	struct fd_set		readBits;
	struct timeval 		tv;
	long 				result;
	BTSSocket 			socket = thisClient->Socket();
	int					socketID = socket.ID();
	bool				exit = FALSE;
	
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	while (exit == FALSE)
	{
		FD_ZERO(&readBits);
		FD_SET(socketID, &readBits);
		if (::select(socketID + 1, &readBits, NULL, NULL, &tv) > 0)
		{
			if (FD_ISSET(socketID, &readBits))
			{
				PRINT(("BTSNetMsgClient::ListenToServer - SERVER MSG on %d\n", 
							socketID));
				result = ReceiveNetMessage(socket, &newMessage);
				if (result == B_NO_ERROR && newMessage != NULL)
				{
					// Post it to the message receiver.
					messageReceiver->PostMessage(newMessage);
				}
				
				else if (result == ECONNABORTED)
				{
					// Connection has died
					if (!thisClient->IsExiting())
					{
						thisClient->PostMessage(DEAD_CONNECTION_MSG);
					}
					exit = TRUE;
				}
			}

		}
	 	if (thisClient->IsExiting())
		{
			exit = TRUE;
		}
	}
	exit_thread(result);
}

This should look very similar to the server, except we need only do select with
a single socket descriptor.

Other than this, the client works similar to the server; post a message to it and
it goes over the net to the server, receive message from the server via it. It uses
the same utility and socket classes as the server.

The draw client creates an instance of BTSNetMsgClient and sends draw commands to it
that are received from the draw view. Messages from other clients are received and
posted to the window for drawing. The draw message itself just contains two BPoints,
for the start and endpoints of the line to be drawn. That's it!



Utility functions
=================

The utility functions do the work of getting network data and converting it back
into BMessages, and vice versa.

SafeUnflatten
-------------
Passing messages over a network is greatly simplified by the presence of 
BMessage's Flatten() and Unflatten() methods. However, care needs to be taken 
when unflattening. In DR8.2, attempting to unflatten a bogus buffer will probably
crash your application. This is much improved in DR9. For now we give you
SafeUnflatten:

BMessage* SafeUnflatten(const char* buf)
{
	// Safely unflattens a buffer back into a BMessage. Basically, just
	// check for proper message data header ('PARC') before unflattening.
	
	BMessage* newMessage = new BMessage();
	
	PRINT(("SafeUnflatten - ENTER\n"));
	if (buf == NULL) return NULL;
	if (newMessage == NULL) return NULL;
	if ((!(strcmp(buf, "PARC")) && newMessage->Error() == B_NO_ERROR))
	{		
		newMessage->Unflatten(buf);
		if (newMessage->Error() != B_NO_ERROR)
		{
			delete newMessage;
			newMessage = NULL;
		}
	}
	else if (newMessage != NULL)
	{
		delete newMessage;
		newMessage = NULL;
	}
	PRINT(("SafeUnflatten - EXIT\n"));
	return newMessage;
}

Valid message buffers should start with "PARC". One other thing. As William said,
"those Error functions are not optional". It's pretty easy to not want to use them
when working with messaging, because they end up all over the place. But they are
necessary!

Receiving the data used in the unflattening is pretty straightforward. What arrives
on the socket is, first, the message buffer size, then the message buffer itself.


ReceiveNetMessage
-----------------

BMessage data is passed over the network by first sending a long identifier,then 
sending a long value indicating the message buffer size, then the buffer itself. 
ReceiveNetMessage wraps up these receives with the call to SafeUnflatten:

long ReceiveNetMessage(const BTSSocket& socket, BMessage** outMessage)
{
	BMessage* 			newMessage = NULL;	// Holds new message
	char*				buf;				// Message data buffer
	long				msgSize;			// Message size
	long 				result;				// Result of socket calls
	long				msgID = -1;
	
	PRINT(("ReceiveNetMessage - ENTER\n"));
	// Get the header identifying a message.
	socket.RecvLock();
	result = socket.Recv((char*)&msgID, sizeof(long));
	if (result == B_NO_ERROR && msgID == MSG_IDENTIFIER)
	{
		// Get the message size.
		result = socket.Recv((char*)&msgSize, sizeof(long));
		
		msgSize = ntohl(msgSize);	// Convert from network to native format.
	
		if (msgSize >= 0 && result == B_NO_ERROR)
		{
			buf = (char*)malloc(msgSize);	
			if (buf != NULL)
			{
				// Get the message data.
				result = socket.Recv(buf, msgSize);
				if (result == B_NO_ERROR) 
				{
					// Convert data back into a BMessage.
					newMessage  = SafeUnflatten(buf);
					
					if (newMessage != NULL)
					{
						// Add an identifier of where it came from.
						newMessage->AddObject(SOURCE_SOCKET, (BObject*)&socket);
					}
					else result = B_ERROR;
				}
				free(buf);
			}
		}
		else if (msgSize > 0) result = B_ERROR;
	}
	socket.RecvUnlock();
	*outMessage = newMessage;
	
	PRINT(("ReceiveNetMessage - EXIT\n"));
	return result;
}


SendNetMessage
--------------
Conversely to ReceiveNetMessage, SendNetMessage sends a BMessage through a socket. The
BMessage's Flatten() method is used, following by a call to another utility, 
SendNetMessageData(). Note that it's our responsibility to free the resulting 
buffer.

long SendNetMessage(BTSSocket* socket, BMessage* inMessage)
{
	// Converts a BMessage into a buffer and sends it over the specified socket.

	char* 	buf = NULL;
	long 	numBytes;
	long 	result = B_NO_ERROR;
	
	PRINT(("SendNetMessage - ENTER\n"));
	inMessage->Flatten(&buf, &numBytes);

	if (numBytes > 0 && buf != NULL)
	{ 
		result = SendNetMessageData(socket, numBytes, buf);
	}
	if (buf != NULL)	free(buf);
	
	PRINT(("SendNetMessage - EXIT\n"));
	return result;
}


SendNetMessageData
------------------
A generic routine to send net message data, by first sending an id, then the message 
buffer size, then the buffer itself. Network data can be sent by using 
BTSSocket::Send() method, which calls the network kit send() function. In DR8, send() 
always blocks until all the data is sent or the socket is interrupted.

long SendNetMessage(const BTSSocket& socket, BMessage* inMessage)
{
	// Converts a BMessage into a buffer and sends it over the specified socket.

	char* 	buf = NULL;
	long 	numBytes;
	long 	result = B_NO_ERROR;
	
	PRINT(("SendNetMessage - ENTER\n"));
	inMessage->Flatten(&buf, &numBytes);

	if (numBytes > 0 && buf != NULL)
	{ 
		result = SendNetMessageData(socket, numBytes, buf);
	}
	if (buf != NULL)	free(buf);
	
	PRINT(("SendNetMessage - EXIT\n"));
	return result;
}


A semaphore is used to lock the socket for sending, so that the message data
doesn't get mixed up with some other message if simultaneous sends are occurring.
But, be careful when using semaphores with sockets, it can get you in trouble. 
Here's why: Suppose a client and server send to each other at the same time, and 
both send buffers that are bigger than the other side's receive buffer. If each 
socket object has a semaphore that locks *all* socket activity, the semaphore may
be held during the send. However, since the buffer sent is bigger than the 
receiver's receive buffer, then the send will interrupt before finishing. 
Continued attempts to send the rest of the data will continue to fail until the 
other side receives some of the data, thus making space for the rest of the data.
But, if both sides are holding socket-specific semaphores, receive can't occur on
either side, so both sides can become deadlocked. This is why this example uses 
separate sempaphores for send and receive.



BTSSocket
=========

You don't have to read this section to use the networking classes described 
above. However, if you want to know more about the built-in networking functions,
read on.


Receiving data on a socket
--------------------------

From BTSSocket::Recv():

long
BTSSocket::Recv(const char* buf, const long bufSize) const
{
	// Receives a network data buffer of a certain size. Does not return until
	// the buffer is full or if the socket returns 0 bytes (meaning it was 
	// closed) or returns an error besides EINTR. (EINTR can be generated when a
	// send() occurs on the same socket.
	
	long result = B_NO_ERROR;	// error value of socket calls
	int  receivedBytes = 0;		
	int  numBytes = 0;
	
	PRINT(("SOCKET %d RECEIVE: ENTER \n", fID));

	while (receivedBytes < bufSize && (result == B_NO_ERROR || result == EINTR))
	{
		PRINT(("Receiving %ld bytes on %d\n", bufSize- receivedBytes, GetID()));
		numBytes = ::recv(fID, (char*)(buf+receivedBytes), 
							bufSize - receivedBytes, 0);
		
		if (numBytes == 0)
		{
			result = ECONNABORTED;
			break;
		}
		else if (numBytes < 0) 
		{
			PRINT(("error when receiving data!\n"));
			result = errno;
		}
		else 
		{
			receivedBytes += numBytes;
			#if DEBUG
			UpdateReceiveCount(numBytes);
			#endif
		}
	}
	PRINT(("SOCKET %d RECEIVE - Received %ld bytes result is %s\n", fID, numBytes,
				strerror(result)));

	if (result == EINTR && receivedBytes == bufSize) result = B_NO_ERROR;
	PRINT(("SOCKET %d RECEIVE: EXIT\n", fID));
	return result;
}


This method shows a couple of things. First, receives can be interrupted, either
by an error or by someone else doing something on the same socket. If the socket
is interrupted by someone else, recv will return a number of bytes less than
the buffer size. You then need to loop to get the rest of the buffer. (errno 
will also return EINTR when this occurs.) If the number of bytes returned is less
than zero and the error is not EINTR, something bad has happened. If 
bytes returned == 0, the socket connection has closed.

The value of errno is ONLY valid after receiving a -1 from recv(). Don't check it if 
you get a postive number or 0. 

Sending data on a socket
------------------------

long
BTSSocket::Send(const char* buf, const long bufSize) const
{
	// Sends the data for a BMessage over a socket, preceded by the message's
	// size.
	
	long 	result = B_NO_ERROR;
	int 	numBytes = -1;
	int		sentBytes = 0;
	PRINT(( "SOCKET SEND - ENTER, %ld bytes on socket %d\n", bufSize, fID));
	if (bufSize > 0 && buf != NULL)
	{
		while (sentBytes < bufSize && result == B_NO_ERROR || result == EINTR)
		{
			PRINT(("SOCKET SEND - Sending data..\n"));
			numBytes = ::send(fID, buf, bufSize, 0);
			if (numBytes < 0) result = errno;
			if (numBytes > 0) sentBytes += numBytes;
			if (sentBytes < numBytes) result = errno;
			#if DEBUG
			else UpdateSendCount(sentBytes);
			#endif
		}
	}	
	PRINT( ("SOCKET SEND - EXIT, sent %ld bytes on %d result is %s\n", sentBytes, 
				fID, strerror(result)));
	return result;
}



Similar to receiving, send needs to make sure that all the data has been sent, and
if it hasn't it must loop and send the rest.This usually occurs because a single send
tries to put more data in the receiver's buffer than it can hold.


Other Notes
===========

There are a few problems with DR8 networking. Specifically, simultaneous sending
and receiving on a socket can cause lockups. This is improved in DR9. 

The use of BMessages as the protocol is not terribly efficient in terms of speed or
size. For a network-intensive application, you would likely want to implement your 
own lower-level protocol. But, if you just need some simple networking so that your
apps can communicate with each other, this approach is fast and easy, and probably
integrates well into what you've already written.

You can use ps to get some information about what's going on on your application.
For example, if you have one server and one client running you see this:

DrawServer (team 26)
    201           DrawServer  sem  10       6      25 rAppLooperPort(13421)
    205        w>Draw Server  sem  15       7       5 Draw Server(13519)
    207                       sem  10       0       1 LooperPort(13535)
    208 Server Request Handler  sem 110       0      15 tcp_receive[201][0](13539)
    209      Client Listener  sem 110       0       0 tcp_receive[208][1](13792)
DrawClient (team 27)
    216           DrawClient  sem  10       7      43 rAppLooperPort(13658)
    224                       sem  10       0       0 LooperPort(13760)
    226 Client socket listener  sem  10       0       1 tcp_receive[216][0](13776)
    228    w>Net Draw Client  sem  15       6       5 Net Draw Client(13806)
net_server (team 14)
     72           net_server  sem  10       6      11 LooperPort(9064)
     86             net main  sem  10       4      16 timeout wait(9082)
     93         ether reader  sem  15       2       8 mace read(9181)
     94        socket server  msg  10       0       2
     95         ether thread  sem  10       5       1 etherwait1(9204)
     96        loopip thread  sem  10       0       0 loop wait(9205)
     97       timeout thread  sem  10       0       0 timeout cancel(9081)
    104       sock:4253,4254  sem  10       0       0 tcp_send[103][0](9349)
    109       sock:4326,4328  sem  10       0       0 tcp_send[108][0](9546)
    206       sock:5879,5880  sem  10       0       0 tcp_send[201][0](13537)
    222       sock:5963,5964  sem  10       0       1 tcp_send[216][0](13774)
    225       sock:5965,5966  sem  10       1       1 tcp_send[208][1](13790)

There's two threads blocked on tcp_receive in the server; one is actually blocked
on select (209), the other on accept (208), but this is how they show up. The 
client also has a thread (226) blocked on select. At the end of the semaphore
name, there are two numbers in square brackets; the first is the thread id of
the thread that created the socket, and the second is the socket descriptor.

In the net server, there's three threads (206, 222, 225) related to the server
and client. Each one represents a 'live' (whether active or not) socket.

When two clients are opened, the effect of this on the select (thread spawning)
in the server can be seen (threads 209, 284, and 285). So, multi-socket selects 
can be pretty inefficient. 

DrawServer (team 26)
    201           DrawServer  sem  10       6      26 rAppLooperPort(13421)
    205        w>Draw Server  sem  15       7       5 Draw Server(13519)
    207                       sem  10       1       2 LooperPort(13535)
    208 Server Request Handler  sem 110       0      31 tcp_receive[201][0](13539)
    209      Client Listener  sem 110      44      37 select sem(21244)
    284        select thread  sem  10       0       0 tcp_receive[208][1](13792)
    285        select thread  sem  10       0       0 tcp_receive[208][2](21012)
net_server (team 14)
     72           net_server  sem  10       6      11 LooperPort(9064)
     86             net main  sem  10      22      47 timeout wait(9082)
     93         ether reader  sem  15       9      34 mace read(9181)
     94        socket server  msg  10       0       4
     95         ether thread  sem  10      26       4 etherwait1(9204)
     96        loopip thread  sem  10       1       1 loop wait(9205)
     97       timeout thread  sem  10      20      25 timeout cancel(9081)
    104       sock:4253,4254  sem  10       0       0 tcp_send[103][0](9349)
    109       sock:4326,4328  sem  10       0       0 tcp_send[108][0](9546)
    206       sock:5879,5880  sem  10       0       0 tcp_send[201][0](13537)
    222       sock:5963,5964  sem  10      19      34 tcp_send[216][0](13774)
    225       sock:5965,5966  sem  10      34      35 tcp_send[208][1](13790)
    262       sock:8878,8879  sem  10       0       1 tcp_send[256][0](20965)
    265       sock:8880,8881  sem  10       1       1 tcp_send[208][2](21010)
DrawClient (team 27)
    216           DrawClient  sem  10       7      43 rAppLooperPort(13658)
    224                       sem  10       0       0 LooperPort(13760)
    226 Client socket listener  sem  10      25      32 tcp_receive[216][0](13776)
    228    w>Net Draw Client  sem  15       6       5 Net Draw Client(13806)
DrawClient (team 29)
    256           DrawClient  sem  10       7      42 rAppLooperPort(20743)
    264                       sem  10       0       0 LooperPort(20940)
    266 Client socket listener  sem  10       0       1 tcp_receive[256][0](20967)
    268    w>Net Draw Client  sem  15       2       1 Net Draw Client(21026)


Also included in the source is a simple chat client and server, using the same 
basic client, server, and socket classes. 
