奇怪的 Linux 套接字协议行为
当使用 socket()
时,我对 Linux 上协议定义之间的差异有点困惑。我正在尝试使用 socket(PF_INET, SOCK_STREAM, proto)
监听 TCP 上的连接,其中 proto
(在我看来)是有争议的,或者至少看起来很奇怪。
来自
:
...
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
...
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
...
同意 /etc/protocols
:
ip 0 IP # internet protocol, pseudo protocol number
hopopt 0 HOPOPT # hop-by-hop options for ipv6
...
tcp 6 TCP # transmission control protocol
...
我从在线教程中学习,也手册页 tcp(7)
中介绍了您初始化 TCP 套接字,
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
该套接字工作得非常好,并且肯定是一个 TCP 套接字。使用上述参数初始化套接字的一件事是,代码
struct timeval timeout = {1, 0};
setsockopt(tcp_socket, 0, SO_RCVTIMEO, &timeout, sizeof(timeout); // 1s timeout
// Exactly the same for SO_SNDTIMEO here
工作得绝对正常,但在用 替换所有协议参数(包括
,而不是他们拥有的 socket()
中的)后不能 >IPPROTO_TCPIPPROTO_IP
,如上所述。
因此,在尝试了差异之后,我需要问一些搜索问题:
- 为什么当我用 IPPROTO_TCP 替换所有协议参数时,在设置时会出现错误 92(“协议不可用”)超时,当协议 0 显然只是一个“虚拟”TCP 时?
- 为什么
socket()
需要它是否应该是流、数据报或原始套接字的信息,而该信息(总是?)从协议中隐式得知,反之亦然? (即 TCP 是流协议,UDP 是数据报协议,...) - “虚拟 TCP”是什么意思?
- 什么是
hopopt
,为什么它的协议号与“ip”相同?
非常感谢。
I'm a little confused about the difference between the definitions of protocols on Linux when using socket()
. I am attempting to listen for connections over TCP using socket(PF_INET, SOCK_STREAM, proto)
, where proto
is (in my mind) disputed, or at least seems odd.
From <netinet/in.h>
:
...
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
...
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
...
Agreed with by /etc/protocols
:
ip 0 IP # internet protocol, pseudo protocol number
hopopt 0 HOPOPT # hop-by-hop options for ipv6
...
tcp 6 TCP # transmission control protocol
...
I learned from an online tutorial, and also from the man page tcp(7)
that you initialise a TCP socket using
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
which works absolutely fine, and certainly is a TCP socket. One thing about using the above arguments to initialise a socket is that the code
struct timeval timeout = {1, 0};
setsockopt(tcp_socket, 0, SO_RCVTIMEO, &timeout, sizeof(timeout); // 1s timeout
// Exactly the same for SO_SNDTIMEO here
works absolutely fine, but not after replacing all protocol arguments (including in socket()
) with IPPROTO_TCP
, as opposed to IPPROTO_IP
which they have, as above.
So after experimenting with the difference, I've needed to ask a few searching questions:
- Why, when I replace all protocol arguments with
IPPROTO_TCP
, do I get error 92 ("Protocol not available") when setting timeouts, when protocol 0 is apparently just a 'dummy' TCP? - Why does
socket()
require the information of whether it should be a stream, datagram or raw socket when that information is (always?) implicitly known from the protocol, and vice versa? (i.e. TCP is a stream protocol, UDP is a datagram protocol, ...) - What could be meant by "dummy TCP"?
- What is
hopopt
, and why does it have the same protocol number as 'ip'?
Many thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
将 0 作为
socket
的协议仅意味着您要使用 family/socktype 对的默认协议。在本例中为 TCP,因此您将获得与IPPROTO_TCP
相同的结果。您的错误出现在setsockopt 调用中。正确的
0 不是协议级别,而是选项级别。
IPPROTO_TCP
是另一个选项级别,但不能将其与SO_RCVTIMEO
结合使用。它只能与SOL_SOCKET
一起使用。与
IPPROTO_TCP
一起使用的那些是 tcp(7) 中列出的,例如TCP_NODELAY
。Giving 0 as protocol to
socket
just means that you want to use the default protocol for the family/socktype pair. In this case that is TCP, and thus you get the same result as withIPPROTO_TCP
.Your error is in the setsockopt call. The correct one would be
the 0 there is not for protocol, but for option level.
IPPROTO_TCP
is another option level, but you can't combine that withSO_RCVTIMEO
. It can only be used together withSOL_SOCKET
.The ones you use with
IPPROTO_TCP
are the ones listed in tcp(7), e.g.TCP_NODELAY
.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
应该可以正常工作。传递 0 作为协议只是意味着给我默认值。在每个系统上,当处理 IP 时,TCP 用于流套接字,UDP 用于数据报套接字。但是socket()可以用于许多其他事情,除了给你一个TCP或UDP套接字。
socket() 本质上是非常通用的。
socket(AF_INET, SOCK_STREAM, 0);
只是读作; “给我一个 IP 协议族中的流套接字”。传递 0 意味着您对哪个协议没有偏好 - 尽管 TCP 对于任何系统来说都是显而易见的选择。但从理论上讲,它可以为您提供例如 SCTP 套接字。对于协议来说,无论您需要数据报还是流式套接字,都不是隐含的。除了基于 IP 的协议之外,还有更多协议,并且许多协议可以在数据报或流模式下使用,例如 SS7 网络中使用的 SCCP。
对于基于 IP 的协议,SCTP 可以以基于数据报或流的方式使用。因此套接字(AF_INET,IPPROTO_SCTP);会很含糊。对于数据报套接字,还有其他选择,UDP、DCCP、UDPlite。
套接字(AF_INET,SOCK_SEQPACKET,0);是另一个有趣的选择。它无法返回 TCP 套接字,TCP 不是基于数据包的。它无法返回 UDP 套接字,UDP 不保证顺序传送。但如果系统支持的话,SCTP 套接字就可以了。
我无法解释为什么有人评论“虚拟 TCP”,因为 linux netinet/in.h
hopopt 是 IPv6 HOP by hop 选项。在IPv6中,协议鉴别符字段也被用作扩展机制。在 IPv4 数据包中有一个协议字段,它是协议鉴别符,如果 IPv4 数据报携带 TCP,它将被设置为 IPPROTO_TCP。如果该 IPv4 数据包还携带一些附加信息(选项),则它们会通过其他机制进行编码。
IPv6 的做法有所不同,如果有扩展(选项),则该扩展会编码在协议字段中。因此,如果 IPv6 数据包需要逐跳选项,则将 IPPROTO_HOPOPTS 放置在协议字段中。实际的逐跳选项还有一个协议鉴别器,它指示下一个协议是什么 - 可能是 IPPROTO_TCP,或另一个选项。
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
should work fine.Passing 0 as the protocol just means, give me the default. Which on every system is TCP for stream sockets and UDP for datagram sockets, when dealing with IP. But socket() can be used for many other things bar giving you a TCP or UDP socket.
socket() is quite general in nature.
socket(AF_INET, SOCK_STREAM, 0);
just reads as; "give me a streaming socket within the IP protocol family". Passing 0 means you have no preferences over which protocol - though TCP is the obvious choice for any system. But theoretically, it could have given you e.g. an SCTP socket.Whether you want datagram or streaming sockets is not implicit for protocols. There are many more protocols bar IP based protocols, and many can be used in either datagram or streaming mode such as SCCP used in SS7 networks.
For IP based protocols, SCTP can be used in a datagram based, or streaming fashion. Thus socket(AF_INET,IPPROTO_SCTP); would be ambiguous. And for datagram sockets, there's other choices as well, UDP, DCCP, UDPlite.
socket(AF_INET,SOCK_SEQPACKET,0); is another interesting choice. It cannot return a TCP socket, TCP is not packet based. It cannot return and UDP socket, UDP gives no guarantee of sequential delivery. But an SCTP socket would do, if the system supports it.
I have no explanation for why someone made the comment "dummy TCP" in that the linux netinet/in.h
hopopt is the IPv6 HOP by hop option. In IPv6, the protocol discriminator field is also used as an extension mechanism. In IPv4 packets there is a protocol field which is the protocol discriminator, it'll be set to IPPROTO_TCP if that IPv4 datagram carries TCP. If that IPv4 packet also carries some additional info(options), they are coded by other mechanisms.
IPv6 does this differently, if there is an extension(option), that extension is coded in the protocol field. So if the IPv6 packet needs the hop-by-hop option, IPPROTO_HOPOPTS is placed in the protocol field. The actual hop-by-hop option also have a protocol discriminator, which signals what the next protocol is - which might be IPPROTO_TCP, or yet another option.