在C

时间:2015-11-08 04:10:51

标签: c sockets struct udp byte

我想通过具有以下格式的套接字发送数据包:

struct Packet{
   uint32_t  seqnum;
   uint16_t  check;
   char data[1024]
}; 

每个数据包都有序列号,校验和以及数据包包含的数据。我如何将这3个字段放入缓冲区以通过套接字发送,然后在收到时,可以提取字段?例如,是否有可能使缓冲区的前4个字节为seqnum,接下来的4个字节为检查,然后剩余的数据为1024字节?因此,接收器将期望接收总共1032个字节,然后应该能够提取前4个并使该seqnum,接下来的4个并将该校验和和最后的1024作为数据?我通过UDP这样做,所以我不能单独发送字段。

2 个答案:

答案 0 :(得分:0)

编辑 - OP没有要求C ++,但如果有帮助,底层实现是在C中完成的。

我修改了Boost示例网站中的一个类以满足您的需求,它是缓冲区和结构的便捷包装器。这应该适合您的需求,但它没有考虑大或小的结束。

当您的邮件始终是固定大小时,它会变得更容易,但是在保留邮件长度的标头中添加另一个4字节整数将为您节省大量带宽并且这非常简单。< / p>

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;
};

答案 1 :(得分:-1)

您必须确保正确设置结构,以使字段在正确的字节边界处正确对齐。

所以这很好:

struct Packet{
   uint32_t seqnum;
   uint32_t check;
   char data[1024];
};

但这不是:

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];
};

任何大于8位的整数字段都应在发送之前转换为网络字节顺序,并在接收之后转换回主机字节顺序。将htonlntohl用于32位值,将htonsntohs用于16位值。

完成后,您可以直接发送该结构:

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));

同样收到它:

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