Linux 网络
- 一前言
- 二、Udp协议
- 1)、Udp协议特点
- 2)、Udp协议格式
- 3)、Udp报文封装和解包过程
- 4)、UDP的缓冲区
- 三、TCP协议
- 1)、TCP协议特点
- 2)、TCP协议格式
- 1、4位首部长度、源端口、目的端口
- 2、16位窗口大小
- 3、Tcp确认应答机制
- 4、序号和确认序号
- 5、标记位
- 四、面向字节流解释
- 五、超时重传机制
- 1)、触发超时重传的两种具体原因
- 2)、超时时间
- 六、连接管理机制
- 1)链接建立和断开时,双方链接状态变化
- 2)经典面试题1:为什么需要进行3次握手,4次挥手
- 3)经典面试题2:请你说说 TCP 三次握手四次挥手过程
- 4)TIME_WAIT状态
- 5)地址复用,bind()失败问题
- 6)、服务器变慢可能原因
- 七、滑动窗口
- 1)什么是滑动窗口
- 2)如何理解滑动窗口
- 3)、快重传机制
- 4)、其他问题
- 八、流量控制
- 九、拥塞控制
- 十、延迟应答、捎带应答
- 十一、粘包问题
- 十二、TCP异常情况
- 十三、面试题:请你说说 TCP 如何实现可靠传输
- 十四、TCP/UDP对比
- 十五、用UDP实现可靠传输
一前言
本篇文章将会介绍Tcp/Udp协议中的所有字段、以及确认应答、超时重发、连接管理、流量控制、拥塞控制、滑动窗口、快速重传、延迟应答、捎带应答相关面试高频考点!!
二、Udp协议
1)、Udp协议特点
Udp是一种面向数据报,无连接、不可靠传输协议。Udp广泛应用于直播领域。
面向数据报解释:
Udp报头中存在描述完整Udp报文长度字段(后面介绍),所以在内核将报头和有效载荷分离后,交给上层的就是一个完成报文!!并且不管应用层交给Udp多长的报文,Udp原样发送,既不才分也不合并。所以Udp面向数据报。
无连接解释:
Udp协议通信只需要对端的ip和port即可通信,不需要建立连接。就好比寄快递,我只需要知道对方的地址就可以向对方邮寄东西了。
不可靠解释:
首先需要明确的是,可靠和不可靠描述的是不同通信协议的特点,本身并不存在谁好谁坏!前面已经提到过Udp无连接,而Udp报文发送过程中可能会存在丢包问题。但对于Udp而言,Udp认为只要将数据交给网络层就发送成功,对于网络报文丢包问题并不做任何处理(Tcp会重传)
虽然Udp不会做丢失重传等工作,但其实在目前的网络环境中,数据丢包的概率极小。并且Tcp为了保证可靠性,需要做大量工作(超时重发、流量控制、拥塞控制、快速重传…),也就意味着Tcp通信更复杂,速度更低。所以如果对可靠性要求不高的场景,一般选择Udp作为传输层协议。
2)、Udp协议格式
- 16位UDP长度,:表示整个数据报(UDP首部+UDP数据)的最大长度。
- 16位UDP校验和:如果校验和出错, 就会直接丢弃;
任何协议都需要解决两个问题:报文和有效载荷分离问题,报文向上交付问题。
报文和有效载荷分离问题:首先Udp报头采用固定长度,8字节。并且报头中16位UDP长度
字段表示整个报文长度。所以在读取Udp报文时,只需要先读取报头(8byte),剩余就是有效载荷了。
报文向上交付问题:报文中存在目的端口号
字段,明确交给上层那个端口。
3)、Udp报文封装和解包过程
在OS中,存在大量的Udp报文。OS需要对报文进行管理,先描述在组织!
4)、UDP的缓冲区
Udp没有真正的发送缓冲区。数据从应用层拷到传输层后,添加报头,构建
sk_buff
结构体后直接向下交付,交给网络层。
但Udp存在接收缓冲区。原因在于Udp可能会接收到大量的报文。Udp本身特点不可靠,如果对端传输层中已经存在报文,但上层没有及时取走,此时Udp可以直接接将新收到的报文丢弃或覆盖原始报文,这并不影响上层对Udp处理,但这并不合理。 Udp报文经过千里迢迢之后将报文正确发送到目标主机后,在这其中消耗了大量的资源,Udp收到报文后仅因为上层没有及时将前一个数据取走,就直接将新报文丢弃。Udp虽然不可靠,但也不能这么不可靠。所以对于Udp接收方,我们还是提供了一个接收缓冲区,暂时保存新到的报文!
Udp接收缓冲区本质就是一个队列,将所有新到的sk_buff结构体连接到改队列下即可。
三、TCP协议
1)、TCP协议特点
TCP全称为 “传输控制协议(Transmission Control Protocol”)。Tcp不仅控制报文传输(丢包了需要重传,网络拥塞时执行网络拥塞算法,以及流量控制等)保证可靠性,还做了大量工作来保证效率(后面介绍)
TCP是一种可靠的,面向字节流的通信协议,并且在通信前需要先建立连接!
2)、TCP协议格式
1、4位首部长度、源端口、目的端口
任何协议都需要解决两个问题:报文和有效载荷分离问题,报文向上交付问题。
报文和有效载荷分离问题:Tcp的标准报头长度为20字节,4位首部长度表示Tcp报头的总长度。所以在分解报文时,只需先读取头20字节数据,然后分析提取出4位首部长度。此时就能明确Tcp报头的长度,将报文和有效载荷分离。
在Tcp报头中存在目的端口号,解决向上交付问题!
- 4位首部长度的基本单位为4字节,所以tcp报头的长度为[20,4*15],即[20, 60]
2、16位窗口大小
Tcp典型的全双工协议,双方地位是对等的。在报文发送过程中,Tcp为了保证可靠性需要做流量控制!如果接收方来不及接收,就让发送端少发点。(否则Tcp收到多余报文会被丢弃,不可靠,不合理)
为了实现流量控制,发送端需要知道接收端的接收能力,也就是对端接收缓冲区的剩余空间大小!而16位窗口中填写的就是接收缓冲区剩余空间大小!
在tcp协议中,所有的报文都必须应答。所以客户端和服务器直接互发消息时,对端必须确认应答。而确认应答大多数就是一个最简单的标准报头,而报头中携带的16位窗口大小表示接收方的接收能力。从而实现告知对方我的接收能力!
- 上述所有的行为都是双方Tcp协议做的,应用层不需要关心。就像文件操作,我们直接调用write将用户数据拷贝到内核后,数据什么时候刷新落盘是由OS来决定实现的,上层用户不需要关心。
- Tcp流量控制和管道类似,当接收端满了后,发送端会发送阻塞。如果此时上层还在忘发送端的发送缓冲区写,导致发送缓冲区也满了,此时上层进程就会发生阻塞!
3、Tcp确认应答机制
Tcp规定接收端必须对接收到的数据进行应答。所以我们可以每发一条消息后,等待应答。然后在发生下一条数据! 但这样串行发送,效率较低。所以实际Tcp发送报文是一次发送多个报文,然后等待应答!
- tcp为了保证可靠性,所以Tcp引入确认应答机制就可以100%明确历史最近一跳消息一定被对方收到!
- tcp做不到100%可靠的网络通信,原因在于最近一条消失没有应答。
4、序号和确认序号
发送方一次发送大量报文,接收方接收到的数据可能是乱序的。而乱序是不可靠的一种表现(发送http报文时,报头和有效载荷接收反了,上层处理会出异常) 所以Tcp引入
32位序号
,保证报文的按序到达!接收方根据接收报文中的序号进行排序即可修正!
但发送方还需要对发送的大量报文进行应答。但一次发送大量报文,如何得到接收到的应答是对历史那个具体报文的确认呢? 所以Tcp引入32位确认序号
(确认序号= 序号 + 1
)。确认序号表示确认序号之前的报文都被对方接收到了!
为什么确认序号需要怎么规定?
这样可以允许应答有少量丢失!!客户端一次向客户端发送大量tcp报文时,理论而言客户端需要收到所有报文的应答。但确认序号规定确认序号之前的报文全部被对方接收了。所以即使存在少量应答丢失,但后面的应答也可以对前面的所有数据进行确认!!
经典面试题:为啥序号和确认序号要分开,不能统一?
原因在于客户端向服务器发送消息后,服务器受到必须进行应答。但服务端不仅仅只进行应答,有可能也想向客户端发送数据。而应答本质也是将Tcp报头中的ack标志位由0置1即可。所以服务端会将应答和发送数据和二为1,即捎带应答!
所以此时的报文既是对上次历史发送报文的应答(需要确认序号),也是数据(需要序号)。所以序号和确认序号有可能会被同时使用,所以必须分开!!
5、标记位
在tcp双方通信过程中,客户端和服务器一定会受到各种各样的tcp报文(新建报文、断开报文、发送数据报文、数据异常报文…)所以tcp报文一定要有类型,所以才用了各种标记位!!
对于Syn、ACK、FIN使用场景如下:
URG标记位、16位紧急指针:
Tcp协议为了保证可靠性,收到的数据应按序到达,也就是一个队列。但也存在一些紧急数据,需要尽快被上次处理!提前处理,也就意味着插队。Tcp不允许大量数据插队,但允许有少量数据插队,所以引入URG标记位、16位紧急指针。(16位紧急指针表示紧急数据在有效载荷中的偏移量)
当接收方接收到一个报文,该报文中的URG标志位为1,此时意味着当前报文携带紧急任务数据。所以Tcp发现URG为1后,直接通过16位紧急指针就可以拿到紧急数据,然后交给上层了。(紧急数据只有1byte,然后类似错误码一样,不同值代表不同含义)
recv函数flags标记位为0,表示接收常规数据。当flag设置为MSG_OOB时,此时表示接收紧急数据。OOB表示带外数据,即常规数据之外的数据!
URG标志位使用场景:
- 第一种场景:有时上传数据错了,我们希望暂停或终止上传的行为时。但我们不希望直接通过退出断开连接来实现,此时就向对端推送紧急数据,让上层尽快暂停或终止上传。
- 第二种场景:现在又一个服务器I/O负载比较重,需要经常性对服务器进行健康监测和管理!服务器响应很慢,甚至卡住了。对于发送方而言,非常担心服务器是不是块扛不住了。此时我们可以向服务端发送紧急数据,此时服务端会优先处理并应答。此时发送方就可以得知服务器运行状况!(可以通过创建线程发送紧急数据,此时服务端也存在响应的接收方案)
PSH标记位:
在接收方处理tcp报文时,上层http可能非常耗时,此时接收方很卡,很慢。但我们发送紧急指针后得知对端状态很好,只是数据处理比较慢。此时我们可以方式一个简单报文,并将PSH标记位置1。(该行为表示告诉对端尽快把数据进行向上交付)
- 最典型的场景就是xshell。我们输入的所有指针都携带PSH标记位,让服务器尽快处理,给使用者带来更好的体验!
RST标记位:
我们已经知道,TCP在通信前需要先建立连接,3次握手成功!但最后一次ACK是否成功,我们无法得知(没有确认应答)如果此时该应答恰好丢失,此时服务端认为连接建立尚未完成。但客户端发送完ACK后认为连接已经建立成功。
所以一旦客户端向服务器发送消息时,会导致双方连接建立认知不一致问题!此时服务端会发送一个简单报文,并将RST标记位置1。此时客户端收到之后就知道连接建立失败了,会将历史建立的连接释放,然后重新建立连接!!(其实客户端就是在赌最后一个报文对方接收到了,但很不幸赌失败了!!)
四、面向字节流解释
tcp通信是全双工的,并且存在接收缓冲区和发送缓冲区!接收缓冲区就是一块简单内存空间,但我们可以把缓冲区理解为一个大的字符数组!此时数据从发送缓冲区按字节为单位拷到对端接收缓冲区,这就是字节概念
并且接收缓冲区本质就是一个生产者消费者模型。不断有数据从对端拷贝至接收向缓冲区中生产数据,同时上层不断从接收缓冲区消费数据。一端进数据,另一端出数据,并且数据间没有界限,这不就出现了流动的概念!
五、超时重传机制
主机A向主机B发送消息时,由于一些原因,在特定时间内没有收到对应应答,此时主机A认为数据丢失,会进行超时重传!!
1)、触发超时重传的两种具体原因
但主机A没有收到数据,分为以下两者情况:
① 数据丢失:
主机A向主机B发送消息时,由于网络拥塞的原因,报文丢失了,主机B并未受到对应报文。
② 确认应答丢失:
主机A向主机B发送消息时,主机B受到了主机A发送的报文,并进行了应答。但主机A在特定时间段内受到确认应答报文。此时主机A认为向B发送数据失败,触发超时重传机制,重新向主机B发送报文!
但此时主机B已经收到过历史报文。此时主机B会根据32位序号进行去重!也就意味着主机收到报文应答后,不能被立即丢弃,需要被暂时保存起来(滑动窗口维持:暂时保存在发送缓冲区的滑动窗口区域,收到应答后,通过窗口移动删除指定报文!)
2)、超时时间
数据发送给对端后,特定时间内没有收到会触发超时重传。但网络状态是浮动的,超时时间设置为多少?超时时间太长, 会影响整体的重传效率;时间太短, 有可能会频繁发送重复的包。
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍!!如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传。如果仍然得不到应答, 等待 4500ms 进行重传. 依次类推, 以指数形式递增.累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接!!
六、连接管理机制
1)链接建立和断开时,双方链接状态变化
在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接。
服务端状态转化:
[CLOSED -> LISTEN] :服务器端调用listen后进入LISTEN状态, 等待客户端连接。
[LISTEN -> SYN_RECV]:服务器接收到客户端发送的SYN报文后,发送SYN+ACK完成。
2)经典面试题1:为什么需要进行3次握手,4次挥手
握手3次:
首先握手1次或2次就将链接建立成功,存在明显bug。会导致一些单机程序,恶意向服务器挂靠链接。而创建链接是有成本的(时间成本:初始化,空间成本:申请内存)。此时会导致服务器存在大量链接,可能大量消耗服务器资源,甚至宕机!
而链接一般都是客户端发起,服务端被动响应。3次或奇数次握手,一定是客户端先将链接建立成功,然后服务端才链接连接。也就意味着,一旦服务器连接建立成功,客户端也需要付出等量的成本,可以一定程度上减少SYN洪水问题!
并且3次握手,能够以最小成本验证全双工通道!
最后一点就是3次握手本质是4次握手。其中SYN+ACK本质是一次捎带应答。如果将其拆开,不管是客户端还是服务器,都以最小成本明确将想要建立连接的意愿传递给对方。既然双方都知道对端想要建立来连接了,后序动作也就每必要!
4次挥手:
4次挥手同样是以最小成本将双发想要断开连接的意愿传递给对方。
那为啥不是3次挥手呢?
首先客户端向服务器断开连接,其实更多的是向服务器表达:“我要给你发送的数据全部发送完毕”。客户端数据发完了,但服务器大概率需要对收到的数据进行处理,存在后续动作。当所有数据全部处理完成后,才和客户端真正断开所有连接!所以在业务上,两者时间一般不会重合!
3)经典面试题2:请你说说 TCP 三次握手四次挥手过程
TCP 三次握手过程:
- 调用socket, 创建文件描述符;
- 调用connect, 向服务器发起连接请求;
- connect会发出SYN段并阻塞等待服务器应答; (第一次)
- 服务器收到客户端的SYN, 会应答一个SYN-ACK段表示"同意建立连接"; (第二次)
- 客户端收到SYN-ACK后会从connect()返回, 同时应答一个ACK段; (第三次)
初始时,客户端和服务器端都处于CLOSED状态,并且通常是由客户端发起连接建立请求,服务器则被动响应。在三次握手前,服务器就已经进入监听状态,等待客户端发起连接建立请求。
第一次握手:客户端发起SYN报文(即SYN标记位置1),请求与服务器建立连接。同时随机生成一个起始序列号x,并将其填充到SYN报文的序列号字段中,随后进入SYN_SEND状态,等待服务器的响应。
第二次握手:服务器收到客户端SYN报文后,如果同意建立连接,会发送ACK/SYN报文(即ACK、SYN标记位置1),并将确认序号字段被设置为x+1,同时随机生成一个数y填充到序列号字段中,完成后进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN/ACK报文后,验证确认序号x+1,确认这是对自己SYN报文的应答,并知道服务器同意建立连接。客户端向服务器发送一个ACK报文,作为对服务器SYN/ACK报文的确认。在ACK报文中, 确认序号字段被设置为y+1,
TCP四次挥手过程:
- 如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段(第一次);
- 此时服务器收到FIN后, 会回应一个ACK, 同时read会返回0 (第二次);
- read返回之后, 服务器就知道客户端关闭了连接, 也调用close关闭连接, 这个时候服务器会向客户端发送一个FIN; (第三次)
- 客户端收到FIN, 再返回一个ACK给服务器; (第四次)
首先第一次挥手:客户端向服务器发送FIN报文,请求断开连接,进入FIN_WAIT_1状态。
第二次挥手:服务器收到连接释放请求,然后发送ACK报文进行确认应答,进入CLOSE-WAIT状态。
第三次挥手:当服务器端数据处理并发送完毕,向客户端发送连接释放请求,进入LAST-ACK状态。
第四次挥手:客户端收到连接释放请求,向服务器端发送确认应答报文,此时客户端进入TIME_WAIT状态,持续2倍的MSL(最长报文段寿命),若期间没有收到服务器端的数据报文,进入CLOSED状态
4)TIME_WAIT状态
在tcp4次挥手完成后,发起连接断开请求的一方会进入TIME_WAIT状态,等待2MSL时间后才进入CLOSE状态。MSL是TCP报文的最大生存时间,是一个经验性数字。不同OS存在差异,在ubuntu下为60S.
主动方进入TIME_WAIT状态,并将时间设为2MSL,主要目的是让两个朝向上历史报文再网络中消失,避免对新连接的影响。同时在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN!
5)地址复用,bind()失败问题
现在服务器主动断开连接后,立即重启,我们会发现bind失败,重启失败。
这主要TIME_WAIT有关。服务器主动断开连接,进入TIME_WAIT状态。虽然上层进程已经退出,但连接并未真正释放,由双方操作系统维护。port和ip依旧被使用,当然bind失败啦!
但此时套接字已经没有进程在使用了,已经处于回收站状态。所以我们可以通过setsockopt()接口进行地址复用!
6)、服务器变慢可能原因
对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成。此时OS中维护大量无意义连接,可能导致服务器响应变慢!
其他可能原因
七、滑动窗口
1)什么是滑动窗口
滑动窗口就是发送缓冲区的一个区域,由两个下标进行维护,是TCP进行流量控制的具体解决方案。滑动窗口将缓冲区分为3块,滑动窗口中保存的是可以直接发送或已经发送尚未收到确认的报文,左侧区域是已经发送并且受到应答的报文,而右侧区域是没有发送的报文或者没有数据。窗机滑动本质就是下标移动,并且在整个过程中,滑动窗口一直在变化,但只会向前移动!
滑动窗口的大小由对方接收能力决定,双发接收能力在3次握手过程就已经协商好了,告知对方。并且窗口越大, 则网络的吞吐率就越高
2)如何理解滑动窗口
滑动窗口是由两个下标维护,那这两个下标如何更新?
首先在3次握手时,双方就已经将起始序号和缓冲区大小告知对方了。所以起始时
win_start = 起始序号;win_end = win_start + min(对方缓冲区大小, 拥塞窗口大小)
。( 拥塞窗口后续介绍)
所以在[win_start, win_end]间的报文就可以发送了,或者等到连接。一旦受到报文的确认后,win_start
便可以向有移动,将受到应答的报文从滑动窗口中删除!所以滑动窗口一直在变化,变大变小,甚至为0。并不断向右移动!
3)、快重传机制
位于滑动窗口中的报文都是可以发送或已经发送尚未受到应答的报文。而tcp在实际过程中,也是一次发送多个报文。但发送的报文可能丢失。
①:如果ACK丢失,由于确认序号定义是确认序号之前的报文已经全部被接收了。所以tcp允许存在少量报文丢失,后续应答会对该报文进行确认!
②:如果是报文数据包直接丢失,此时后续所有报文确认应答会全部一样。此时客户端收到发送端主机连续三次相同确认序号时,就知道确认序号的后一个报文丢失了。此时客户端会触发快重传机制,补发丢失报文!!这种机制被称为 "高速重发控制“(”快重传机制“)
4)、其他问题
报文丢失怎么办?
报文丢失分为:最左侧丢失、中间丢失和对右侧丢失。
首先最左侧报文丢失分为:数据包丢失和ACK丢失。对于数据包丢失,可以通过块重传机制进行部分;对于ACK丢失,后序报文确认序号会对该报文进行确认。
而中间丢失和对右侧丢失,我们可以等丢失报文左侧所有报文全应答后,滑动窗口左边界向右移动,最终转化为最左侧报文丢失问题!
超时重传 VS 快重传
首先快重传是有条件的,只有连续收到3个以上的相同序号的报文时,才会触发。快重传是提高效率的重传策略;而超时重传是兜底的。两者相互配合,相互补充!
为啥发送报文时只发送一个报文,而是分为多个发送
八、流量控制
流量控制是tcp通过滑动窗口来控制发送端数据发送速度的一种机制, 其主要目的是防止发送端发送数据过快,导致接收端缓冲区溢出,从而引发丢包、重传等一系列连锁反应。
接收端每次应答时,会将自身缓冲区的大小填充到报文的16位窗口大小中,从而将自身的接收能力告知发送端。此时发送端根据窗口的大小来控制发送数据的速度。如果窗口大,此时网络的吞吐量就越大;如果窗口较小,此时发送数据的速度就变慢。如果窗口为0,此时发送端会停止发送数据。并且发送端会定期询问接收端窗口大小,或者接收端接收缓冲区有空间了会主动发送报文告知发送端,以及时更新窗口大小!
九、拥塞控制
网络状态是浮动的,在不清楚网络状态的情况下,贸然发送大量的数据可能会导致大量丢包。
TCP的拥塞控制主要是调节拥塞窗口来影响滑动窗口大小,因为滑动窗口大小=min(拥塞窗口, 对方接收缓冲区大小)。网络状态是浮动的,可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的。所以tcp引入慢启动机制。
发送开始的时候, 定义拥塞窗口大小为1。每次收到一个ACK应答, 拥塞窗口加1;
每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗
口
十、延迟应答、捎带应答
延迟应答:
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小。但如果每次ACK前,我们先等一段时间,此时上层就有可能将数据取走,从而更新一个更大的窗口大小。此时发送端就可以更新更大的滑动窗口,并发发送更多的数据,从而提高效率!
延迟应答机制有两种:数量限制: 每隔N个包就应答一次;时间限制: 超过最大延迟时间就应答一次。具体的数量和超时时间, 依操作系统不同也有差异; 一般超时时间取200ms。
捎带应答:
在延迟应答的基础上,客户端和服务器一般是一收一发。但如果服务端对客户端报文进行应答时,也想进行数据发送。此时服务端只需将数据报文的ACK标记位置1,即两个报文合并为一个进行发送。我们将其称为捎带应答。
十一、粘包问题
由于tcp面向字节流,当上层通过套接字tcp读取报文时,多读或少读一个完整报文时,并且上层还不做任何处理。此时该报文不完整或超量读取,我们将这种问题称为数据粘包问题!!
虽然站在传输层角度,报文时一个一个过来的,然后按序号将数据放到缓冲区中。但站在应用层角度,如果用户不做任何处理,看到的只是一串连续的字节数据,并不清楚数据间的界限。此时读取数据时,就可能会产生数据粘包问题。
要解决数据粘包问题,只需要明确报文间的界限即可。我们可以在报头前添加描述自身报文长度的字段,并将该字段和实际报文间通过换行隔开。此时接收方上层收到数据后,就可以先读取到换行符提取报文长度。然后按指定长度即可将完整报文提取出来!!(通过一些特殊字符来标识报文间边界、定长报文、报头 + 自描述字段)
- 自定义协议,只需要解决序列化和反序列化问题,以及数据粘包问题才能成功!
十二、TCP异常情况
- 进程终止: Tcp连接本质就是文件。进程异常终止或退出,被打开的连接文件会由双方操作系统自动关闭!
- 机器重启:和进程终止情况相同。当我们机器重启时,电脑本身会询问我们是否关闭。如果确定,此时OS会先关闭所有线程后才重启!
- 机器断电/网线断开:首先tcp内部存在保活机制,假设服务器会向对方发送询问报文,如果都没响应,此时服务器会将连接断开!但tcp内部保活机制间隔时间一般几分钟或几小时,所以保活机制在应用层实现——心跳机制。 所以机器断电后有保活机制维护;但如果机器立马上线,没有触发保活机制。此时服务端向客户端发送消息后,客户端以及将该连接断开,此时客户端会返回RST报文,重新建立连接。 如果是客户端向服务器发送消息,浏览器一般都会自动识别到我们掉线了。重新向对端发送数据时,会新建连接。而对端会通过保活机制将老链接断开!
十三、面试题:请你说说 TCP 如何实现可靠传输
可靠传输就是通过TCP连接传送的数据是没有差错、不会丢失、不重复并且按序到达的。TCP是通过序列号、检验和、确认应答信号、重发机制、连接管理、窗口控制、流量控制、拥塞控制一起保证TCP传输的可靠性的
校验和:目的是检测数据在传输过程中的任何变化。
序列号:保证数据按序到达。
确认应答:确保历史最近一条消息被对方收到。
超时重发:超时数据可能丢失,进行补发。
连接管理:确报全双工通道畅通。
流量控制:根据实际网络和对端情况,调整报发送速度。在保证速度前提下,防止数据大量丢包,和网络拥塞问题。通过滑动窗口实现。
拥塞控制:根据网络情况,当网络拥塞时,减少数据的发送。
提高性能:滑动窗口、快速重传、延迟应答、捎带应答
定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器
十四、TCP/UDP对比
- TCP面向字节流、有连接、可靠通信协议。主要用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景
- UDP面向数据报,无连接、不可靠通信协议。UDP的优点是快,节省了大量的网络资源;缺点是不可靠不稳定,网络不好的时候很容易造成数据丢失。主要用于对高速传输和实时性要求较高的通信领域,广泛应用于直播领域。
十五、用UDP实现可靠传输
在应用层将UDP进行封装,比如提供确认序列号,保证数据拼接时候的正确排序;提供超时重传机制,能避免数据报丢失的问题。将TCP传输层功能,如确认机制、重传功能、流量控制、拥塞控制等功能实现在了应用层。