【问题标题】:Why use htons() to specify protocol when creating packet socket?为什么在创建数据包套接字时使用 htons() 来指定协议?
【发布时间】:2016-08-12 19:42:40
【问题描述】:

要创建packet socket,使用socket() 函数调用(套接字类型和协议可能不同):

socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))

要创建stream socket,使用以下调用:

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)

我的问题是为什么在创建packet socket 时使用htons() 来指定协议,而不是在创建AF_INET or AF_INET6 系列的套接字时?为什么不使用

socket(AF_INET, SOCK_XXX, htons(IPPROTO_XXX))

在创建packet socket 时创建一个STREAM 或DATAGRAM 套接字,反之亦然。在对 socket() 函数的两个调用中使用的协议有什么不同,因为这两个调用都用于创建套接字,一个用于数据包套接字,另一个用于 TCP 级别的套接字?

【问题讨论】:

    标签: sockets


    【解决方案1】:

    首先,与传递给内核的大多数其他网络参数(IP 地址、端口等)一样,这些参数以它们的“在线”格式传递,因此底层软件不需要操纵它们在比较/复制/传输/等之前。 (为了比较,考虑AF_PACKETSOCK_RAW 是内核本身的参数——因此“本机格式”是合适的——而ETH_P_xxx 值通常用于“与传入数据包进行比较”;它就是这样发生的ETH_P_ALL 是一个特殊的信号值,表示“捕获一切”。)

    其次,协议的解释可能因地址族而异。不同的地址族可以选择以任何对它有意义的形式来解释协议。恰好以太网和 IP 一直使用大端序(并且非常重要/无处不在,以至于大端序被称为网络顺序)。

    第三,AF_INET 世界中的协议号(即 Internet 协议)只占用一个字节,因此指定字节顺序没有意义。

    【讨论】:

    • for EthernetIP 报头的在线格式称为网络字节顺序(正如我在第二段中解释的那样)。您可以“在线”下订单。
    • 对于第 1 段,考虑到 ETH_P_XXX 值是用于“与传入数据包比较”,那么为什么 IPPROTO_XXX 值没有转换为网络字节顺序。我知道第 2 段给出了上述问题的原因,即不同的协议族可以选择以对其有意义的形式来解释协议。那么这是否意味着这是函数开发者的决定,因为他可以选择在函数内部转换协议?对于第三段,协议号作为 int 传递,因此字节排序确实有意义。如果我错了,请纠正。
    • 这样想:如果您在 little-endian 机器上比较两个 16 位值。进行比较的方法有两种:(a) 按照它们在内存中出现的顺序逐字节比较,(b) 将 16 位单元加载到寄存器中,然后进行比较。另一方面,对于单字节值,这两种方式都相当于同一件事。
    • 我的意思是 08 00 00 00 被传递给 ntohs(0x800) 并注意该字节流在任一大端序中是 not 0x800(它将是 0x08000000 ) 或 little-endian (它只是 0x00000008)。相反,它是一个大端 short 转换为一个小端 int。当转换回短时,尽管它将保留其大端短值。同样具有单字节 IPPROTO_xx 值。它将被放入 32 位 int 的最低位字节,但当转换回 char 时,它会保留相同的值:因此单个字节不需要字节交换。
    • 使用大端值的实际原因[我认为]主要是为了保持一致性和传统的延续。套接字 API 最初是在大端机器上创建/使用的。当它被移植到 little-endian 时,必须做出选择:(a) 确保堆栈的所有部分都知道各种寻址值(以太网类型、IP 地址、端口)的结构并以本机格式传递它们,或者(b) 将正确排序字节的要求留给用户空间,允许内核的大部分部分将地址结构视为不透明的 blob。
    猜你喜欢
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 2018-11-26
    • 2023-01-27
    • 1970-01-01
    • 2020-02-21
    相关资源
    最近更新 更多