简体   繁体   English

在C中使用固定大小的标头构建UDP数据报

[英]Building UDP datagram with fixed size header in C

I am new to C and I am trying to build a UDP datagram and then send it from a client program to a server program on an different machine. 我是C语言的新手,我正在尝试构建UDP数据报,然后将其从客户端程序发送到另一台计算机上的服务器程序。

I have run into some problem when trying to build the datagram, specifically the fixed size (IPv4) header: The format of the header is (client IP (32 bit) | client port (16 bit) | packet length (32 bit) | ... etc.) 尝试构建数据报时,特别是固定大小(IPv4)标头时遇到了一些问题:标头的格式为(客户端IP(32位)|客户端端口(16位)|数据包长度(32位)| ...等)

The approach I choose to go with was to gather the necessary header and body information and then cast them into char* and then concatenate them together to generate the datagram. 我选择使用的方法是收集必要的标头和主体信息,然后将它们转换为char* ,然后将它们连接在一起以生成数据报。 However, when I cast int and long to char* (port number and packet length) it does not guarantee a fixed size char* (for example, if the packet length (datatype long) is calculated to be 440 and I convert it to char* , then strlen will show the length of the char* to be 2 instead of 4 since the number is not big enough to take all 4 bytes, I would assume...) 但是,当我将intlongchar* (端口号和数据包长度)时,它不能保证固定大小的char* (例如,如果数据包长度(数据类型为long)被计算为440,然后将其转换为char* ,然后strlen将显示char*的长度为2而不是4,因为该数字不足以容纳所有4个字节,我想...)

I tried a different way of converting long into fixed size char* by bit-wise operation: 我尝试了通过按位操作将long转换为固定大小的char *的另一种方法:

//assume the datagram size was calculated to be 440 bits
unsigned long len = 440;
unsigned char dg_len [4];
dg_len[0] = (len >> 24) & 0xFF;
dg_len[1] = (len >> 16) & 0xFF;
dg_len[2] = (len >> 8) & 0xFF;
dg_len[3] = len & 0xFF;
printf("%d" ,strlen(dg_lenPtr)); //this would display 0 
printf("%d" ,(unsigned long)dg_lenPtr); //this display some random number

The only explanation I thought of for this is that the unused 2 higher bytes of dg_lenPtr are being read as null character since strlen shows the length to be 0. 我想到的唯一解释是,由于strlen显示长度为0,因此dg_lenPtr的未使用的2个高字节被读取为空字符。

I am starting to think that char* is not the data structure I need to use to build the datagram. 我开始认为char*不是构建数据报所需的数据结构。 Can someone point me to the right direction on what I need to do to build the fix sized header for the datagram? 有人可以为我建立数据报的固定大小标头指出正确的方向吗?

You are not responsible for generating the IPv4 header. 您不负责生成IPv4标头。 The send() and sendto() functions handle that for you. send()sendto()函数可以为您处理该问题。 All you have to focus on is sending your desired datagram payload to the desired destination IP:Port (by specifying it directly to sendto() , or to connect() in the case of send() ). 您只需关注的是将所需的数据报有效负载发送到所需的目标IP:Port(通过直接将其指定到sendto()或在send()的情况下send()其指定为connect() send() )。

int connect(int socket, const struct sockaddr *address, socklen_t address_len);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
 unsigned long len = 440; unsigned char dg_len [4]; dg_len[0] = (len >> 24) & 0xFF; dg_len[1] = (len >> 16) & 0xFF; dg_len[2] = (len >> 8) & 0xFF; dg_len[3] = len & 0xFF; 

This is the right way to store values in a specific byte-order. 这是按特定字节顺序存储值的正确方法。 Shift-and-mask lets you access any part of the buffer without having to be concerned about alignment issues or the byte-order on your local machine, and unsigned char is the only type that is quaranteed to be without padding bits, trap representations or alignment problems. 移位和掩码使您可以访问缓冲区的任何部分,而不必担心对齐问题或本地计算机上的字节顺序,并且unsigned char是唯一被隔离为没有填充位,陷阱表示或对齐问题。

 printf("%d" ,strlen(dg_lenPtr)); //this would display 0 

You haven't declared dg_lenPtr , but assuming that it is a pointer to dg_len , calling strlen() on it is meaningless because it doesn't point to a string. 您尚未声明dg_lenPtr ,但是假设它是dg_len的指针,则dg_len调用strlen()是没有意义的,因为它没有指向字符串。 If len is less than 2 24 , strlen() will return 0 because the first byte happens to be zero. 如果len小于2 24 ,则strlen()将返回0因为第一个字节恰好为零。

 printf("%d" ,(unsigned long)dg_lenPtr); //this display some random number 

This might output the address stored in dg_lenPtr , converted to unsigned long (whatever that means). 这可能会输出存储在dg_lenPtr地址 ,将其转换为unsigned long (无论如何)。 It might not, because you're passing an unsigned long to printf() which is expecting a signed int . 可能不是,因为您要将unsigned long传递给需要signed int printf() The result of this mismatch is undefined behavior , which means that the program is free to do whatever it wants. 这种不匹配的结果是不确定的行为 ,这意味着程序可以自由地执行其所需的任何操作。


Personally, I would use a buffer to represent the entire datagram and write the values directly into the buffer. 就个人而言,我将使用缓冲区来表示整个数据报,然后将值直接写入缓冲区。 You can use either a fixed sized buffer ( unsigned char buf[/* some size*/]; ) or an allocated one ( unsigned char *buf = malloc (/* some size */); ) depending on your needs or preferences. 您可以根据需要或喜好使用固定大小的缓冲区( unsigned char buf[/* some size*/]; )或分配的unsigned char *buf = malloc (/* some size */);unsigned char *buf = malloc (/* some size */); )。

To simplify reading and writing to the buffer, I would create some helper functions: 为了简化读写缓冲区,我将创建一些辅助函数:

static void w32 (unsigned char *b, unsigned long v)
{
  b[0] = (v >> 24) & 0xff;
  b[1] = (v >> 16) & 0xff;
  b[2] = (v >>  8) & 0xff;
  b[3] = (v      ) & 0xff;
}
static void w16 (unsigned char *b, unsigned v)
{
  b[0] = (v >>  8) & 0xff;
  b[1] = (v      ) & 0xff;
}

w32 (buf  , ip);
w16 (buf+4, port);
w32 (buf+6, len);
/* And so on */

You could define macros to do the same job, but that would be pointless. 您可以定义宏以完成相同的工作,但这将毫无意义。 Any reasonably modern compiler with optimizations turned on should inline the functions and exclude them from the final executable. 任何启用了优化的合理现代编译器都应内联函数,并将其从最终可执行文件中排除。 The macros will just be more convoluted and less type-safe. 宏将更加复杂,类型安全性降低。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM