C原始套接字发送地址的目的是什么?
我正在发送一些ping数据包,通过我的linux机器上的一个原始套接字。C原始套接字发送地址的目的是什么?
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
这意味着,我指定的IP数据包报头时,我写入套接字(IP_HDRINCL
被隐含)。
写入与send
插座失败,告诉我我需要指定一个地址。
如果我使用sendto
那么它工作。对于sendto
我必须指定一个sockaddr_in
结构使用,其中包括字段sin_family
,sin_port
和sin_addr
。
不过,我已经注意到了几件事:
- 的
sin_family
是AF_INET
- 创建插座时已经指定。 -
sin_port
自然未被使用(端口不是IP的概念)。 - 只要它是外部地址(IP数据包指定为8.8.8.8,
sin_addr
指定为1.1.1.1),我使用哪个地址并不重要。
看来sendto
的额外字段实际上在很大程度上没有任何用处。那么,是否有一个技术原因,我必须使用sendto
而不是send
,还是仅仅是API中的疏忽?
与
send
写入套接字失败,告诉我我需要指定一个地址。
它失败,因为send()
函数只能在连接插座中使用(如所陈述here)。通常,您将使用send()
进行TCP通信(面向连接),并且sendto()
可用于发送UDP数据报(无连接)。
由于您想发送“ping”数据包,或者更正确的ICMP数据报,它们显然是无连接的,所以您必须使用sendto()
函数。
看起来
sendto
的额外字段实际上用于很大的 程度。那么,是否有技术上的原因,我必须使用sendto
而不是send
或者它只是API中的疏忽?
简短的回答:
当您不允许使用send()
,那么就只剩下一种选择,叫sendto()
。
龙答:
它不只是在API中的监督。如果你想用一个普通的套接字发送一个UDP数据报(例如SOCK_DGRAM
),sendto()
需要你在结构sockaddr_in
中提供的有关目标地址和端口的信息,对不对?内核会将该信息插入到结果IP头中,因为结构sockaddr_in
是只有您指定接收者的位置。换句话说:在这种情况下,内核必须从结构中获取目标信息,因为您不提供额外的IP标头。
由于sendto()
不仅用于UDP,还用于原始套接字,它必须是或多或少的“通用”功能,可以覆盖所有不同的使用情况,即使某些参数(如端口号)不相关/最终使用。
例如,通过使用IPPROTO_RAW
(它自动暗示IP_HDRINCL
),表明您希望自己创建IP标头。因此,sendto()
的最后两个参数实际上是冗余信息,因为它们已经包含在您作为第二个参数传递给sendto()
的数据缓冲区中。请注意,即使将IP_HDRINCL
用于原始套接字,如果将相应的字段设置为0
,内核也会填入IP数据报的源地址和校验和。
如果你想编写自己的ping程序,你也可以在你的socket()
功能从IPPROTO_RAW
改变的最后一个参数IPPROTO_ICMP
并让内核创建IP报头的你,让你有一个东西少操心。现在您可以轻松了解两个sendto()
-参数*dest_addr
和addrlen
如何再次变得有意义,因为它是唯一提供目标地址的地方。
语言和API非常陈旧,并随着时间的推移而增长。从今天的角度来看,一些API看起来很怪异,但是如果不破坏大量的现有代码,您就无法更改旧界面。有时你必须习惯于多年或几十年前定义/设计的东西。
希望能回答你的问题。
当套接字处于TCP SOCK_STREAM
连接状态时,使用send()
调用。
从手册页:
在send()调用可用于只有当插座连接器处于连接状态 (使预期的接收者是已知的)。
由于您的应用程序显然不与任何其他套接字连接,我们不能指望send()
工作。