套接字(socket
)是一种使用标准Unix文件描述符与其他程序进行通信的方式,它在实际的应用中都十分常用。所以从这一篇文章开始,我将详细介绍一下Linux环境下的socket的用法。本篇文章将介绍套接字、字节序和地址结构体的相关知识。
1 什么是套接字
计算机网络由多个层组成,每个网络层提供关于该层数据的不同限制。而套接字的本意是提供一种进程间通信的方法,使得在相同或不同主机上的进程能够以相同的规范进行双向的信息传送。如下图所示,套接字接口提供了对网络下层(1-4层)的统一API,并能套接字应用程序中实现上层(5-7层和应用格式)功能。
套接字是一种通用的编程接口,可以用于与不同类型的网络协议进行交互,不仅仅限于IP协议,还有UDP、ICMP等。在实际应用中,套接字应用程序通常会依赖于IP协议来实现网络通信。在传输层,套接字支持两种特定的协议:TCP(Transmission Control Protocol
)和UDP(User Datagram Protocol
)。其中,TCP是面向连接、可靠、有序的流协议,而UDP是无连接、不可靠、自我界定的数据报协议。
在Linux中,Layer2-Layer4已经由内核实现,所以套接字可以看做是内核提供的给用户使用的网络协议栈的编程接口。
2 字节序
端口号和IP地址由多字节数据类型表示,它们被放置在数据包中,用于路由和多路复用。端口号占据2个字节,IPv4地址占据4个字节,但在不同架构之间传输多字节数据类型时会出现问题。
大端(Big-Endian
)和小端(Little-Endian
)是两种不同的字节序方式,用于表示多字节数据类型(如整数、浮点数)在计算机内存中的存储方式。
- 大端字节序:MSB(最高有效位)位于地址最低的内存单元
- 大端字节序:LSB(最低有效位)位于地址最低的内存单元
如下图所示,一个64位的值0x1122334455667788,在小端机器的内存中为0x8877665544332211,而在大端机器的内存中为0x1122334455667788。
如下图所示,可以通过程序来判断计算机的字节序:
在网络协议中,使用的是大端字节序,而大部分的主机使用的是小端字节序。所以,在Socket
编程中提供了下面的函数来更改IP和端口号的字节序:
1.转换端口号(16位的整数)
Host->Network:主机字节序转网络字节序
unit16_t htons(uint16_t hostportnumber)
Network->Host:网络字节序转主机字节序
unit16_t ntohs(uint16_t netportnumber)
2.转换IPV4地址(32位的整数)
Host->Network
unit32_t htonl(uint32_t hostportnumber)
Network->Host
Unit32_t ntohl(uint32_t netportnumber)
3 地址结构体
像connect()
、accept()
和bind()
等套接字函数需要使用明确定义的地址结构来保存IP地址信息、端口号和协议类型。我们使用套接字来编写使用不同协议的网络应用程序,比如我们可以使用IPV4、IPV6、Unix等。问题在于:每种不同的协议都使用不同的地址结构来保存其寻址信息。
我们有存在一个通用的地址结构:struct sockaddr
,在传递给这些套接字函数时,必须将您特定于协议的地址结构强制转换为通用的地址结构。
特定于协议的地址结构通常以sockaddr_
开头,并以取决于该协议的后缀结尾。如下:
struct sockaddr_in
:sockaddr_in
结构用于表示IPv4地址,通常称为"Internet地址"。它包含 IP 地址、端口号和协议族信息。这是在IPv4网络编程中最常使用的结构。
struct sockaddr_in6
:sockaddr_in6
结构用于表示IPv6地址,与sockaddr_in
类似,但适用于IPv6网络编程。它包含IPv6地址、端口号和协议族信息。
struct sockaddr_un
:sockaddr_un
结构用于表示本地Unix套接字地址。Unix域套接字是用于本地通信的一种方式,而不是通过网络进行通信。它包含套接字文件的路径信息。
struct sockaddr_dl
:sockaddr_dl
结构用于表示数据链路层地址,通常在底层网络编程中使用。这包括硬件地址,如MAC地址。这通常用于需要直接访问底层网络的应用程序。
本篇文章仅介绍IP4地址结构:struct sockaddr_in
,来看一下它的结构体成员:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
sin_family
:地址结构的类型,在这里固定设置为AF_INET
,表示这是一个IPv4地址结构sin_port
:端口号struct in_addr sin_addr
:IPv4地址信息的结构体,该结构体中使用s_addr
存储32位的IPv4地址
我们平时使用的地址都是192.168.1.1
这种类型的,如果要转换为s_addr
类型的整数,我们可以使用inet_addr
或inet_aton
将点分十进制字符串表示的IPv4地址转换为网络字节顺序的32位整数。
inet_addr
已经过时了,建议使用inet_aton
,因为它可以提供更多的错误处理信息
反过来,我们可以使用inet_ntoa()
将32位的网络字节顺序的整数表示的IPv4地址转换为点分十进制字符串。