繁体   English   中英

C中的“仅UDP数据报”套接字

[英]“UDP datagrams only” socket in C

在Linux中,Ubuntu 14.04:我正在编写一个代码,该代码实现套接字以发送纯UDP数据报,其中包括UDP标头+有效载荷,而没有IP标头的任何部分。

我已经创建了套接字

sokt_fd=socket(AF_INET, SOCK_RAW, IPPROTO_UDP)

另外,我还准备了UDP标头。

  1. 我想将IP封装过程留给内核。

  2. 我想通过任何可用的IP接口发送数据报。 (我不想指定源IP,也不要将此任务留给内核)。

  3. 发送数据报之前是否需要指定目标IP地址。

  4. 我必须使用“ sendto()”命令发送数据报; 我必须如何填充“ sockaddr”数据结构?

     #include <netinet/in.h> struct sockaddr { unsigned short sa_family;// address family, AF_xxx char sa_data[14];// 14 bytes of protocol address }; 

不要使用sockaddr结构。 改用sockaddr_in并在必须将sockaddr*传递给函数时进行sockaddr*转换。

struct sockaddr_in myaddr;
int s;

myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(3490);
inet_aton("63.161.169.137", &myaddr.sin_addr.s_addr);

s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));

套接字API专为不同的寻址系列而设计,其他是红外和蓝牙。 由于AF_INET只是其中一个系列,因此API函数在参数中使用常规的sockaddr类型。

理查德·史蒂文斯(Richard Stevens)等人着名的《 Unix网络编程》,《套接字网络API》 (第1卷),在第3章“套接字介绍”中对此进行了很好的解释。 人。 让我引用:

大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。 每个受支持的协议套件都定义了自己的套接字地址结构。 这些结构的名称以sockaddr_开头,并以每个协议套件的唯一后缀结尾。

对于IP(Internet协议)套件,结构为sockaddr_in因此可以理解,由于您的示例在创建套接字时指定了AF_INET地址族,因此您将使用更具体的sockaddr_in结构而不是更通用的sockaddr。 为了提高效率,套接字API在签名原型中使用了更通用的sockaddr指针。

关于使用send()sendto() ,我发现sendto()与UDP一起使用,而send()与TCP套接字一起使用。 因此,要回答上面#3中的问题,使用UDP不必预先指定目标地址,而是将其作为sendto()的参数提供。

对于给定的udp_datagramdatagram_length ,您的代码可能看起来像这样:

uint32_t       address = inet_addr("1.2.3.4");   // can also provide hostname here
uint16_t       port    = 27890;
sockaddr_in_t  dest_addr;
memset(*dest_addr, 0, sizeof(dest_addr));

dest_addr.sin_family = AF_INET;
dest_addr.sin_port   = htons(port);
dest_addr.sin_addr.s_addr = htonl(address);

sendto(socket_fd, 
       (const char*)upd_datagram, 
       datagram_length,
       0,
       reinterpret_cast<sockaddr_t*>(&dest_addr),
       sizeof(dest_addr));

地址API确实希望是面向对象的,但必须处理C不是OO语言这一事实​​。 sockaddr可以看作是“基类”和绑定,连接,sendto,recvfrom等在需要地址时使用的参数类型。 但是,您必须提供一个与您使用的套接字域匹配的“子类”地址。 这是因为Berkeley套接字可用于广泛且可扩展的协议。 IPv4和IPv6是最典型的,但是基于UNIX的安装还支持将套接字作为文件系统对象(按路径“寻址”),例如,虚拟机监控程序驱动程序可以安装对特殊的VM间或来宾到主机套接字的支持。 有关概述,请参见man 7 socket

如果使用IPv4,则需要使用sockaddr_in 如果使用IPv6,则需要使用sockaddr_in6 在这两种情况下,都需要将指针转换为sockaddr*

要填写sockaddr_in ,您需要执行以下操作:

struct sockaddr_in inet_addr;
inet_addr.sin_family = AF_INET;
inet_addr.sin_port = htons(port);
inet_addr.sin_addr.s_addr = htonl(ip_address_as_number);
struct sockaddr* addr = (struct sockaddr*)&inet_addr;

htonshtonl分别表示“主机到网络(短)”和“主机到网络(长)”。 您之所以需要它,是因为有时网络驱动程序过于笨拙,无法抽象出机器的字节序,因此我们无法及时返回来修复它们。 (网络字节顺序为大字节序。)

暂无
暂无

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

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