1

Socket网络通信

 2 years ago
source link: https://blog.p2hp.com/archives/9116
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Socket 是什么?

Socket 其实就是套接字,大部分人对于 Socket 的理解就是它可以实现一个简单的网络通信,但是它「具体解决了哪些问题?有什么实际的作用?为什么会有一个 Socket 出现?」

1277635-20220704160500516-229964898.png

Socket 其实是在「应用层与传输层之间的一个产物」,它把传输层的很多复杂操作封装成一些简单的接口,来让应用层调用以此来实现进程在网络中的通信,Socket 是对端口通信开发的工具,它要更底层一些。

Socket 其实类似于一台洗碗机,它的功能就是洗碗(网络通信),如果没有它,你可能需要自己手动去洗碗(手动调用传输层、应用层之间的各个 api),但是有了它你只需要点击开关、调整时长就行了(封装了 api),你可以不需要它,但是如果没有它,洗碗(应用层与传输层之间的交互)将变得非常繁琐。
1277635-20220704160546465-1770577749.png

一次完整的网络通信必不可少的会经过物理传输层的网线和网卡,网络传输层的 IP 协议可以知道要将数据传送给哪台机器,但是在计算机系统中会运行不同进程,那要如何把「网卡中的网络数据识别出来是给哪个进程的」,这其实就是 Socket 设计的想解决的一点了。

Socket 是「对 TCP/IP 或者 UDP/IP 协议的封装」,Socket 本身其实就是一个调用接口。通过这个接口我们在开发网络应用程序的时候,就可以不用关心底层是怎么实现的,减轻开发的难度。

Socket 运行流程

基于 TCP

1277635-20220704160620684-750275136.png

Server

  • socket():表示创建一个 socket,底层会生成一个文件描述符,用来表示该 socket
  • bind():用来绑定服务的端口,地址,这里一般都是以固定的为主,因为在客户端连接的时候需要指定
  • listen():当绑定完成之后,listen 就会监听这个端口的数据包
  • accept():相当于一个开关,表示我准备好了,可以接受请求了,但是这里会一直阻塞,直到客户端连接成功
  • read():读取客户端发送过来的内容
  • write():客户端写入要返回的数据
  • close():断开连接,「四次挥手」

Client

  • socket():表示创建一个 socket,底层会生成一个文件描述符,用来表示该 socket
  • connet():表示与指定地址进行连接,在此之前,会随机创建自己的端口,tcp 的「三次握手就是从这里开始」
  • write():客户端写入要发送的数据
  • read():客户端读取服务端返回的数据
  • close():断开连接,「四次挥手」,给客户端发送断开连接的信息

基于 UDP

1277635-20220704160643286-1354977550.png
 因为 UDP 是无状态的,所以对于服务端来说没有连接,并且其会在调用 Recvfrom() 方法后就收客户端的请求,并一直阻塞,直到收到信息

Socket TCP 是如何建立连接的

在 Socket 绑定完服务器的地址后,就开始和服务器建立连接了,TCP 建立连接的方式其实就是大名鼎鼎三次握手了

1277635-20220704160716790-1343835157.png
  • 第一次握手:A 的 TCP 进程创建一个 传输控制块 TCB ,然后向 B 发出连接请求报文段。之后将同步位 SYN 设置为 1,同时选择一个初始序列号 seq=x,这时客户端 A 进入到 SYN-SENT(同步已发送)状态。
  • 第二次握手:B 收到连接请求报文段,如果同意建立连接,则向 A 发送确认。在确认报文段中 同步位 SYN=1、确认位 ACK=1、确认号 ack=x+1,同时也为自己选择一个初始序列号 seq=y,这时服务器 B 进入 SYN-RCVID 状态。
  • 第三次握手:A 收到 B 的确认以后,再向 B 发出确认。确认报文 ACK=1、确认号ack=y+1。这时A进入到 ESTAB-LISHED 状态。当B接收到A的确认后,也进入 ESTAB-LISHED 状态。连接建立完成

三次握手发生在 socket 的哪几个函数中

 
1277635-20220704160820241-400684540.png
  • 当客户端调用 connect 时,触发了连接请求,向服务器发送了SYN 信号,这时 connect 进入阻塞状态;
  • 服务器监听到连接请求,即收到 SYN,调用 accept 函数接收,进入阻塞状态,在此之前会尽力 socket、bind、listen 函数;然后返回相关的 syn 以及 ack 信号
  • 客户端接受到服务端的信息,此时 connect 完成,解除阻塞状态,并且向服务端发送 ack 信号
  • 服务端收到 ack, accept 阻塞解除,完成连接

在建立连接之后,connect() 就已经执行完毕了,服务端就可以向客户端发送数据了。

Socket TCP 是如何断开连接的

1277635-20220704160843048-1038402050.png
  • 第一次挥手:A 先发送连接释放报文段,段首部的终止控制位 FIN=1,序号seq=u(等于A前面发送数据的最后一个序号加1);然后 A 进入 FIN-WAIT-1(终止等待1)状态,等待 B 的确认。
  • 第二次挥手:B 收到 A 的连接释放报文段后,立刻发出确认报文段,确认号 ack=u+1,序号 seq=v(等于 B 前面发送数据的最后一个序号加1);然后 B 进入 CLOSE-WAIT(关闭等待)状态。
  • 第三次挥手:A 收到 B 的确认报文段后进入到 FIN-WAIT-2(终止等待2)状态,继续等待 B 发出连接释放报文段;
    • 若 B 已经没有数据要发送,B 就会向 A 发送连接释放报文段,段首部的终止控制位 FIN=1,序号 seq=w(半关闭状态可能又发送了一些数据),确认号 ack=u+1,这时B进入 LAST-ACK(最后确认)状态,等待A的确认。
  • 第四次挥手:A收到B的连接释放报文段并发出确认,确认段中 确认位 ACK=1,确认号 ack=w+1,序号 seq=u+1;然后 A 进入到TIME-WAIT(时间等待)状态。当 B 再接收到该确认段后,B 就进入 CLOSED状态。

第四次挥手后为何要等待 2MSL

首先 2MSL 的时间是从客户端(A)接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端(A)的 ACK 没有传输到服务端(B),客户端(A)又接收到了服务端(B)重发的 FIN 报文,那么 2MSL 时间会被重置。等待 2MSL 原因如下

  • 1.得原来连接的数据包消失
    • 如果B没有收到自己的ACK,会超时重传FiN那么A再次接到重传的FIN,会再次发送ACK
    • 如果B收到自己的ACK,也不会再发任何消息

在最后一次挥手后 A 并不知道 B 是否接到自己的信息, 包括 ACK 是以上哪两种情况,A 都需要等待,要取这「两种情况等待时间的最大值,以应对最坏的情况发生」,这个最坏情况是:去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。这刚好是2MSL,这个时间,足以使得原来连接的数据包在网络中消失。

  • 2.保证 ACK 能被服务端接收到从而正确关闭链接

因为这个 ACK 是有可能丢失的,会导致服务器收不到对 FIN-ACK 确认报文。假设客户端不等待 2MSL ,而是在发送完 ACK 之后直接释放关闭,一但这个 ACK 丢失的话,服务器就无法正常的进入关闭连接状态。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK