[Assorted-commits] SF.net SVN: assorted:[1200] sandbox/trunk/src/cc/sockstream.cc
Brought to you by:
yangzhang
From: <yan...@us...> - 2009-02-19 06:58:54
|
Revision: 1200 http://assorted.svn.sourceforge.net/assorted/?rev=1200&view=rev Author: yangzhang Date: 2009-02-19 06:58:42 +0000 (Thu, 19 Feb 2009) Log Message: ----------- added sockstream demo Added Paths: ----------- sandbox/trunk/src/cc/sockstream.cc Added: sandbox/trunk/src/cc/sockstream.cc =================================================================== --- sandbox/trunk/src/cc/sockstream.cc (rev 0) +++ sandbox/trunk/src/cc/sockstream.cc 2009-02-19 06:58:42 UTC (rev 1200) @@ -0,0 +1,296 @@ +// +// sockstream.cc - various tests on socket streams, using C++ with homemade +// networkbuf, C++ with gnu's stdio_filebuf, and C with +// plain old fopen and friends. Compiles on gcc 3.3.4. +// +// Copyright 2005, Chris Frey. To God be the glory. +// You are free to use, modify, redistribute, and sublicense this code, +// as long as this copyright message is not removed from the source, +// and as long as the existence of any changes are noted in the source +// as well. (i.e. You don't need a complete history, just don't claim +// that the modified code was written entirely by me -- include your own +// copyright notice as well.) +// +// If you find this code useful, please email me and let me know. +// +// Conclusion: +// C++'s getline() processes the stream one character at a time, in order +// to search for the delimiter. Therefore, with networkbuf, which only +// fills the input buffer as much as it can with the available network +// data and then returns, it doesn't block until it really needs to, +// and gets all available lines from the kernel, even with a nice large +// buffer. +// +// With stdio_filebuf, this is built on top of C's streams, which has its +// own buffer. So the likelyhood of blocking before getting all the +// data is higher, since C's buffers may empty and get forced to fill +// with an internal fread() while C++'s getline is only asking for a +// single char. +// +// C++ streambufs and derived classes are actually pretty cool. I just +// wish they had used more user-friendly names. But as is, you can +// still use functions like xsgetn and xsputn as read and write, and +// the streambuf supplies all the needed buffering. Plus you can do it +// on a character basis, while still maintaining efficiency with +// buffered kernel calls. Plus there are iterators to work with these +// things, which I haven't fully investigated. +// +// Time to rethink my design of reuse lib's buffer classes, and turn +// them into streambufs perhaps, or at least derive streambuf interface +// classes to make use of them. Also, the transfer classes should be +// able to use streambufs as well. +// +// This stuff is complicated, and not commonly well documented, but +// it sure is useful. +// +// Chris Frey +// <cd...@ne...> +// 2005/02/13 +// + +// c++ io +#include <iostream> +#include <ext/stdio_filebuf.h> + +// c io +#include <cstdio> + +// networking +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +// posix io +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +// debugging +#include <assert.h> + +using namespace std; + +void cpp_networkbuf(int s); +void cpp_stdio_filebuf(int s); +void cpp_work(istream &nin, ostream &nout); +void c_work(int s); + +// +// networkbuf - gives a simple buffer to posix read/write calls, suitable +// for use with a network. Do not use this for plain files, +// since there is no conflict checking code for reading/writing +// to different sections of the same file. It is meant for +// processing two independent streams, just like a network. +// +class networkbuf : public streambuf +{ + int _fd; + char_type *_getbuf, *_putbuf; + size_t _getbuf_size; + size_t _putbuf_size; + +public: + networkbuf(int fd, size_t size) + : _fd(fd) + { + _getbuf = new char_type[size]; + _getbuf_size = size; + // set the input buffer to "nothing there" + setg(_getbuf, _getbuf, _getbuf); + + _putbuf = new char_type[size + 1]; // one more, for when + // overflow has extra byte + _putbuf_size = size; + // set the output buffer to "empty" + setp(_putbuf, _putbuf + _putbuf_size); + } + ~networkbuf() { + sync(); + close(_fd); + delete [] _getbuf; + _getbuf_size = 0; + delete [] _putbuf; + _putbuf_size = 0; + } + + // write out any data in the out buffer, and write c as well if c!=eof() + virtual int_type overflow (int_type c) { +// cout << "overflow called: " << c << endl; + assert( pbase() ); + bool have_extra = c != traits_type::eof(); + + // pbase() is a pointer at the start of our buffer, + // and pptr() is a pointer to the next free spot, + // so we can check for data in buffer by comparing them + if( pptr() > pbase() || have_extra ) { + // we have something to write! + ssize_t count = pptr() - pbase(); + if( have_extra ) { + // tack extra value onto the end of the buf + // (note constructor added a byte for us here, + // just in case) + *(pptr()) = traits_type::to_char_type(c); + count++; + } + + // loop and write all bytes, even if ::write() returns + // less than what we asked for + ssize_t written; + char_type *wp = pbase(); + do { + written = ::write(_fd, wp, count); + if( written > 0 ) { + count -= written; + assert(count >= 0); + wp += written; + } + else { + // fixme... do we set badbit here? + // failbit? + return traits_type::eof(); + } + } while(count); + // reset output buffer to empty state + setp(_putbuf, _putbuf + _putbuf_size); + } + return traits_type::not_eof(c); + } + + // called on ostream::flush()... return -1 here on failure + virtual int sync() { + if( overflow(traits_type::eof()) == traits_type::eof() ) + return -1; + return 0; + } + + // fill the empty buffer, and return the first char in it, + // without advancing the pointer. if EOF, return eof() and + // leave gptr() == egptr() (this is the default, since this + // function won't be called if this wasn't the case, so we + // just do nothing if EOF, below) + virtual int_type underflow () { + int_type ret = traits_type::eof(); + ssize_t count; + if( (count = ::read(_fd, _getbuf, _getbuf_size)) > 0 ) { + // set our input buffer to indicate new data + setg(_getbuf, _getbuf, _getbuf + count); + + // return new character + ret = traits_type::to_int_type(*_getbuf); + } +// cout << "underflow called: " << endl; + return ret; + } +}; + +void cpp_networkbuf(int s) +{ + networkbuf nb(s, 100); + iostream mail(&nb); + cpp_work(mail, mail); +} + +void cpp_filebuf(int s) +{ + // You must make separate filebufs here, since it seems the filebuf + // classes assume a file is seekable, while a socket is really just + // two independent data streams, one in, ont out. So treat it like + // that in code. + __gnu_cxx::stdio_filebuf<char> fbin(s, ios::in, 100); + __gnu_cxx::stdio_filebuf<char> fbout(s, ios::out, 100); + istream mailin(&fbin); + ostream mailout(&fbout); + + cpp_work(mailin, mailout); +} + +void cpp_work(istream &nin, ostream &nout) +{ + cout << "Sending data to socket..." << endl; + nout << "MAIL from: cdfrey" << endl; + nout << "RCPT to: destination-dude" << endl; +// nout << "quit" << endl; // commented out to see buffer behaviour + + cout << "Looping for data..." << endl; + char buffer[1024]; + while( nin.getline(buffer, sizeof(buffer)) ) { + cout << "socket data: " << buffer << endl; + + if( nin.fail() ) { + cout << "failbit \r" << flush << endl; + } + if( nin.bad() ) { + cout << "badbit \r" << flush << endl; + } + if( nin.eof() ) { + cout << "eofbit \r" << flush << endl; + } + } + +/* + // alternate method of copying a streambuf + cout << "Copying all socket data..." << endl; + cout << nin.rdbuf(); +*/ +} + +void c_work(int s) +{ + FILE *mail = fdopen(s, "r+"); + if( mail == NULL ) + return; + + cout << "Sending data to socket..." << endl; + + fputs("MAIL from: cdfrey\n", mail); + fputs("RCPT to: destination-dude\n", mail); + fputs("quit\n", mail); + + cout << "Looping for data..." << endl; + char buffer[1024]; + while( !feof(mail) ) { + // normal method +// fgets(buffer, sizeof(buffer), mail); + + // play with seeking on a network to test its behaviour + fseek(mail, 10, SEEK_CUR); + size_t count = fread(buffer, 1, 1, mail); + if( count == 1 ) + ungetc(buffer[0], mail); + count = fread(buffer, 1, 1, mail); + buffer[count] = 0; + if(count == 0) + cout << "(" << count << ")" << flush; + cout << buffer << flush; + } + + fclose(mail); +} + + +int main() +{ + std::ios::sync_with_stdio(false); // optional, only affects cout, etc. + + // setup socket and connect to local mail host + int s = socket(PF_INET, SOCK_STREAM, 0); + if( s == -1 ) { cerr << "socket error" << endl; return 1; } + + sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = htons(25); + dest.sin_addr.s_addr = inet_addr("127.0.0.1"); + if( connect(s, (sockaddr *) &dest, sizeof(dest)) == -1 ) { + cerr << "connect error" << endl; return 1; + } + + // or use a file (for testing) +// int s = open("blah.txt", O_RDWR); + + cpp_networkbuf(s); +// cpp_filebuf(s); +// c_work(s); + return 0; +} + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |