I want to send packets over a socket which have the following format:
struct Packet{
uint32_t seqnum;
uint16_t check;
char data[1024]
};
Each packet has a sequence number, a checksum, and the data that the packet contains. How could I put these 3 fields into a buffer to be sent over a socket, and then when received, the fields can be extracted? For example, is it possible to maybe make the first 4 bytes of the buffer the seqnum, the next 4 bytes the check, and then the remainder the data which is 1024 bytes? So the receiver will expect to receive a total of 1032 bytes and then should be able to extract the first 4 and make that seqnum, the next 4 and make that check sum and the last 1024 as the data? I am doing this over UDP so I cannot send the fields separately.
edit - OP didn't ask for C++, however the underlying implementation is done in C if that helps.
I modified a class from the Boost example website to suit your needs, it's a convenience wrapper for your buffers and struct. This should suit your needs, but it doesn't take into account big or little endianess.
It makes it a lot easier when your message is always a fixed size, but adding another 4 byte integer to your header that holds message length will save you a lot of bandwidth and it's very simple to do.
message myMessage;
myMessage.setSequence(nextSequenceNumber);
myMessage.setCheckSum(mycheckSum);
//assuming the message you have is a std::string
memcpy( myMessage.body(), message.c_str(), myMessage.body_length() );
myMessage.encode_header();
sendto(socket, myMessage.data(), myMessage.length(), 0, &my_sockaddrsizeof(my_sockaddr));
myMessage.reset();
recvfrom(socket, myMessage.data(), myMessage.length(), 0, &my_sockaddr, &my_sockaddr_len);
if(!myMessage.decodeHeader()){
// handle bad header/corrupt message
}
int32_t sequence = myMessage.getSequence();
int32_t checkSum = myMessage.getSheckSum();
class message
{
public:
enum { check_length = 4 };
enum { seq_length = 4 };
enum { body_length = 1024 };
message() : _checkSum(0), _sequence(0), _body_length(1024), _header_length(8) {} // constructor + init list
const char* data() const { return data_; }
char* data() { return data_; }
const char* body() const { return data_ + check_length + seq_length; }
char* body() { return data_ + check_length + seq_length; }
std::size_t length() { return _body_length + _header_length; }
std::size_t body_length() { return _body_length; }
int32_t const& getCheckSum() const { return _checkSum; }
int32_t const& getSequence() const { return _sequence; }
void setCheckSum(const int32_t& s) { _checkSum = s; }
void setSequence(const int32_t& s) { _sequence = s; }
bool decode_header()
{
char check_sum[check_length + seq_length + 1] = "";
char sequence[check_length + seq_length + 1] = "";
strncat_s(check_sum, data_, seq_length);
strncat_s(sequence, data_ + check_length, check_length);
_checkSum = std::atoi(check_sum);
_sequence = std::atoi(sequence);
if (_checkSum == 0 || _sequence == 0)
{
std::cout << "Header malfunction" << std::endl;
return false;
}
return true;
}
void encode_header()
{
char header[check_length + seq_length + 1] = "";
std::memcpy(header, &_sequence, sizeof(int32_t));
std::memcpy(header + seq_length, &_checkSum, sizeof(int32_t));
std::memcpy(data_, header, check_length + seq_length);
}
void reset()
{
memset(&data_[0], 0, sizeof(data_));
int32_t _checkSum = 0;
int32_t _sequence = 0;
}
private:
char data_[check_length + seq_length + body_length];
int32_t _body_length, _header_length;
int32_t _checkSum;
int32_t _sequence;
};
You have to make sure your struct is set up properly to have fields properly aligned at the proper byte boundaries.
So this is fine:
struct Packet{
uint32_t seqnum;
uint32_t check;
char data[1024];
};
But this is not:
struct Packet{
uint16_t other;
// 2 bytes of padding exist here so seqnum sits on a 4-byte boundary
uint32_t seqnum;
uint32_t check;
char data[1024];
};
Any integer fields larger than 8 bit should be converted to network byte order before sending and back to host byte order after receiving. Use htonl
and ntohl
for 32-bit values and htons
and ntohs
for 16-bit values.
Once you've done that, you can send that structure directly:
struct Packet packet;
// populate packet
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// perform error checking, call bind, etc.
int sendLen = sendto(socket, &packet, sizeof(packet), 0, &my_sockaddr, sizeof(my_sockaddr));
And similarly receive it:
struct Packet packet;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// perform error checking, call bind, etc.
int recvLen = recvfrom(socket, &packet, sizeof(packet), 0, &my_sockaddr, &my_sockaddr_len);
// read packet
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.