socket英文意思是”插口”,在网络编程中,它的寓意是可以通过插口接入的方式,快速完成网络连接和数据收发.
客户端和服务器工作的核心逻辑
服务器端:
- 初始化socket(在客户端发起连接请求前,服务器必须先初始化好).
- 执行bind函数(将自己的服务能力绑定到一个众所周知的地址和端口上)
- 执行listen操作(将原先的socket转换为服务端的socket)
- 服务器阻塞在accept上,等待客户端请求的到来
客户端:
- 初始化socket
- 执行connect向服务器端的地址和端口发起连接请求(地址和端口是客户端预先知道的).
建立连接后,就可以进行数据传输了.
客户端进程向操作系统内核发起 write 字节流写操作,内核协议栈将字节流通过网络设备传输到服务器端,服务器端从内核得到信息,将字节流从内核读入到进程中,并开始业务逻辑的处理,完成之后,服务器端再将得到的结果以同样的方式写给客户端。可以看到,一旦连接建立,数据的传输就不再是单向的,而是双向的,这也是 TCP 的一个显著特性。
当客户端和服务器端完成交互后,需要断开连接时,就会执行close函数.操作系统内核此时会通过原先的连接链路向服务器端发送一个 FIN 包,服务器收到之后执行被动关闭,这时候整个链路处于半关闭状态,此后,服务器端也会执行 close 函数,整个链路才会真正关闭。半关闭的状态下,发起 close 请求的一方在没有收到对方 FIN 包之前都认为连接是正常的;而在全关闭的状态下,双方都感知连接已经关闭。
以上所有操作都是通过socket来完成的,socket是用来建立连接,传输数据的唯一途径.
socket地址格式
在使用socket时,首先要解决双方的寻址问题,由于需要socket的地址来建立连接,所以我们首先需要找到这个地址. 就像打电话(使用socket进行通信)时首先需要查找电话簿(寻址),找到你想要联系的那个人,你才可以建立连接,开始交流
通用sokcet地址格式
1 | C复制代码/* POSIX.1g 规范规定了地址族为2字节的值. */ |
在这个结构体里,第一个字段是地址族,它表示使用什么样的方式对地址进行解释和保存,好比电话簿里的手机号格式,或者是固话格式,这两种格式的长度和含义都是不同的.地址族在 glibc 里的定义非常多,常用的有以下几种:
- AF_LOCAL:表示的是本地地址,对应的是 Unix 套接字,这种情况一般用于本地 socket 通信,很多情况下也可以写成 AF_UNIX、AF_FILE;
- AF_INET:因特网使用的 IPv4 地址;
- AF_INET6:因特网使用的 IPv6 地址
这里的 AF_ 表示的含义是 Address Family,即地址族, 但是很多情况下,我们也会看到以 PF_ 表示的宏,比如 PF_INET、PF_INET6 等,实际上 PF_ 的意思是 Protocol Family,也就是协议族的意思。我们用 AF_xxx 这样的值来初始化 socket 地址,用 PF_xxx 这样的值来初始化 socket。我们在<sys/socket.h>
头文件中可以清晰地看到,这两个值本身就是一一对应的。
1 | C复制代码/* 各种地址族的宏定义 */ |
通用地址结构中的通用的意思是适用于多种地址族.
IPv4 socket地址格式
1 | C复制代码/* IPV4套接字地址,32bit值. */ |
和通用地址格式一样,都有一个16bit的sin_family字段,对于IPv4来说这个字段的值就是AF_INET.
对于端口号,我们可以看到端口号最多是 16-bit,也就是说最大支持 2 的 16 次方,这个数字是 65536,所以我们应该知道支持寻址的端口号最多就是 65535。有一些保留端口,即约定俗成的,已经被对应服务广为使用的端口,如ssh的22端口,http的80端口,一般而言,大于5000的端口可以作为我们自己应用程序的端口使用.
glibc定义的保留端口如下:
1 | C复制代码 |
IPv6 socket地址格式
1 | C复制代码struct sockaddr_in6 |
整个结构体长度是 28 个字节,其中流控信息和域 ID 先不用管,这两个字段,一个在 glibc 的官网上根本没出现,另一个是当前未使用的字段。这里的地址族显然应该是 AF_INET6,端口同 IPv4 地址一样,关键的地址从 32 位升级到 128 位,这个数字就大到恐怖了,完全解决了寻址数字不够的问题。
本地socket地址格式
前面的IPv4和IPv6地址格式都是因特网socket的格式,而本地socket格式是用来作为本地进程间的通信的,也就是前面提到的 AF_LOCAL
1 | C复制代码struct sockaddr_un { |
几种socket地址格式的比较
IPv4和IPv6的socket地址格式的长度是固定的,而本地socket地址格式的长度是可变的.
问题
- 这些socket地址格式有什么共性?
都具有地址族字段和地址字段(本地socket地址格式中,路径名即是地址).
通过通用socket地址格式来提供一个统一的接口,然后通过地址族字段来确定具体是什么类型的地址.
2. 为什么本地socket格式不需要端口号,而 IPv4 和 IPv6 socket格式却需要端口号?
在unix系统中,一切皆文件,socket也是文件.
本地socket本质上是在访问本地的文件系统,根据文件路径就可以区分,所以不需要端口号.而远程socket是直接将一段字节流发送到远程计算机的一个进程,而远程计算机可能同时有多个进程在进行监听,所以需要用端口号标志发给哪一个进程.
本文转载自: 掘金