网络传输协议TCP,你了解多少?

48 阅读6分钟

image.png

一、简介

TCP,传输控制协议(Transmission Control Protocol),是面向连接的,提供可靠、有序的传输并且还提供流控和拥塞控制。

二、三次握手

TCP通过三次握手来建立客户端与服务端的连接

第一次握手

客户端向服务器发送一个SYN( Synchronize Sequence Numbers)包,表示请求建立连接。此时客户端进入SYN_SEND状态。

第二次握手

服务器收到客户端的SYN包后,向客户端发送一个SYN+ACK包,表示同意建立连接。此时服务器进入SYN_RECV状态。

第三次握手

客户端收到服务器的SYN+ACK包后,向服务器发送一个ACK包,表示确认建立连接。此时客户端进入ESTABLISHED状态,服务器也进入ESTABLISHED状态

举个打电话的例子

  • 第一次握手:小明给小红打电话,接通了后,小明说喂,能听到吗,这就相当于是连接建立。
  • 第二次握手:小红给小明回应,能听到,你能听到我说的话吗,这就相当于是请求响应。
  • 第三次握手:小明听到小红的回应后,好的,这相当于是连接确认。在这之后小明和小红就可以通话/交换信息了。

两次握手可以吗?

第三次握手主要为了防止已失效的连接请求突然又传输到了服务端,导致产生问题。

比如客户端A发出连接请求,可能因为网络阻塞原因,A没有收到确认报文,于是A再重传一次连接请求。 连接成功,等待数据传输完毕后,就释放了连接。

然后A发出的第一个连接请求等到连接释放以后的某个时间才到达服务端B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段。

如果不采用三次握手,只要B发出确认,就建立新的连接了,此时A不会响应B的确认且不发送数据,则B一直等待A发送数据,浪费资源。

三、四次挥手

第一次挥手

客户端向服务器发送一个FIN包,表示请求关闭连接。此时客户端进入FIN_WAIT_1状态。

第二次挥手

服务器收到客户端的FIN包后,向客户端发送一个ACK包,表示同意关闭连接。此时服务器进入CLOSE_WAIT状态。客户端收到服务器的确认后,进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段

第三次挥手

服务器完成数据传输后,向客户端发送一个FIN包,表示请求关闭连接。此时服务器进入LAST_ACK状态。

第四次挥手

客户端收到服务器的FIN包后,向服务器发送一个ACK包,表示确认关闭连接。此时客户端进入TIME_WAIT状态,等待一段时间(2MSL)以确保所有报文都被传输完毕。一段时间后,客户端进入CLOSED状态。

还是举电话的例子

  • 第一次挥手:小明对小红说,我所有的东西都说完了,我要挂电话了。
  • 第二次挥手:小红说,收到,我这边还有一些东西没说。
  • 第三次挥手:经过若干秒后,小红也说完了,小红说,我说完了,现在可以挂断了
  • 第四次挥手:小明收到消息后,又等了若干时间后,挂断了电话。

为什么是四次挥手?

当Server端收到Client端发出的连接释放报文时,很可能并不会立即关闭SOCKET,所以Server端先回复一个ACK报文,告诉Client端我收到你的连接释放报文了。只有等到Server端所有的报文都发送完了,这时Server端才能发送连接释放报文,之后两边才会真正的断开连接。故需要四次挥手。

第四次挥手为什么要等待2MSL时间?

保证A发送的最后一个ACK报文段能够到达B。

这个ACK报文段有可能丢失,B收不到这个确认报文,就会超时重传连接释放报文段,然后A可以在2MSL时间内收到这个重传的连接释放报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入到CLOSED状态。

防止已失效的连接请求报文段出现在新连接中。

A在发送完最后一个ACK报文段后,再经过2MSL,就可以使这个连接所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现旧的连接请求报文段。

四、UDP的区别

  • 连接

    • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
    • UDP 是不需要连接,即刻传输数据。
  • 可靠性

    • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    • UDP 是尽最大努力交付,不保证可靠交付数据。
  • 服务对象

    • TCP 是一对一的两点服务,即一条连接只有两个端点。
    • UDP 支持一对一、一对多、多对多的交互通信
  • 拥塞控制、流量控制

    • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
    • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

五、粘包和拆包

问题

TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分。 如果要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;如果要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;所以解决这个问题需要应用层显式地处理。

解决方案

  • 消息定长

    • 发送端将每个数据包封装为固定长度
  • 添加特殊字符分割

    • 在数据的末尾或开头添加一个特殊字符,以作为不同数据包之间的界限。
  • 自定义协议

    • 将数据分为两部分,一部分是头部,一部分是内容体;头部信息,比如数据的长度或类型,以便接收端解析