C raw socket的sendto地址的用途?

时间:2016-09-25 03:08:00

标签: c linux sockets ip raw-sockets

我在我的linux机器上通过C中的原始套接字发送一些ping数据包。

int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

这意味着我在写入套接字时指定了IP数据包标头(隐含了IP_HDRINCL)。

使用send写入套接字失败,告诉我需要指定一个地址。

如果我使用sendto则可行。对于sendto,我必须指定要使用的sockaddr_in结构,其中包括字段sin_familysin_portsin_addr

但是,我注意到了一些事情:

  • sin_familyAF_INET - 已在创建套接字时指定。
  • sin_port自然未使用(端口不是IP的概念)。
  • 我使用的地址并不重要,只要它是外部地址(IP数据包指定为8.8.8.8,sin_addr指定1.1.1.1)。

似乎sendto中的任何额外字段都没有在很大程度上被使用。那么,是否有技术原因导致我必须使用sendto而不是send,或者它只是API的疏忽?

3 个答案:

答案 0 :(得分:9)

  

使用send写入套接字失败,告诉我需要指定一个地址。

失败,因为send()函数只能用于连接的套接字(如here所述)。通常,您将send()用于TCP通信(面向连接),sendto()可用于发送UDP数据报(无连接)。

因为你想发送" ping"数据包,或者更准确的ICMP数据报,它们是无连接的,你必须使用sendto()函数。

  

似乎sendto中的额外字段实际上并不是很好用   程度。那么,是否有技术原因我必须使用sendto   而不是send或者它只是API的疏忽?

简答:

如果您不允许使用send(),则只剩下一个选项,名为sendto()

答案很长:

这不仅仅是API的疏忽。如果要使用普通套接字(例如SOCK_DGRAM)发送UDP数据报,sendto()需要您在结构sockaddr_in中提供的有关目标地址和端口的信息, ?内核会将该信息插入到生成的IP头中,因为struct sockaddr_in only 的位置,您可以在其中指定接收者的身份。换句话说:在这种情况下,内核必须从您的结构中获取目标信息,因为您不提供额外的IP标头。

因为sendto()不仅用于UDP而且还用于原始套接字,因此它必须是或多或少的"泛型"功能可以覆盖所有不同的用例,即使端口号等参数最终不相关/使用。

例如,通过使用IPPROTO_RAW(自动隐含IP_HDRINCL),您可以表明您想要自己创建IP标头。因此sendto()的最后两个参数实际上是冗余信息,因为它们已经包含在作为第二个参数传递给sendto()的数据缓冲区中。请注意,即使您对原始套接字使用IP_HDRINCL,如果将相应字段设置为0,内核也会填写IP数据报的源地址和校验和。

如果你想编写自己的ping程序,你也可以将socket()函数中的最后一个参数从IPPROTO_RAW更改为IPPROTO_ICMP,让内核为你创建IP头,所以你有一点不用担心。现在,您可以轻松查看两个sendto() - 参数*dest_addraddrlen如何再次显着,因为它是您提供目标地址的唯一位置。

语言和API非常陈旧,并且随着时间的推移而增长。从今天的角度来看,有些API看起来很奇怪,但是你不能在不破坏大量现有代码的情况下改变旧的接口。有时你只需要习惯几年或几十年前定义/设计的东西。

希望能回答你的问题。

答案 1 :(得分:2)

当套接字处于TCP send()连接状态时,将使用SOCK_STREAM调用。

从手册页:

  

只有当套接字处于连接状态时,才可以使用send()调用       状态(以便知道预期的接收者)。

由于您的应用程序显然无法与任何其他套接字连接,因此我们不能指望send()能够正常工作。

答案 2 :(得分:0)

除了InvertedHeli的答案,内核还将使用dest_addr中传递的sendto()来确定要使用哪个网络接口。

例如,如果dest_addr的IP为127.0.0.1,而原始数据包的目标地址为8.8.8.8,则您的数据包仍将被路由到lo接口。