绑定套接字时,为什么应将端口号转换为网络字节顺序?

8

我发现在设置socket的核心代码中出现了这段片段:

#define PORT xxxx

struct sockaddr_in self;
self.sin_family = PF_INET;
self.sin_port = htons(PORT);

我知道我们需要将在网络上传输的数据的字节顺序转换为 网络字节序,但是我不明白为什么在设置套接字时也需要将端口号转换为它。我的意思是,在绑定时,这不是一个“本地”的事情吗?假设我们打算绑定的端口是 1,而机器实际上使用的是小端序;现在我们将其转换为网络字节序,那么我们难道不是将完全不同的端口绑定到套接字上吗?


因为这是套接字API最初由伯克利的人定义的方式? - Chris Dodd
5个回答

4

我想假设你正在使用TCP协议。端口号将在数据包头中。它将被传输,因此以网络字节顺序表示。


但是在执行绑定操作时,端口完全被反转了,这样不会导致解释不同吗? - Xufeng
1
Socket是操作系统的一部分,但是操作系统可以在任何机器上运行,可能是大端机器。在使用sockets编程时,我们不应编写与机器相关的代码(尽管我们无论如何都无法避免),因此应该以网络字节顺序编写代码,这是与机器无关的。在我看来,这只是一个定义。 - Hot.PxL
API的更好设计应该是完全端中立的,这样API的用户就不必为每个sockaddr_in手动处理它。但事实并非如此,现在由于兼容性原因我们被困在这里。 - Jeremy Friesner
@JeremyFriesner,你们的意思是我们应该使用网络字节顺序来实现可移植性,即使这意味着在小端机器上绑定的端口与原始PORT宏不同? - Xufeng
1
@Xufeng,你应该按照示例使用htons()编写代码,这样它在所有机器上都能正常工作。(在理想的世界里,你不必这样做,但在我们实际的世界中,你必须这样做) - Jeremy Friesner

1
你是否在问为什么应用程序员要这样做,而不是库在内部完成它?如果是的话,我唯一想到的技术优势是它允许应用程序进行一次转换并缓存它,而无需进行多次转换即可多次使用。在TCP上,您只需要在每个连接上使用它一次,并且通常不会建立数百万个连接。但是,在UDP上,每次发送数据包时都需要使用它,可以合理地假设您将进行数百万或数十亿次这样的调用。然后,对于众多对于UDP的sendto()调用,重新排序(如果必要)的地址被提供给操作系统,该操作系统可以直接将其复制到传出网络数据包中。在内核中执行的替代方法将要求每次调用sendto()时重复采用应用程序已知的相同地址,并每次重新转换。由于sendto()受益于此,这可能足以成为他们使API的其余部分工作方式相同的充分理由。

0

机器可以使用不同的编码方式,如小/大端。为了标准化,在通过网络进行通信时应使用统一的编码方式。这就是为什么无论是小端还是大端,都必须将编码转换为网络字节顺序,因为重要的是它是统一的,并且每个设备和软件在网络中都能正确理解。


0

您可以通过网络传输端口号。它是TCP的IP数据包的一部分。请查阅RFC (ietf.org/rfc/rfc793.txt)


第二个问题呢?如果端口号在 hton 后被反转,bind 如何正确识别端口? - Xufeng

0

struct sockaddr_in只是struct sockaddr的包装结构:

struct sockaddr {
    unsigned short sa_family;
    char           sa_data[14];
};

端口号和IP地址在这里一起保存。它们被保存在sa_data[14]中-前2个字节保存端口号,接下来的4个字节保存IP地址。剩余的8个字节未使用。当您使用sockaddr_in时,通过sin_zero[8]将其清零为零。
整个内容都通过网络发送,包括以网络顺序发送的端口号。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接