Sign in to follow this  
  • entries
    132
  • comments
    99
  • views
    88618

Custom Streambuf, Take I

Sign in to follow this  
Driv3MeFar

227 views

It's been ages since I wrote something dev related, so here's my first pass at a custom streambuf for sending out data over the net. This is all untested, so I'm sure it doesn't work at all, but you can see some of the design decisions I've made, and maybe where I want to go from here.

First, the code:
NetStream.h

#ifndef NETSTREAM_H
#define NETSTREAM_H
#include
#include
#include
#include "Net.h"

namespace Networking
{

//helper function
template
class T2Bytes
{
public:
T2Bytes(const T &value) : TypeVal(value) {}

union
{
T TypeVal;
unsigned char Bytes[sizeof(T)];
};
};

class NetBuffer : public std::basic_streambuf<char>
{
public:
typedef std::basic_streambuf<char>::traits_type traits_type;
typedef std::basic_streambuf<char>::int_type int_type;

public:
NetBuffer(const SOCKET &mySocket, const sockaddr_in &theirAddr, size_t outSize, size_t inSize);

void SetBufferSize(size_t size);

protected:
//basic_streambuf virtual member functions
char_type* pbase() { return m_PBegin._Myptr; }
char_type* pptr() { return m_PCur._Myptr; }
char_type* epptr() { return m_PEnd._Myptr; }
char_type* eback() { return m_GBegin._Myptr; }
char_type* gptr() { return m_GCur._Myptr; }
char_type* egptr() { return m_GEnd._Myptr; }

//output
std::streamsize xsputn( const char_type *_Ptr, std::streamsize _Count );
int_type overflow(int_type c = traits_type::eof());

int sync( );

//input
std::streamsize xsgetn( char_type *_Ptr, std::streamsize _Count );
int_type underflow( );

private:
std::vector<char> m_inputBuffer;
std::vector<char> m_outputBuffer;

std::vector<char>::iterator m_PBegin, m_PEnd, m_PCur, m_GBegin, m_GEnd, m_GCur;

const SOCKET &m_mySocket;
const sockaddr_in &m_theirAddr;
};

} //namespace Networking

#endif






NetStream.cpp

#include
#include
#include "..\Header Files\NetStream.h"
#include "..\..\Main\Header Files\Macros.h"
#include "..\Header Files\Peer.h"
#include "..\Header Files\Transport.h"

namespace Networking
{

NetBuffer::NetBuffer(const SOCKET &mySocket, const sockaddr_in &theirAddr, size_t outSize, size_t inSize)
: m_mySocket(mySocket),
m_theirAddr(theirAddr),
m_outputBuffer(outSize),
m_inputBuffer(inSize)
{
m_PBegin = m_PCur = m_outputBuffer.begin();
m_PEnd = m_outputBuffer.end();
m_GBegin = m_PCur = m_GEnd = m_inputBuffer.begin();
}

void NetBuffer::SetBufferSize(size_t size)
{
m_inputBuffer.clear();
m_inputBuffer.reserve(size);
m_outputBuffer.clear();
m_outputBuffer.reserve(size);
m_PBegin = m_PCur = m_outputBuffer.begin();
m_PEnd = m_outputBuffer.end();
m_GBegin = m_PCur = m_GEnd = m_inputBuffer.begin();
}


std::streamsize NetBuffer::xsputn( const char_type *_Ptr, std::streamsize _Count )
{
assert(m_PEnd - m_PCur >= _Count);

stdext::checked_copy(_Ptr, _Ptr + _Count, m_PCur);
m_PCur += _Count;
return -1;
}

NetBuffer::int_type NetBuffer::overflow(NetBuffer::int_type c)
{
sync();
return traits_type::not_eof(c);
}

int NetBuffer::sync( )
{
//@TODO: is this safe? do come checking here...
int size = static_cast<int>(m_PCur - m_PBegin);
int retval = sendto(m_mySocket, &m_outputBuffer[0], size, 0, (const sockaddr *)&m_theirAddr, sizeof(sockaddr_in));
m_outputBuffer.clear();
m_PCur = m_PBegin;
return retval;
}

std::streamsize NetBuffer::xsgetn( char_type *_Ptr, std::streamsize _Count )
{
if (m_GCur == m_GEnd)
{
underflow();
}

if (!m_inputBuffer.empty())
{
assert(m_GEnd - m_GCur >= _Count);
stdext::checked_copy(m_GCur, m_GCur + _Count, _Ptr);
//reset get ptr to beginning of input buffer if there is nothing left
m_GCur += _Count;
return _Count;
}

return 0;
}

NetBuffer::int_type NetBuffer::underflow()
{
m_inputBuffer.clear();
m_GCur = m_GEnd = m_GBegin;

int fromLen = sizeof(sockaddr_in);
int recvd = recvfrom(m_mySocket, &*m_GBegin, (int)m_inputBuffer.size(), 0, (sockaddr *)&m_theirAddr, &fromLen);

m_GEnd += recvd;

assert(&*m_GEnd <= &*m_inputBuffer.end());

/*HPEER peer = Peers[m_theirAddr.S_ulAddr];

peer->Buffer*/


return recvd;
}

} //namespace Networking





The code is hopefully pretty self-explanatory. You can just create any new iostream (or hijack an existing one), and set a net streambuf via an rdbuf() call. Writing to the stream will be done regularly, via operator<< or .write(), which will call the netbufs xsputn function. Data is stored in a std::vector, as always. Output is buffered, it won't be sent out until overflow is called, either when the buffer fills up, or it is flushed manually via a std::endl or similar.

Input works similarly, with underflow() actually reading in from the net. Data is buffered, and it won't read from the network again until the input buffer is actually emptied of what was read in previously.

I'm still not sure exactly sure how input will work. I'd like input buffers to be kept on a per-peer basis, but I can't exactly know who's sending me data until I've read it in. Right now, I'm thinking I'll either manually read in the packet header to get the senders address, then look that up in a map and tell that peer to finish reading in everything, but that could have some problems:
1) What if I get information from an unregistered peer (such as a connection request). I won't have a peer to delegate reading to, and will need a default buffer to fall back to.
2) I'd need to read in a packet header, tell the necessary peer to read in the rest of the packet, then go back to the networking class to read in the next header, in case it came from a different peer, etc.
The other option would be to keep a single input buffer to do all the reading from the network, and pull data from the buffer and hand those off to the appropriate peer after reading. So far, I think I like this solution more, because all my reading from the network is done by one centralized buffer.

So that's the basic plan for the network streambufs. Like I said, this is all untested (and I'm completely new to overriding std::streambufs), so bonus points to all the bugs and flaws that you all can find. I'm sure there are plenty.

Next time: more refined stream buffers, and the transport class (and my reliability scheme) explained.

I'm also hoping to have something spiffy (and non networking related) to show off by the end of the week, so stay tuned!
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now