I often see budding network programmers confused about how to send messages (packets of a specific size) across a stream protocol, such as TCP. (The same problem also applies when writing structured data to files on disk).

Because TCP is a stream protocol, you can't assume that a call to send() will correspond to a single return from recv() because the implementation is free to split a single send across multiple packets -- or coalesce multiple packets into one.

To make sure you can still send a message and have it come out as such on the other end, you need to prefix the message by a size. You may also want to add a known message identifier (such as an enum or int) while you're at it. Here is some code that will wrap send() and recv() to do that:

struct Header {
  unsigned int msg;
  unsigned int size;
};

struct Packet {
  Packet() { data = 0; h.msg = 0; h.size = 0; }
  ~Packet() { free( data ); }
  Packet( Packet const & o ) { ... }
  Packet& operator=( Packet const & o ) { ... }
  Header h;
  void * data;
};

/// send_packet is used on the sending side to send a packet
void send_packet( Packet const & p, SOCKET s ) {
  if( ::send( s, sizeof(p.h), &p.h, 0 ) != sizeof(p.h) ) {
    throw BadSend;
  }
  if( p.h.size > 0 && ::send( s, p.h.size, p.data, 0 ) != p.h.size ) {
    throw BadSend;
  }
}


/// recv_all is a convenient wrapper to ensure receiving 
/// enough data on a reliable stream.
bool recv_all( SOCKET s, size_t z, void * d ) {
  while( z > 0 ) {
    int r = ::recv( s, z, d );
    if( r < 1 ) return false;
    z -= r;
    d = (void *)(((char *)d)+r);
  }
  return true;
}

/// recv_packet is used on the receiving side to receive 
/// a packet.
void recv_packet( Packet * oP, SOCKET s ) {
  if( !recv_all( s, sizeof(oP->h), &oP->h ) ) {
    throw BadRecv;
  }
  oP->data = realloc( oP->data, oP->h.size );
  if( oP->h.size > 0 && !recv_all( s, oP->h.size, oP->data ) ) {
    throw BadRecv;
  }
}
          

Now, if you're using non-blocking sockets (which is a good idea if you're running an interactive system using a single thread), you can't block for data like the above code illustrates. Instead, you'll have to buffer input data, and build a state machine that knows whether it's looking for a header/length, or looking for that much data coming over the wire.

class ReceiveBuffer {
  public:
    ReceiveBuffer( size_t maxSize = 1024 ) {
      base_ = new char[maxSize];
      end_ = base_+maxSize;
      ptr_ = base_;
    }
    ~ReceiveBuffer() {
      delete[] base_;
    }
    void poll( SOCKET s ) {
      int r = ::recv( s, ptr_, end_-ptr_, 0 );
      if( r > 0 ) {
        ptr_ += r;
      }
    }
    size_t size() const {
      return ptr_-base_;
    }
    char const * data() const {
      return base_;
    }
    void remove( size_t s ) {
      assert( s <= size() );
      ::memmove( base_, base_+s, size()-s );
      ptr_ -= s;
    }
    size_t maxSize() const {
      return ptr_-base_;
    }

  char * base_;
  char * end_;
  char * ptr_;
};


class ReceiveStateMachine {
  public:
    ReceiveStateMachine( SOCKET s ) : s_( s ), gotHdr_(false) {
      packet_.data = 0;
    }

    Packet * poll() {
      if( packet_.data ) {
        assert( gotHdr_ );
        gotHdr_ = false;
        packet_.data = 0;
        buf_.remove( packet_.h.size );
      }
      buf_.poll( s_ );
      if( !gotHdr_ && buf_.size() >= sizeof(packet_.h) ) {
        ::memcpy( &packet_.h, buf_.data(), sizeof(packet_.h) );
        buf_.remove( sizeof(packet_.h) );
        gotHdr_ = true;
        if( packet_.h.size >= buf_.maxSize() ) {
          throw NetError( "junk input data" );
        }
      }
      if( gotHdr_ && buf_.size() >= packet_.h.size ) {
        packet_.data = buf_.data();
        return &packet_;
      }
      return 0;
    }

    SOCKET        s_;
    ReceiveBuffer buf_;
    bool          gotHdr_;
    Packet        packet_;
};
           
 
;