
I used to do serialization using all kinds of fancy templates and macros. You can create pretty elegant systems that way. However, at some point, simplicity should win out. Here's a system that might work just fine for you:
A simple packet class, which really is all you need:
class packet { public: packet() : pos_(0) {} void append(void const *data, size_t size) {

I recently have answered several questions about how to structure a networking library such that it can be easy to use for users of the library and/or when expanding the game you're writing. Here are some thoughts on that. (Code examples in C++)
Networking generally ends up needing to do three things:
1) Mirror state updates from one object to another.
2) Request remote services ("RPC").

#include "etwork/etwork.h" #include "etwork/buffer.h" #include "etwork/errors.h" #include "etwork/notify.h" #include "etwork/marshal.h" #include <assert.h> #include <stdio.h> #include <string> #include <math.h> #if defined( NDEBUG ) #pragma warning( disable: 4101 ) // unreferenced local variable #endif void TestEtworkCreate() { EtworkSettings es;

A simple command-line program that exercises parts of the Etwork API and asserts if something fails. Think of it as an API acceptance test.

#if !defined( etwork_sockimpl_h ) #define etwork_sockimpl_h #include "etwork/etwork.h" #include "etwork/locker.h" #include "etwork/buffer.h" #include "etwork/timer.h" #include "etwork/errors.h" #include "etwork/notify.h" #if defined( WIN32 ) #include <windows.h> #endif #include <stdio.h> // for _snprintf #include <math.h> #include <string> #include <map>

#include "sockimpl.h" using namespace etwork; using namespace etwork::impl; SocketManager::SocketManager() { listening_ = INVALID_SOCKET; maxNumSocks_ = FD_SETSIZE; numSocks_ = 0; maxSock_ = 0; allSet_ = (fd_set *)::operator new( sizeof(fd_set) ); FD_ZERO( allSet_ ); readSet_ = (fd_set *)::operator new( sizeof(fd_set) ); FD_ZERO( readSet_ );

#include <assert.h> #include <string> #include <math.h> #include <map> #include "etwork/marshal.h" namespace marshaller { inline std::string operator+( std::string const & left, int right ) { char buf[24]; sprintf( buf, "%d", right ); return left + std::string( buf ); } inline std::string operator+( std::string const & left, size_t right ) { char buf[24];

#include "sockimpl.h" #include <stdarg.h> using namespace etwork; using namespace etwork::impl; #if !defined( NDEBUG ) bool etwork::impl::gDebugging = true; #else bool etwork::impl::gDebugging = false; #endif bool etwork::impl::wsOpen = false; Lock etwork::impl::gethostLock; //!< gethostbyname is not thread safe IErrorNotify * etwork::impl::gErrorNotify;

#include "etwork/buffer.h" #include <string.h> #include <deque> #include <assert.h> using namespace etwork; //! The framing protocol for etwork::Buffer is simple: each packet is //! preceded by a two-byte length, in network-endian order. //! If random data is received, this may result in arbitrary packet

#include "etwork/marshal.h" Block::Block( void * base, size_t size ) { buf_ = (unsigned char *)base; size_ = size; pos_ = 0; deleteIt_ = false; atEof_ = false; } Block::Block( size_t size ) { buf_ = (unsigned char *)::operator new( size ); size_ = size; pos_ = 0; deleteIt_ = true; atEof_ = false; } Block::~Block() { if( deleteIt_ ) {