// -*- c++ -*- //------------------------------------------------------------------------------ // Socketbuf.cpp //------------------------------------------------------------------------------ // Copyright (C) 1997-2002,2005 Vladislav Grinchenko // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. //------------------------------------------------------------------------------ // Created: 12/03/99 //------------------------------------------------------------------------------ // win32 #include #include "assa/Socket.h" #include "assa/Socketbuf.h" #include "assa/MemDump.h" using namespace ASSA; Socketbuf:: Socketbuf (Socket* s_) : m_s (s_) { trace_with_mask("Socketbuf::Socketbuf",STRMBUFTRACE); // By default, I am doing buffering IO unbuffered (0); } int Socketbuf:: sync () { trace_with_mask("Socketbuf::sync",STRMBUFTRACE); return flush_output (); } int Socketbuf:: showmanyc () { trace_with_mask("Socketbuf::showmanyc",STRMBUFTRACE); return m_s->getBytesAvail (); } void Socketbuf:: xput_char (char c_) { trace_with_mask("Socketbuf::xput_char",STRMBUFTRACE); *pptr() = c_; pbump (1); } Socketbuf:: ~Socketbuf () { trace_with_mask("Socketbuf::~Socketbuf",STRMBUFTRACE); overflow (EOF); // flush put area } int Socketbuf:: sys_read (char* b_, int len_) { trace_with_mask("Socketbuf::sys_read",STRMBUFTRACE); int ret = ::recv (m_s->getHandler (), b_, len_, 0); DL((STRMBUFTRACE,"Tried to read %d bytes from fd=%d\n", len_, m_s->getHandler ())); DL((STRMBUFTRACE,"::recv() returned %d\n", ret)); if (ret == -1) { DL((STRMBUFTRACE,"::recv() error: %d (%s)\n", errno, strerror (get_errno ()))); } return (ret); } int Socketbuf:: sys_write (char* b_, int len_) { trace_with_mask("Socketbuf::sys_write",STRMBUFTRACE); int ret = ::send (m_s->getHandler (), b_, len_, 0); DL((STRMBUFTRACE,"Tried to write %d bytes to fd=%d\n", len_, m_s->getHandler ())); DL((STRMBUFTRACE,"::send() returned %d\n", ret)); if (ret == -1) { DL((STRMBUFTRACE,"::send() error: %d\n",errno)); } return (ret); } int Socketbuf:: underflow () { /* The important thing to note is that this function returns: a) pointer to the first character in buffer available. b) EOF if sys_read () failed. In case of peer closing its connection, a) will be true. Caller can always find out number of bytes in buffer and determine if peer indeed closed connection. */ trace_with_mask("Socketbuf::underflow",STRMBUFTRACE); if (gptr () < egptr ()) // The get area is not empty, { return *(unsigned char*) gptr (); // return 1st character } if (base () == 0 && // If buffer isn't established, doallocate () == EOF) // allocate buffer (both buff & unbuff IO) { return EOF; } int bufsz = unbuffered () ? 1 : MAXTCPFRAMESZ; /* Read as much as I can up to the allocated buffer size. EOF = (-1). */ int rval = sys_read (base (), bufsz); DL((STRMBUF,"Socketbuf::sys_read() returned %d bytes\n", rval)); if (rval == EOF) { if (get_errno () != EWOULDBLOCK) { m_flags |= EOF_SEEN; } return EOF; } DL((STRMBUF,"Having read %d bytes from socket\n",rval)); MemDump::dump_to_log (STRMBUF, "Data received:", base (), rval); // Set get area pointers according to the data just read setg (base (), base (), base () + rval); dump (); return (*(unsigned char*) gptr ()); // Return front character } int Socketbuf:: overflow (int c_) { trace_with_mask("Socketbuf::overflow",STRMBUFTRACE); // If c == EOF, return flush_output() // Otherwise, insert c into the buffer if (c_ == EOF) return flush_output (); if (pbase () == 0 && doallocate () == EOF) return EOF; if (pptr () >= epptr() && flush_output () == EOF) return EOF; xput_char (c_); dump (); if ((unbuffered () || pptr () >= epptr ()) && flush_output () == EOF) return EOF; dump (); return c_; } int Socketbuf:: flush_output () { trace_with_mask("Socketbuf::flush_output",STRMBUFTRACE); if (pptr () <= pbase ()) { // Nothing to flush return 0; } int requested; int xmitted; requested = pptr () - pbase (); if ((xmitted = sys_write (pbase (), requested)) < 0) { return EOF; } if (unbuffered ()) { setp (pbase (), epptr ()); return 0; } requested -= xmitted; setp (pbase (), pbase () + MAXTCPFRAMESZ); pbump (requested); if (requested > 0) { ::memmove (pbase (), pbase () + xmitted, requested); } return 0; } int Socketbuf::doallocate () { trace_with_mask("Socketbuf::doallocate",STRMBUFTRACE); // Get area comes first and matches entier buffer area. // Put area comes right after it. They are two equally-sized // separate buffers. // // ------------ - // | | ^ // | get area | | buffer area // | | v // ------------ - // | put area | // | | // ------------ // // Return 1 on allocation and 0 if there is no need if (m_buf_base) return 0; if ( ! (m_flags & UNBUFFERED) ) { DL((STRMBUF,"Buffered IO - allocating %d bytes\n", 2 * MAXTCPFRAMESZ)); char* buf = new char [2 * MAXTCPFRAMESZ]; setg (buf, buf + MAXTCPFRAMESZ, buf + MAXTCPFRAMESZ); setb (buf, buf + MAXTCPFRAMESZ, 1); buf += MAXTCPFRAMESZ; setp (buf, buf+MAXTCPFRAMESZ); } else { DL((STRMBUF,"Unbuffered IO - same 1 byte array\n")); setb (m_shortbuf, m_shortbuf+1, 0); // read_base is pointing to the begining, and // read_end to one past end of the buffer. // Because buffer is empty, read_ptr should point // to the end (same as read_end). This way, calling // routines will detect that get area needs data from sync. // // +- read_base +- read_ptr // | |- read_end // | |+----- one past end-of-block // v vv // +--------------+-+ // | get area | | // +--------------+-+ setg (m_shortbuf, m_shortbuf+1, m_shortbuf+1); // write_base & write_ptr are pointing to the begining, // and write_end to one past end of the buffer. // Because buffer is empty, read_ptr should point // to the beginning (same as write_base). This way, calling // routines will detect that put area needs data from sync. // // +- write_base +- write_end points one past // |- write_ptr | end-of-block // v v // +--------------+-+ // | put area | | // +--------------+-+ setp (m_shortbuf, m_shortbuf+1); } dump (); return 1; }