【问题标题】:Strange Linux socket protocols behaviour奇怪的 Linux 套接字协议行为
【发布时间】:2011-09-08 10:37:43
【问题描述】:

在使用socket() 时,我对 Linux 上协议定义之间的差异感到有些困惑。我正在尝试使用socket(PF_INET, SOCK_STREAM, proto) 监听 TCP 上的连接,其中proto (在我看来)是有争议的,或者至少看起来很奇怪。

来自<netinet/in.h>

...
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

工作得很好,但是不是在用IPPROTO_TCP替换所有协议参数(包括在socket()中)之后,而不是他们拥有的IPPROTO_IP,如上所述。

所以在尝试了不同之处之后,我需要问一些搜索问题:

  1. 为什么,当我将所有协议参数替换为 IPPROTO_TCP 时,在设置超时时会出现错误 92(“协议不可用”),而协议 0 显然只是一个“虚拟”TCP?
  2. 为什么socket() 需要知道它应该是流、数据报还是原始套接字的信息,而该信息(总是?)从协议中隐式知道,反之亦然? (即 TCP 是流协议,UDP 是数据报协议,...)
  3. “虚拟 TCP”是什么意思?
  4. 什么是hopopt,为什么它的协议号和“ip”一样?

非常感谢。

【问题讨论】:

    标签: c linux sockets protocols


    【解决方案1】:

    将 0 作为协议提供给 socket 仅意味着您要使用家庭/socktype 对的默认协议。在这种情况下是 TCP,因此您会得到与 IPPROTO_TCP 相同的结果。

    您的错误出现在 setsockopt 调用中。正确的应该是

    setsockopt(tcp_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); // 1s timeout
    

    这里的 0 不是协议,而是选项级别。 IPPROTO_TCP 是另一个选项级别,但您不能将其与 SO_RCVTIMEO 结合使用。只能与SOL_SOCKET 一起使用。 您与IPPROTO_TCP 一起使用的是 tcp(7) 中列出的那些,例如TCP_NODELAY.

    【讨论】:

    • 啊,谢谢!我想我被tcp(7)中的这句话推翻了:“例如,表示一个选项要被TCP协议解释,level应该设置为TCP的协议号”,但是设置超时是在套接字 API 级别执行的?
    • 超时在套接字 api 级别。 tcp(7) 手册页描述了 TCP 级别的可用选项。
    • 中间还有一层,即IPPROTO_IP。这些在 ip(7) 中列出。
    【解决方案2】:

    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 可以以基于数据报或流的方式使用。因此 socket(AF_INET,IPPROTO_SCTP);会模棱两可。对于数据报套接字,还有其他选择,UDP、DCCP、UDPlite。

    socket(AF_INET,SOCK_SEQPACKET,0);是另一个有趣的选择。它不能返回 TCP 套接字,TCP 不是基于数据包的。它不能返回和 UDP 套接字,UDP 不保证顺序传递。但如果系统支持,SCTP 套接字也可以。

    我无法解释为什么有人在 linux netinet/in.h 中发表评论“dummy TCP”

    hopopt 是 IPv6 HOP by hop 选项。在 IPv6 中,协议鉴别器字段也用作扩展机制。在 IPv4 数据包中有一个协议字段,它是协议鉴别符,如果该 IPv4 数据报携带 TCP,它将被设置为 IPPROTO_TCP。如果该 IPv4 数据包还带有一些附加信息(选项),则它们由其他机制编码。

    IPv6 以不同的方式执行此操作,如果有扩展(选项),则该扩展在协议字段中编码。因此,如果 IPv6 数据包需要逐跳选项,则 IPPROTO_HOPOPTS 放在协议字段中。实际的逐跳选项也有一个协议鉴别器,它表明下一个协议是什么 - 可能是 IPPROTO_TCP 或另一个选项。

    【讨论】:

    • AF_INET 是 POSIX 中指定的常量; PF_INET 那里不存在。所以前者总是有效的,因为 Linux 不会因为像 socket() 调用这样基本的东西而放弃 POSIX 兼容性。
    • 是的。删除了所有的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 1970-01-01
    • 1970-01-01
    • 2019-06-25
    相关资源
    最近更新 更多