This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Network Stream Submitted by

Network streams is a set of easy-to-use classes to deal with peer-to-peer communication over the Internet. It uses << and >> operators to stream pre/user defined datatypes, and commands for sending/receiving arbitrary buffers of data over a connection. It operates on a very low level, making it independent of any application-level protocol. I have attached a sample file. server.cpp, which creates a server and wait for someone to connect. Once connected, it starts listening for incomming data, which is extracted, displayed on the standard output on the server, and sent back to the client. You can take a telnet client and connect to the server on the proper port. When you type something, it will show up on the server, and in your telnet window. /Mikael a.k.a. Bob or Brother Bob

Currently browsing [netstream.zip] (8,465 bytes) - [netstream/netstream.cpp] - (19,228 bytes)

    /********************************************************************

    	filename:   netstream.cpp
    	author:     Mikael Swartling
    	e-mail:     e98msv@du.se

    	description:
    	------------
    	Network streams is a set of easy-to-use classes to deal with peer-to-peer
    	communication over the Internet. It uses << and >> operators to stream
    	pre/user defined datatypes, and commands for sending/receiving arbitrary
    	buffers of data over a connection. It operates on a very low level, making
    	it independent of any application-level protocol.

    	-----

    	This software is provided 'as-is', without any express or implied warranty.
    	In no event will the author be held liable for any damages arising from the
    	use of this software.

    	Permission is hereby granted to use, copy, modify, and distribute this
    	source code, or portions hereof, for any purpose, without fee, subject to
    	the following restrictions:

    	1. The origin of this source code must not be misrepresented. You must not
    	   claim that you wrote the original software. If you use this software in
    	   a product, an acknowledgment in the product documentation would be
    	   appreciated but is not required.

    	2. Altered versions must be plainly marked as such and must not be
    	   misrepresented as being the original source.

    	3. This notice must not be removed or altered from any source distribution.

    *********************************************************************/

    #ifdef _WIN32

    #include <winsock2.h>

    #else

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <arpa/inet.h>

    #endif

    #include <sstream>
    #include "netstream.h"

    // --------------------------------------------------------------------------------
    // Winsock
    // --------------------------------------------------------------------------------
    #ifdef _WIN32

    // --------------------------------------------------------------------------------
    // Used to start/clean up WinSock on Windows platforms.
    // WinsockStartup() must be called once before you connect/listen.
    // WinsockCleanup() is called before program is closing.
    //
    // return value:
    //   true if no error occured
    //   false otherwise
    // --------------------------------------------------------------------------------
    bool WinsockStartup()
    {
    	WSADATA versionData;
    	int versionRequested = MAKEWORD(1, 0);

    	return (WSAStartup(versionRequested, &versionData) == 0) ? true : false;
    }

    bool WinsockCleanup()
    {
    	return (WSACleanup() == 0) ? true : false;
    }

    #endif // _WIN32

    // --------------------------------------------------------------------------------
    // Local utility function.
    // Convert a hostname into a network byteorder integer.
    // --------------------------------------------------------------------------------
    bool getinetaddr(const string &host, sockaddr_in *inAddr)
    {
    	// inet_addr converts a string containing an IP address
    	// to network byteorder integer version of the IP.
    	inAddr->sin_addr.s_addr = inet_addr(host.c_str());

    	// INADDR_NONE is returned if no valid IP could be extracted.
    	if(inAddr->sin_addr.s_addr == INADDR_NONE)
    	{
    		// If no valid IP was extracted, chanses are that we have
    		// a domain name instead, so we try to translate the name
    		// to an IP.
    		hostent *hostEntry = gethostbyname(host.c_str());

    		// If this also fails, we are unable to determine the address.
    		if(hostEntry == NULL) return false;

    		// Take the IP address out of the hostent structure
    		unsigned long hostAddress = *((unsigned long*)*hostEntry->h_addr_list);
    		inAddr->sin_addr.s_addr = hostAddress;
    	}

    	return true;
    }

    // --------------------------------------------------------------------------------
    // netsocket class
    // --------------------------------------------------------------------------------
    // --------------------------------------------------------------------------------
    //
    // --------------------------------------------------------------------------------
    netsocket::netsocket()
    {
    	m_socket = 0;
    }

    // --------------------------------------------------------------------------------
    //
    // --------------------------------------------------------------------------------
    netsocket::~netsocket()
    {
    	return;
    }

    // --------------------------------------------------------------------------------
    // Allocate a new socket.
    // --------------------------------------------------------------------------------
    bool netsocket::create()
    {
    	close();

    	m_socket = socket(AF_INET, SOCK_STREAM, 0);
    	return m_socket ? true: false;
    }

    // --------------------------------------------------------------------------------
    // Close the socket
    // It will gracefully (as it's called in the MSDN docs) close the connection.
    //
    // for netserver: socket is closed, and no more connections can be accepted.
    // for netstream: stream is closed, and no more data can be sent/received.
    //
    // return value:
    //   true if socket/stream was successfully closed.
    // --------------------------------------------------------------------------------
    bool netsocket::close()
    {
    	if(isopen() == false) return true;

    	int res = 0;

    #ifdef _WIN32
    	res = closesocket(m_socket);
    #else
    	res = ::close(m_socket);
    #endif

    	m_socket = 0;

    	return res ? false: true;
    }

    // --------------------------------------------------------------------------------
    // Determines wether the socket is open or not.
    //
    // return value:
    //   for netserver: true if socket is listening for connections.
    //   for netstream: true if stream is open.
    // --------------------------------------------------------------------------------
    bool netsocket::isopen() const
    {
    	return m_socket ? true : false;
    }

    // --------------------------------------------------------------------------------
    // return value:
    //   The name of the host. Host is the computer on which the function was called.
    // --------------------------------------------------------------------------------
    string netsocket::gethostname() const
    {
    	sockaddr_in hostAddress;
    	int         addressLength = sizeof hostAddress;

    	if(::getsockname(m_socket, (sockaddr*)&hostAddress, &addressLength) == 0)
    	{
    		return string(inet_ntoa(hostAddress.sin_addr));
    	}

    	return string();
    }

    // --------------------------------------------------------------------------------
    // netstream class
    // --------------------------------------------------------------------------------
    // --------------------------------------------------------------------------------
    // Automatically closes the stream on desrtuction.
    // For example, if the user forgot to close the connection before exiting
    // the program, the desturctor will close the connection when it goes out of scope.
    // --------------------------------------------------------------------------------
    netstream::~netstream()
    {
    	close();
    }

    // --------------------------------------------------------------------------------
    // Attach a socket to this stream
    // --------------------------------------------------------------------------------
    bool netstream::attach(SOCKET socket)
    {
    	if(isopen() == true) return false;

    	m_socket = socket;
    	return m_socket ? true : false;
    }

    // --------------------------------------------------------------------------------
    // Connect the stream to a remote host.
    // There are two types of connect-functions, one takes hostname and port
    // as two different parameters, while the other takes them both in one parameter.
    // If you have the portnumber as a separate integer value, you call this
    // two-parameter variant.
    //
    // port:
    //   Port number as an unsigned integer.
    //
    // name:
    //   Name of the host to connect to as a string.
    //   name can either be an IP address or a domain name.
    //   ex. "192.168.1.1" or "host.domain.com"
    //
    // return value:
    //   true if connection was successfull (server accepted connection)
    //   false if connection failed, possible causes:
    //    - server rejected connection attempt
    //    - unable to resolve domain name or find IP
    //    - already connected
    // --------------------------------------------------------------------------------
    bool netstream::connect(const string &hostName, int hostPort)
    {
    	if(isopen() == true) return false;

    	sockaddr_in inAddr;
    	inAddr.sin_family = AF_INET;
    	inAddr.sin_port   = htons(hostPort);

    	if(getinetaddr(hostName, &inAddr) == false) return false;

    	if(create() == false) return false;
    	if(::connect(m_socket, (sockaddr*)&inAddr, sizeof inAddr) != 0) return false;

    	return true;
    }

    // --------------------------------------------------------------------------------
    // (see above for more information)
    //
    // This function requires the name/port to be of a certain format. The format is
    // the host name followed by the portnumber, separated by a space (' ')
    // and/or a colon (':').
    //
    // name and port are as described above
    //
    // valid formats are (among a few other combinations of spaces and colons):
    // "192.168.1.1:1234"           - name and port separated by a colon
    // "host.domain.com 1234"       - name and port separated by a space
    // "  host.domain.com :  1234 " - extra spaces does (should) not affect the result
    //
    // return value:
    //   same as above
    // --------------------------------------------------------------------------------
    bool netstream::connect(const string &hostNameAndPort)
    {
    	string t = hostNameAndPort;

    	int pos;

    	// Replace all colons with spaces
    	while((pos = t.find(":")) != string::npos);
    	{
    		t[pos] = ' ';
    	}

    	std::stringstream s;

    	// stringstream is like a custom I/O stream.
    	// We put the userformated input (but with no colons) into the stream...
    	s << t;

    	string hostName;
    	int hostPort;

    	// ... and extract a string and a portnumber just like
    	// we do from a regular input stream.
    	s >> hostName >> hostPort;

    	return connect(hostName, hostPort);
    }

    // --------------------------------------------------------------------------------
    // Send a buffer to the stream
    //
    // data:
    //   Pointer to buffer of data to send.
    //
    // len:
    //   Number of bytes to send.
    //
    // return value:
    //   true if successfully sent
    //   false if failed to send
    // --------------------------------------------------------------------------------
    bool netstream::send(const void *data, int len) const
    {
    	if(isopen() == false) return false;

    	if(::send(m_socket, (const char *)data, len, 0) == SOCKET_ERROR)
    	{
    		return false;
    	}

    	return true;
    }

    // --------------------------------------------------------------------------------
    // Receive data from the stream
    //
    // data:
    //   Pointer to buffer where the data is to be stored.
    //
    // len:
    //   Number of bytes to receive.
    //
    // len number of bytes is read from the stream. If len is less than the number
    // of available bytes in the stream, recv will block until more data has arrived
    // and the entire buffer is filled.
    //
    // return value:
    //   number of bytes received
    // --------------------------------------------------------------------------------
    int netstream::recv(void *data, int len) const
    {
    	if(isopen() == false) return false;

    	int bytesReceived = 0;

    	// Keep on extracting until the whole buffer is filled
    	while(bytesReceived < len)
    	{
    		bytesReceived += ::recv(m_socket, ((char *)data) + bytesReceived, len - bytesReceived, 0);
    	}

    	return bytesReceived;
    }

    // --------------------------------------------------------------------------------
    // Check wether there are data to be read from the stream or not.
    //
    // This function will also check wether the peer is alive or not.
    // If the connection was interrupted for some reason, then connection
    // is automatically closed on this side aswell, to prevent handling a closed
    // connection.
    //
    // Possible causes of an interrupted connection:
    //  - peer closed connection
    //  - lost connection to peer
    //  - remote computer halted/rebooted
    //
    // return value:
    //   number of bytes pending to receive
    //
    //   If a connection is interrupted, canrecv() will return zero and close
    //   the connection. Any following call to isopen() will return false, and you
    //   can assume the connection was interrupted in one way or another.
    // --------------------------------------------------------------------------------
    int netstream::canrecv()
    {
    	fd_set readFD;
    	readFD.fd_count    = 1;
    	readFD.fd_array[0] = m_socket;

    	timeval timeout;
    	timeout.tv_sec  = 0;
    	timeout.tv_usec = 0;

    	// Select is used to determine status on one or more sockets.
    	// readFD contains all sockets we want to look at, in this case we only want
    	// to look at one socket, the socket used by the stream.
    	// Select returns the number of sockets who wants to tell us something.
    	if(select(0, &readFD, NULL, NULL, &timeout) != 0)
    	{
    		// This socket want to tell us something, now we need to
    		// determine what it want.

    		byte data;

    		// We use MSG_PEEK so we don't remove any important information
    		// for the user. ::recv returns the number of bytes pending to receive.
    		int bytesPending = ::recv(m_socket, &data, 1, MSG_PEEK);

    		// If no bytes where extracted, it's trying to tell us something else.
    		// In this case, it can only tell us one thing: the connection was lost/closed.
    		// We close the connection on our side aswell, and tell the suer no bytes is
    		// waiting to be read.
    		if(bytesPending == 0) close();

    		// Return the number of bytes pending.
    		return bytesPending;
    	}

    	// If we get here, the socket don't have anything to tell us,
    	// and therefore no bytes can be read.
    	return 0;
    }

    // --------------------------------------------------------------------------------
    // return value:
    //   Name of peer. Peer is the computer "in the other end" of the stream.
    // --------------------------------------------------------------------------------
    string netstream::getpeername() const
    {
    	sockaddr_in hostAddress;
    	int         addressLength = sizeof(hostAddress);

    	if(::getpeername(m_socket, (sockaddr*)&hostAddress, &addressLength) == 0)
    	{
    		return string(inet_ntoa(hostAddress.sin_addr));
    	}

    	return string();
    }

    // --------------------------------------------------------------------------------
    // Stream a string to the other computer.
    // --------------------------------------------------------------------------------
    netstream &netstream::operator <<(const string &data)
    {
    	send(data.c_str(), data.length() + 1);

    	return *this;
    }

    // --------------------------------------------------------------------------------
    // Extract a string from the stream.
    // --------------------------------------------------------------------------------
    netstream &netstream::operator >>(string &data)
    {
    	char c;
    	string t = string();

    	// Extract byte after byte, untill a null character is extarcted, and insert
    	// it into the string.
    	while(true)
    	{
    		recv(&c, 1);
    		if(c == '\0') break;

    		t += c;
    	}

    	data = t;

    	return *this;
    }

    // --------------------------------------------------------------------------------
    // Stream a null terminated character array.
    // --------------------------------------------------------------------------------
    netstream &netstream::operator <<(const char *data)
    {
    	string t = data;

    	// Since char* is naturally ended with a null character, we don't need to
    	// go through the process of finding extra null characters.
    	send(t.c_str(), t.length() + 1);

    	return *this;
    }

    // --------------------------------------------------------------------------------
    // netserver class
    // --------------------------------------------------------------------------------
    // --------------------------------------------------------------------------------
    //
    // --------------------------------------------------------------------------------
    netserver::~netserver()
    {
    	close();
    }

    // --------------------------------------------------------------------------------
    // Tell socket to listen for incomming connections.
    //
    // port:
    //   The port number to listen on.
    //
    // interface:
    //   Specifies which interface to use to listen on. Interface is the specific
    //   adapter to listen on. Usefull if you have more than one network adaper but
    //   only wants to listen for connection on one of them.
    //
    // backlog:
    //   The maximum number of incomming connections that can wait for
    //   acception at any given time, before connections are automatically rejected
    //   by the socket handler (this happens on driver/os level, and not by these
    //   functions).
    //
    // interface and backlog are optional values and can be ignored.
    // interface defaults to an empty string, which will listen to any adapter.
    // backlog defaults to 5 incomming connections.
    //
    // return value:
    //   true if socket successfully placed into a listening state
    //   false otherwise
    // --------------------------------------------------------------------------------
    bool netserver::listen(int port, const string &adapter, int backlog)
    {
    	if(isopen() == true) return false;

    	sockaddr_in inAddr;
    	inAddr.sin_family = AF_INET;
    	inAddr.sin_port   = htons(port);

    	if(adapter.length() == 0)
    	{
    		// If no string is passed, we assume any adapter is valid...
    		inAddr.sin_addr.s_addr = INADDR_ANY;
    	}
    	else
    	{
    		// ... otherwise we need to get the IP of the adapter
    		if(getinetaddr(adapter, &inAddr) == false) return false;
    	}

    	if(create() == false) return false;
    	if(::bind(m_socket, (sockaddr*)&inAddr, sizeof(inAddr)) != 0) return false;
    	if(::listen(m_socket, backlog) != 0) return false;

    	return true;
    }

    // --------------------------------------------------------------------------------
    // Accept an incomming connection and attach it to a netstream.
    //
    // socket:
    //   The stream to which you want to attach the incomming connection.
    //
    // return value:
    //   true if socket was successfully attached to stream
    //   false otherwise
    // --------------------------------------------------------------------------------
    bool netserver::accept(netstream& socket) const
    {
    	// Accept the incomming socket
    	SOCKET s = ::accept(m_socket, NULL, 0);

    	if(s == INVALID_SOCKET) return false;

    	// Attach the socket to a stream
    	return socket.attach(s);
    }

    // --------------------------------------------------------------------------------
    // Checks is there is an incomming connection waiting
    //
    // return value:
    //   true if there is a connection waiting
    //   false if no connection is waiting
    // --------------------------------------------------------------------------------
    bool netserver::canaccept() const
    {
    	fd_set readFD;
    	FD_ZERO(&readFD);
    	FD_SET(m_socket, &readFD);

    	timeval timeout;
    	timeout.tv_sec  = 0;
    	timeout.tv_usec = 0;

    	// Select is described in canrecv()
    	int res = select(1, &readFD, NULL, NULL, &timeout);

    	// If a socket is trying to tell us something, and this socket is
    	// in a listening state, it means someone is trying to connect to us.
    	return res ? true : false;
    }

Currently browsing [netstream.zip] (8,465 bytes) - [netstream/netstream.h] - (4,263 bytes)

    /********************************************************************

    	filename:   netstream.h
    	author:     Mikael Swartling
    	e-mail:     e98msv@du.se

    	description:
    	------------
    	Network streams is a set of easy-to-use classes to deal with peer-to-peer
    	communication over the Internet. It uses << and >> operators to stream
    	pre/user defined datatypes, and commands for sending/receiving arbitrary
    	buffers of data over a connection. It operates on a very low level, making
    	it independent of any application-level protocol.

    	-----

    	This software is provided 'as-is', without any express or implied warranty.
    	In no event will the author be held liable for any damages arising from the
    	use of this software.

    	Permission is hereby granted to use, copy, modify, and distribute this
    	source code, or portions hereof, for any purpose, without fee, subject to
    	the following restrictions:

    	1. The origin of this source code must not be misrepresented. You must not
    	   claim that you wrote the original software. If you use this software in
    	   a product, an acknowledgment in the product documentation would be
    	   appreciated but is not required.

    	2. Altered versions must be plainly marked as such and must not be
    	   misrepresented as being the original source.

    	3. This notice must not be removed or altered from any source distribution.

    *********************************************************************/

    #ifndef __NETSTREAM_H__
    #define __NETSTREAM_H__

    // --------------------------------------------------------------------------------
    // Add the WinSock library
    // --------------------------------------------------------------------------------
    #ifdef _WIN32
    #include <winsock2.h>
    #pragma comment(lib, "ws2_32.lib")
    #endif

    // --------------------------------------------------------------------------------
    //
    // --------------------------------------------------------------------------------
    #include <string>

    typedef std::string string;

    // --------------------------------------------------------------------------------
    // On windows we need to setup WinSock before using sockets.
    // --------------------------------------------------------------------------------
    #ifdef _WIN32
    bool WinsockStartup();
    bool WinsockCleanup();
    #endif

    // --------------------------------------------------------------------------------
    // netsocket:
    // It provides some functions that is common to both netstream and netserver.
    // --------------------------------------------------------------------------------
    class netsocket
    {
    public:

    	netsocket();
    	virtual ~netsocket();

    	bool   close();
    	bool   isopen() const;
    	string gethostname() const;

    protected:

    	bool create();

    protected:

    	SOCKET m_socket;
    };

    // --------------------------------------------------------------------------------
    // netsream:
    // Stream data from/to another computer.
    // --------------------------------------------------------------------------------
    class netstream : public netsocket
    {
    public:
    	netstream() : netsocket() {}
    	~netstream();

    	bool attach(SOCKET socket);

    	bool connect(const string &hostName, int hostPort);
    	bool connect(const string &hostNameAndPort);

    	bool send(const void *data, int len) const;
    	int  recv(      void *data, int len) const;

    	int  canrecv();

    	string getpeername() const;

    	template<typename T> netstream &operator <<(const T &data) {send(&data, sizeof T);return *this;}
    	template<typename T> netstream &operator >>(      T &data) {recv(&data, sizeof T);return *this;}

    	netstream &operator <<(const string &data);
    	netstream &operator >>(      string &data);

    	netstream &operator <<(const char   *data);
    };

    // --------------------------------------------------------------------------------
    // netserver:
    // The class for dealing with incomming connections.
    // --------------------------------------------------------------------------------
    class netserver : public netsocket
    {
    public:
    	netserver() : netsocket() {}
    	~netserver();

    	bool listen(int port, const string &adapter = string(), int backlog = 5);
    	bool accept(netstream &socket) const;

    	bool canaccept() const;
    };

    #endif // __NETSTREAM_H__

Currently browsing [netstream.zip] (8,465 bytes) - [netstream/server.cpp] - (1,770 bytes)

    #include <iostream>
    #include <netstream/netstream.h>

    using namespace std;

    const int port = 1234;

    void main(void)
    {
    	netserver server;
    	netstream remote;

    #ifdef _WIN32
    	WinsockStartup();
    #endif

    	// Place the server in listening state
    	if(server.listen(port) == false)
    	{
    		cout << "Unable to start server" << endl;
    		return;
    	}

    	cout << "Waiting for new connection" << endl;

    	// Wait until someone connects. Check every second.
    	while(server.canaccept() == false) Sleep(1000);

    	// Accept the new connection and attach it to remote
    	if(server.accept(remote) == false)
    	{
    		cout << "Unable to accept new connection" << endl;
    		return;
    	}

    	// Close the server.
    	// Note that this only closes the server socket so new connections
    	// cannot be established. It does not close any connection already established.
    	server.close();

    	// Send a message to our new user, telling him/her we have a connection.
    	remote << "Connection established" << "\n\r";
    	cout   << "Connection accpeted" << endl;

    	while(true)
    	{
    		byte c;

    		if(remote.canrecv() != 0)
    		{
    			// Receive a byte from the stream
    			remote >> c;

    			// If it was a lower case 'q', exit the server loop
    			if(c == 'q') break;

    			// Print the character and send if back
    			cout << c;
    			remote << c;
    		}

    		// If the connection is suddenly no longer open, canrecv probably noticed
    		// that we lost connection to the host and closed it.
    		if(remote.isopen() == false)
    		{
    			cout << "Lost connection to remote host" << endl;
    			return;
    		}

    		Sleep(10);
    	}

    	// Stream a string to the remote computer and close the conneciton.
    	remote << "Closing connection" << "\n\r";
    	remote.close();

    #ifdef _WIN32
    	WinsockCleanup();
    #endif
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.