【网络面试(5)】收发数据及断开服务器(四次挥手)

 前面了解到服务器和客户端在创建套接字,建立连接后,就可以进入到下一步,双发可以互相发送和接收数据,本篇博客就来学习一下这个过程。
 我们印象里,发送数据应该是我们在浏览器输入网址,敲击回车的一瞬间,发送动作就完成了,回头服务器处理完成将数据发送客户端,浏览器解析出来,这就是反过来接收的过程。

1. 发送数据

 由浅入深,了解这个大体过程,我们先来看看发送数据的简单过程。对于浏览器,他没有办法直接向网络中发送数据,而是要将http请求委托给协议栈(操作系统的网络控制软件)来发送。但实际上,在计算机中,并不是只有浏览器会发送网络请求,QQ、微信等很多应用程序都会执行这个动作。所以协议栈工作就是会接收各种应用程序发送过来的网络请求数据,其实就是一堆的二进制字节数据。

 协议栈在拿到数据后,是不是会直接发送到网络中的呢?必然不是,他在内部会维护一段内存缓冲区,等待下一段数据,然后在某个合适的时机再发送出去。这块内存就是发送数据的专用缓冲区。当然,接收数据的时候也是有一块专用内存的,后面我们再说这个。这里还提到了,合适的时机发送数据,这个时机是根据两个要素来判断的,我们看看是哪两个。

1.1 网络包长度

 第一个因素是跟网络包长度相关的,什么意思呢,对于某些GET请求,要发送的请求内容必然很少,一个网络包就能放得下,但是有些POST请求,比如我要写的这篇博客,经过编码解析,需要很多歌网络包才能放的下,这里就涉及到拆包的概念。

 这里先了解两个网络词汇:MTUMSS

  • MTU: 指的是一个网络包的最大长度,以太网中通常是1500个字节。
  • MSS: MTU中去掉头部之后,所能容纳的数据的最大长度。

在这里插入图片描述

 了解这两个概念,我们在来看下上面说的拆包的概念,即我们发送的某次网络请求,可能是通过1个网络包发送给服务器的,也可能是很多个,决定因素就是MTU和MSS。

 在应用程序将数据发送给协议栈的时候,数据可大可小,协议栈无法决定,如果每次接收到应用程序的一次数据就立即发送出去,必然会导致发送大量小的网络包,网络效率下降。所以,协议栈一般会累积到数据量可以塞满一个网络包的时候再发送出去,即MTU的长度,这就是第一个决定协议栈发送数据的因素。

1.2 发送时间

 决定协议栈 发送数据的第二个因素是时间,为啥呢?我们可以试想一下,如果GET请求的数据长度无法达到一个MTU的长度,协议栈一直等待到一个网络包的数据长度再发出去,必然会产生很大的延迟,给我们卡顿的感觉。所以,某些情况下,即便网络包没有被填满,也会立即把数据发送出去。
 协议栈内部会维护一个计时器,在超过设定的时间阈值后,即便没有达到一个完整网络包数据长度,也会立即发送。一般,这个时间是由协议栈的开发者决定的,不同操作系统的不同版本会有不同实现。

 其实,决定协议栈发送数据的这两个要素,在某些情况下是比较矛盾的,立即发送会导致网络效率下降,等待太久又会造成延迟。过分依靠协议栈来决定发送时机会带来一些问题,所以协议栈也给了应用程序一个选项,来决定是否立即发送。像浏览器这样的会话型应用程序,一般会选择“立即发送”的选项。

2. 确认发送成功以及重发功能的实现

 TCP协议的非常重要的功能就是可以确认通信的一方是否已经成功收到了网络包,如果没有收到,必须具有重发的功能。这个功能的实现就是借助于ACK号和seq序号要进行对方接收确认的操作。

 上文我们说过,在网络请求内容过大的时候,TCP会有拆包的逻辑,那么在拆分的过程中,TCP就会计算好并记录每个网络小包在整个请求内容中处于第几个字节,然后再发送网络包的时候,在TCP头部记录这个字节数(就是seq序号,比如目前是第1个字节),服务器在接收到网络包的时候,会读取这个字节,然后再计算这个网络包MSS的长度(比如网络包数据长度是1000),在确认回复的时候,会将ACK赋值为ACK = 1 + 1000 并返回给客户端。客户端在接收到ACK号的时候就可以确定网络包已经顺利被对方接收,否则就会重试发送。

 我们可以想象一下,客户端在发送下一个网络包的时候,一定是从第1001个字节开始的,于是服务器在收到请求后,可以顺便验证1001是不是和自己最后一次ACK响应的字节数相等,如果相等说明中间没有丢包,如果是2001,说明中间丢失了至少一个网络包。

 这里我们已经提到了ACK和seq,TCP协议可以通过ACK号和序号就可以确认对方是否收到了网络包。我们来看一个虚拟的例子加深一下了解。

在这里插入图片描述

2.1 调整ACK号等待时间

 我们的网络传输并不是一帆风顺的,发生拥塞和抖动的情况是非常常见的。前文我们提到TCP会通过ACK号确认对方已经接收到网络包,但是在网络比较慢的情况下,发送和接收ACK号的平均响应时间就会比较长了,如果客户端在这个时候设置了比较短的等待时间,就会在没收到ACK的情况一直向以太网中发送数据,这对于本来已经繁忙的网络就更加糟糕了,这其实就是TCP的网络包重传。

 通常,当网络包重传发生后,有可能前一个相同网络包的ACK号才返回,这样的重传其实是不必要的。所以,对于等待时间来说,需要设置一个合适的值,这个时间应该是可以动态调整的,而计算方法就是根据过往发送数据的过程中,持续监测ACK号的响应时间,如果ACK号的返回时间变慢,就会响应延长这个等待时间,否则就缩短等待时间。

 除此之外,TCP还是使用了滑动窗口的方式来管理数据发送和ACK号的管理,大体思路就是第一个网络包在发送出去之后,并不是等待当前网络包的ACK号返回才发送下一个,而是直接发送下一个,或者说是下面一系列的网络包,这样的话,发送的等待时间就会被有效的利用起来了。这个过程相对复杂一些,涉及到窗口大小的概念,这个窗口大小就是指接收方网络协议栈中,在当前时间里,剩余的最大缓存空间,也就是能接收的字节数。下图中可以看出来一来一回和滑动窗口的方式,这里不再深入展开,可以查看相关资料。

在这里插入图片描述

3. 接收数据

 在客户端发送完数据的过程后,服务器端就可以接收并处理网络包了,对于单个网络包的处理比较简单,对于客户端拆分后分多次发送的网络包,服务器的TCP协议同样会以相同的方式拼接起来转换成为对应的网络请求,其实就是和客户端处理相反的方式进行的。服务器在处理请求后,就会将相应数据发送给客户端。

 我们可以想一下,客户端的浏览器程序在委托协议栈发送了网络请求后,就处于等待响应结果的状态。这个状态其实是浏览器调用了Socket组件库的read()函数,协议栈会将这个工作挂起,直到服务器数据相应之后,协议栈写入到接收缓冲区中,在这个过程之前,接收缓冲区一直是空的,浏览器就无法处理数据,这个挂起就是我们常说的阻塞过程。这个如果继续延伸的话,会有阻塞式IO,非阻塞式IO,IO多路复用等知识点,在此不深入。

 总结一下这个过程,客户端的协议栈会检查接收到的数据和TCP头部的内容,判断是否有数据丢失,如果没有问题会向服务器返回ACK号。然后协议栈将接收到的数据暂存到接收缓冲区(这个缓冲区是协议栈的)中,然后将数据块按照顺序连接起来还原成原始的数据,最后将数据交还给应用程序,其实是把协议栈缓冲区中的数据复制到浏览器制定的内存地址中,然后浏览器去解析的过程(这个过程还是在read里面实现并把控制流程交还给浏览器的)。

4. 断开连接

 接下来最后一个流程,就是数据发送完成之后的断开连接了,那么断开连接这个操作是由客户端还是服务端发起的呢?
 在协议栈中并没有规定哪一方应该先发起断开操作,通常是由应用程序判断自己的数据已经发起后就可以发起断开动作了。比如我们访问web服务器,发送请求,服务器接受请求处理完成会向客户端返回数据包,等到所有数据都返回了,服务器会主动发起断开操作。下面,我们就以这个例子,服务器先发起断开操作理解这个过程。

 所谓的断开操作也是由发起方调用Socket库的中close()程序实现的,在这个方法中,协议栈会生成包含了断开控制信息的TCP头部,具体来说就是将FIN比特位设置1,然后再委托IP模块将数据发给客户端,接下来,服务器套接字中就会记录下断开操作的相关信息。

 接下来看我们的客户端,在接收到FIN比特位为1的包时,客户端知道了,噢服务器要断开连接了,那好在自己的套接字中标记一下要进入断开操作了,记住这里只是标记一下,同时必须要返回服务器ACK号,告知服务器已收到FIN=1的断开网络包了。

 然后,待到客户端协议栈接收缓冲区数据被应用程序全部取走之后(前面讲到的应用程序的read()操作),客户端感觉时机成熟了,也会向服务器发送一个包含FIN=1头部的网络包,服务器同理也要返回ACK包,至此,双方的通讯正式结束。

在这里插入图片描述

5. 删除套接字

 接下来断开操作的最后一步就是删除套接字,这里尤其注意用来通讯的套接字不会立即删除,而是会等待一段时间后再删除,具体原因如下:

 我们现在举个跟上面相反的断开的例子,由客户端发起断开请求:

  • 客户端发送FIN=1
  • 服务器返回ACK
  • 服务器发送FIN=1
  • 客户端返回ACK

 这里特别注意最后一步,客户端在返回ACK号之后,如果立即删除套接字会发生什么呢?正常情况,可能是服务器收到客户端的ACK号双方通讯结束没问题。但是如果因为网络拥塞问题,服务器没有在规定时间收到第四步的ACK号,那么服务器又发送了一次FIN=1,这里可能会有问题了,因为客户端已经删除了套接字,此时如果恰巧又其他应用程序请求连接服务器并且创建了相同端口号的套接字,那么这个新创建的套接字因为收到了一条莫名奇妙的FIN=1就要进入断开操作了,就会有问题了。所以客户端并不会立即删除套接字,就是为了防止这个问题发生。
 通常,这个等待删除套接字的时间就是几分钟而已。

6. TCP的整体流程

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/283212.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

L1-077:大笨钟的心情

有网友问:未来还会有更多大笨钟题吗?笨钟回复说:看心情…… 本题就请你替大笨钟写一个程序,根据心情自动输出回答。 输入格式: 输入在一行中给出 24 个 [0, 100] 区间内的整数,依次代表大笨钟在一天 24 小时…

【LLM 】7个基本的NLP模型,为ML应用程序赋能

在上一篇文章中,我们已经解释了什么是NLP及其在现实世界中的应用。在这篇文章中,我们将继续介绍NLP应用程序中使用的一些主要深度学习模型。 BERT 来自变压器的双向编码器表示(BERT)由Jacob Devlin在2018年的论文《BERT:用于语言…

C/C++ 函数重载

函数多态是C在C语言的基础新增的功能。默认参数能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让您能够使用多个同名的函数。术语“多态”指的是有多种形式,因此函数多态允许函数可以有多种形式。类似地,术语“函数重载”指的是…

【时钟】分布式时钟HLC|Logical Time|Vector Clock|True Time

目录 简略 详细 附录 1 分布式系统不能使用NTP的原因 简略 分布式系统中不同于单机系统不能使用NTP(网络时间协议(Network Time Protocol))来获取时间,所以我们需要一个特别的方式来获取分布式系统中的时间,mvcc也是使用time保证读…

2024最全面且有知识深度的web3开发工具、web3学习项目资源平台

在Web3技术迅速发展的时代,寻找一个综合且深入的Web3开发工具和学习项目资源平台变得至关重要。今天,我将向大家介绍一个非常有价值的网站,它就是https://web3x.world 。 Web3X是一个全面而深入的Web3开发者社区,为开发者们提供了…

最优化方法Python计算:无约束优化应用——神经网络回归模型

人类大脑有数百亿个相互连接的神经元(如下图(a)所示),这些神经元通过树突从其他神经元接收信息,在细胞体内综合、并变换信息,通过轴突上的突触向其他神经元传递信息。我们在博文《最优化方法Python计算:无约…

跳跃表原理及实现

一、跳表数据结构 跳表是有序表的一种,其底层是通过链表实现的。链表的特点是插入删除效率高,但是查找节点效率很低,最坏的时间复杂度是O(N),那么跳表就是解决这一痛点而生的。 为了提高查询效率,我们可以给链表加上索…

打破成本壁垒,免费SSL证书为中小企业保驾护航

HTTPS,这个曾经看似遥远的技术词汇,如今已与我们每个人的网络生活息息相关。而实现HTTPS加密传输的关键一环——SSL证书,正以其独特的安全性能,为网站筑起一道坚实的防护墙。更令人惊喜的是,免费SSL证书服务已经到来&a…

数据结构与算法教程,数据结构C语言版教程!(第二部分、线性表详解:数据结构线性表10分钟入门)三

第二部分、线性表详解:数据结构线性表10分钟入门 线性表,数据结构中最简单的一种存储结构,专门用于存储逻辑关系为"一对一"的数据。 线性表,基于数据在实际物理空间中的存储状态,又可细分为顺序表&#xff…

自动化网络故障修复管理

什么是故障管理 故障管理是网络管理的组成部分,涉及检测、隔离和解决问题。如果实施得当,网络故障管理可以使连接、应用程序和服务保持在最佳水平,提供容错能力并最大限度地减少停机时间。专门为此目的设计的平台或工具称为故障管理系统。 …

JavaScript setTimeout和setInterval的用法与区别详解

目录 I. 总述 II. setTimeout()函数 III. setInterval()函数 IV. 新年倒计时案例 Javascript的setTimeOut和setInterval函数应用非常广泛,它们都用来处理延时和定时任务,下面这篇文章主要给大家介绍了关于JavaScript setTimeout和setInterval的用法与…

解决 Nginx 反向代理中的 DNS 解析问题:从挑战到突破20231228

引言 在使用 Nginx 作为反向代理服务器时,我们可能会遇到各种配置和网络问题。最近,我遇到了一个有趣的挑战:Nginx 在反向代理配置中无法解析特定的域名,导致 502 错误。这个问题的解决过程不仅揭示了 Nginx 的一个不太为人知的功…

分布式【雪花算法】

雪花算法 背景:在分布式系统中,需要使用全局唯一ID,期待ID能够按照时间有序生成。 **原理:**雪花算法是 64 位 的二进制,一共包含了四部分: 1位是符号位,也就是最高位,始终是0&am…

MySQL存储过程、创建、调用、查看、删除、存储过程与函数的额区别、缺陷等、存储过程写分页等

MySQL存储过程 1、存储过程的定义2、存储过程使用的意义3、存储过程的创建4、存储过程的调用5、存储过程的查看6、存储过程的删除7、存储及过程与函数的区别8、存储过程的缺陷9、存储过程写分页 1、存储过程的定义 存储过程:存储过程(Stored Procedure&…

redis 从0到1完整学习 (十二):RedisObject 之 List 类型

文章目录 1. 引言2. redis 源码下载3. redisObject 管理 List 类型的数据结构3.1 redisObject 管理 List 类型3.2 List PUSH 源码 4. 参考 1. 引言 前情提要: 《redis 从0到1完整学习 (一):安装&初识 redis》 《redis 从0到1…

pytest --collectonly 收集测试案例

pytest --collectonly 是一条命令行指令,用于在运行 pytest 测试时仅收集测试项而不执行它们。它会显示出所有可用的测试项列表,包括测试模块、测试类和测试函数,但不会执行任何实际的测试代码。 这个命令对于查看项目中的测试结构和确保所有…

千里马2023年终总结-android framework实战

背景: hi粉丝朋友们: 2023年马上就过去了,很多学员朋友也都希望马哥这边写个年终总结,因为这几个月时间都忙于新课程halsystracesurfaceflinger专题的开发,差点都忘记了这个事情了,今天特别花时间来写个bl…

思维链COT原理探究

要进行因果分析,需要把思维链中的不同元素拆解开来,然后通过控制变量实验,来研究不同元素对COT效果的影响。以下两篇论文的核心差异就在于: COT的变量拆解,以及控制变量的实验方式。 结合两篇论文的实验结论,可能导致…

【深度学习:Convolutional Neural Networks】卷积神经网络入门指南

卷积神经网络(CNN)是深度学习领域最引人注目的成就之一。自从LeCun等人在20世纪90年代初引入以来,CNN在图像处理、视频分析和自然语言处理等领域取得了显著的成就。在这篇博客中,我们将探讨CNN的基本原理、结构和一些实际应用案例…

实验3 vTPM相关

一、实验目的 1.了解vTPM原理和相关知识;2.创建具备vTPM的虚拟机;3.加深对可信计算技术的理解。 二、实验内容 安装seabios,libtpms,swtpm,qemu‐tpm;启动vTPM;安装虚拟机。 三、实验环境 …