此文内容全部基于小林Coding图解网络系列,将此记录在博客中也是为了方便自己查阅和复习,如有任何侵权等问题,联系我立即删除!
TCP三次握手
注意:第三次握手是可以携带数据的,前两次握手是不可以携带数据的
Linux中查看TCP状态
netstat -nap
为什么是三次握手
1.避免历史连接
简单来说就是,当之前发送第一次握手信息(seq=90)后,客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq = 100)报文。当之前的SYN到达服务器后,会开始第二次握手ack=90+1发给客户端,但此时客户端想要的ack是101,因此发送RST给服务器断开连接。
如果是两次握手的话,当服务器收到seq=90的时候,他就会在发送完ACK后,开始传输数据,这样就可能建立一个历史连接,造成**资源浪费**
2.同步双方初始序列号
在握手的时候,发送SYN报文,每一次发送了SYN后,都需要有ack=seq+1来回应,表示已经接受了该SYN报文。因此两个SYN报文就需要两个ACK报文来回应,但是因为第二次握手可以将SYN报文和ACK一起发过去,因此就是三次握手。
3.避免浪费资源
当服务器收到SYN报文后,发送完ACK报文,建立起一个连接,那么当服务器第二次握手发送的ACK报文如果没有被客户端收到,那么客户端就会重新发送一次SYN报文,这个时候服务器又建立起一个连接,就会浪费。如果客户端发送的 SYN
报文在网络中阻塞了,重复发送多次 SYN
报文,那么服务端在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
为什么建立新连接时序列号要不同
防止历史报文被新的连接接受,造成数据混乱
TCP四次挥手
TCP挥手一定需要四次吗?
答案是“大多数情况下,三次挥手比四次挥手情况更多“
什么情况下是三次挥手
当被动关闭方(上图的服务端)在 TCP 挥手过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
https://xiaolincoding.com/network/3_tcp/tcp_three_fin.html#为什么-tcp-挥手需要四次呢
讨论四次挥手中每一次挥手丢失会发生什么。
注意:ACK报文是不会主动重发的,当丢失的报文是ACK报文时,重传的报文是FIN报文!!
为什么需要TIME_WAIT状态
1.原因一:防止历史连接中的数据,被后面相同四元组的连接错误的接收
如果TIME_WAIT状态不存在或者时间小于2MSL
2.原因二:保证「被动关闭连接」的一方,能被正确的关闭
如果客户端(主动关闭方)最后一次 ACK 报文(第四次挥手)在网络中丢失了,那么按照 TCP 可靠性原则,服务端(被动关闭方)会重发 FIN 报文。
假设客户端没有 TIME_WAIT 状态,而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态,如果该 ACK 报文丢失了,服务端则重传的 FIN 报文,而这时客户端已经进入到关闭状态了,在收到服务端重传的 FIN 报文后,就会回 RST 报文。
服务端收到这个 RST 并将其解释为一个错误(Connection reset by peer),这对于一个可靠的协议来说不是一个优雅的终止方式。
Socket编程
- 服务端和客户端初始化
socket
,得到文件描述符; - 服务端调用
bind
,将 socket 绑定在指定的 IP 地址和端口; - 服务端调用
listen
,进行监听; - 服务端调用
accept
,等待客户端连接; - 客户端调用
connect
,向服务端的地址和端口发起连接请求; - 服务端
accept
返回用于传输的socket
的文件描述符; - 客户端调用
write
写入数据;服务端调用read
读取数据; - 客户端断开连接时,会调用
close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
这里需要注意的是,服务端调用 accept
时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。
所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。
Linux内核中会维护两个队列:
- 半连接队列(SYN 队列):接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状态;
- 全连接队列(Accpet 队列):已完成 TCP 三次握手过程,处于 ESTABLISHED 状态;