C++ iostream socket.

General, off-topic discussion about things of interest to the members of these forums.

Moderator: cheriff

Post Reply
Oobles
Site Admin
Posts: 347
Joined: Sat Jan 17, 2004 9:49 am
Location: Melbourne, Australia
Contact:

C++ iostream socket.

Post by Oobles »

Here's a little something for anyone working with sockets in C++. I needed an iostream to access a socket. The correct way to do this is to implement a subclass of stream_buf. Scanning the net I found a number of large C++ socket libraries, but no bare bones examples. I did find some examples of either bad examples or GPL code.

So this is just a very simple example of where to start released into the public domain. Do as you please with it. It still needs some work, so if you make improvements, it would be nice to reply here or publish as public domain aswell.

Oobles.

Code: Select all

// Created by David Ryan.
// Released into public domain Feb 2004.
// Please improve and set free again.

using namespace std;

#include <streambuf>
#include <iostream>

#include <netdb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

#ifndef TIOCINQ
#define	TIOCINQ FIONREAD
#endif

class socket_buf &#58; public streambuf
&#123;
private&#58;
   int _socket;
   char _inBuffer&#91;1024&#93;;  
   char _outBuffer&#91;1024&#93;;

public&#58;
   socket_buf&#40; int socket &#41;
   &#123;
      _socket = socket;
      setg&#40; _inBuffer, _inBuffer, _inBuffer &#41;;
      setp&#40; _outBuffer, _outBuffer+1024 &#41;;
   &#125;

protected&#58;
	int overflow&#40;int x&#41;
	&#123;
		cout << "socket_buf&#58;&#58;overflow&#40;&#41;" << x << endl;
		return x;
	&#125;
	
	int sync&#40;&#41;
	&#123;
		cout << "socket_buf&#58;&#58;sync&#40;&#41;" << endl;
		
		// if no data availalbe just return.
		if &#40; pbase&#40;&#41; == pptr&#40;&#41; &#41;
			return 0;
		
		// try and send the data.
		int len = pptr&#40;&#41; - pbase&#40;&#41;;
		int rc = send&#40; _socket, pbase&#40;&#41;, len, 0 &#41;;
		if &#40; rc < 0 &#41;
			return rc;
			
		setp&#40; _outBuffer, _outBuffer+1024 &#41;;
		return 0;
	&#125;

	int underflow&#40;&#41;
	&#123;
		cout << "socket_buf&#58;&#58;underflow&#40;&#41; " << endl;
		
		if &#40;gptr &#40;&#41; < egptr &#40;&#41;&#41; return *&#40;unsigned char*&#41;gptr &#40;&#41;;

		int len;
		
		// find out how much data is available.
		if&#40; ioctl&#40; _socket, TIOCINQ, &len&#41; < 0 &#41;
		&#123;
			// error
			cout << "socket_buf error" << endl;
			len = 1;
		&#125;
		
		// make sure length is atleast 1. We will block.
		if &#40; len == 0 &#41;
			len = 1;
		
		// try and read in some data.
		int read = recv&#40; _socket, &_inBuffer, len, 0 &#41;;
		if &#40; read > 0 &#41;
		&#123;
			setg&#40; _inBuffer, _inBuffer, _inBuffer+read &#41;;
		&#125;
		else
		&#123;
			return EOF;	
		&#125;
		
		return *&#40;unsigned char*&#41;gptr &#40;&#41;;   	
	&#125;

&#125;;


class siostream&#58; public iostream
&#123;
private&#58;
   socket_buf _buf;

public&#58;
   siostream&#40; int socket &#41;
   &#58;_buf&#40; socket &#41;, iostream&#40; &_buf &#41;
   &#123;
   &#125;
&#125;;


int main&#40; int argc, char *argv&#91;&#93; &#41;
&#123;
	int port = 95;
	
	cout << "test socket" << endl;
	
	struct hostent *he=gethostbyname&#40;argv&#91;1&#93;&#41;;
	struct sockaddr_in sa;
	memset&#40;&sa, 0, sizeof&#40;sa&#41;&#41;;
	sa.sin_family = PF_INET;
	sa.sin_addr = *&#40;&#40;struct in_addr *&#41;he->h_addr&#41;;       
	sa.sin_port = htons&#40;port&#41;; 
	         
	int s = socket&#40;AF_INET, SOCK_STREAM, 0&#41;;
	int rv = connect&#40; s, &#40;struct sockaddr*&#41; &sa, sizeof&#40;sa&#41; &#41;;
	
	cout << "connect = " << rv << endl;
	
	siostream sock&#40; s &#41;;
	
	sock << "23" << endl;
	sock.flush&#40;&#41;;
	cout << "got =" << sock.get&#40;&#41; << endl;
	
	cout << "finished" << endl;
&#125;
nosense

Post by nosense »

I did exactly this a few years ago & the issue I had was correctly handling socket errors. Who should handle socket specific exceptions? given that the calling code probably shouldn't know the stream sits on a socket. This is especially critical for handling drop outs & retries.
Oobles
Site Admin
Posts: 347
Joined: Sat Jan 17, 2004 9:49 am
Location: Melbourne, Australia
Contact:

Post by Oobles »

My thought is that any errors on send/recv should cause the stream to close. A problem with the protocol stream breaking is going to cause the protocol to need to retry at a higher level. It would be too painful to keep the state of the protocol at this layer and be able to get to the same place easily.

I'd be interesting if you can see any problems in my implementation so far? Did you over-ride any other methods in the stream_buf?

Oobles.
Post Reply