
When you send packets over TCP, you have to precede them with a byte count, because TCP just delivers a stream of bytes, with no particular block size. If you don't delimit your packets somehow, they will run into each other, and you won't be able to tell where one ends and the next one starts.
// how to decode packets that are preceded by a length count // from a TCP socket #define MAX_PACKET_SIZE 4000 typedef unsigned short packet_len_t; class Sender { public: char buf_[MAX_PACKET_SIZE+sizeof(packet_len_t)]; int toSend_; // you can add a new packet if the canAddPacket() function says you can void addPacket(void const *data, size_t size) { assert(size <= MAX_PACKET_SIZE); assert(canAddPacket()); // re-buffer with a packet length header *(packet_len_t *)buf_ = htons((packet_len_t)size); memcpy(&buf_[sizeof(packet_len_t)], data, size); toSend_ = size + sizeof(packet_len_t); } bool canAddPacket() { return toSend_ == 0; } // call this when select() says the socket can be written on void updateSend(SOCKET s) { if (toSend_ > 0) { // not necessarily everything is sent in a single go int s = send(s, buf_, toSend_, 0); if (s > 0) { // remove sent data from queue toSend_ -= s; memmove(buf_, &buf_[s], toSend_); } else { // handle error or connection close } } } }; class Receiver { public: char buf_[MAX_PACKET_SIZE+sizeof(packet_len_t)]; // assume max message size is 4000 bytes or less int curBufSize_; // call recvfromsocket when select() says there is data to read void recvFromSocket(SOCKET s) { if (curBufSize_ < sizeof(buf_)) { int r = recv(s, &buf_[curBufSize_], sizeof(buf_)-curBufSize_, 0); if (r > 0) { curBufSize_ += r; } else { // handle disconnect or error } } } bool hasOnePacket() { if (curBufSize_ < sizeof(packet_len_t)) { return false; } packet_len_t pl = htons(*(packet_len_t *)buf_); if (pl <= sizeof(packet_len_t) + curBufSize_) { return true; } if (pl > MAX_PACKET_SIZE) { // detect protocol error here, flush buffer } } // you can read at least one packet when hasOnePacket says there is one int extractOnePacket(void *dst) { assert(hasOnePacket()); packet_len_t pl = ntohs(*(packet_len_t *)buf_); assert(pl <= MAX_PACKET_SIZE); // extract the data to the given buffer memcpy(dst, &buf[sizeof(packet_len_t)], pl); pl += sizeof(packet_len_t); // remove the data for this packet memmove(buf_, &buf_[pl], curBufSize_-pl); curBufSize_ -= pl; return pl - sizeof(packet_len_t); } };
Your code will typically keep a queue of packets to shuffle out, and try to put them onto the Sender for a given connection. It will then try to push the data out the socket each time through the main loop of the program, until it can add a new packet and try to push that, etc.
On the receiving side, for sockets that have data, the receiver will read the data from the socket. Each time through the main loop, each receiver should be polled, and if there is a packet available to read, it should be extracted from the receiver. You can loop on while(hasOnePacket()) and call extractOnPacket() more than once if you want, as long as it returns true.