草庐IT

“三次握手,四次挥手“作为当代文明青年怎么能不会【计网】

爪哇贡尘拾Miraitow 2023-05-18 原文

👨‍🎓博主主页爪哇贡尘拾Miraitow
📆传作时间:🌴2022年1月4日🌴
📒内容介绍:最近在学习计算机网络所以会时不时更新有关内容
📚参考资料:王道考研计算机网络 度娘
🔗参考链接:👉TCP报文段的首部格式
⏳简言以励:列位看官,且将新火试新茶,诗酒趁年华
📝内容较多有问题希望能够不吝赐教🙏
🎃 欢迎点赞 👍 收藏 ⭐留言 📝

📌我是目录📌

TCP的三次握手和四次挥手,可以说是老生常谈的经典问题了,通常也作为各大公司常见的面试考题,我觉得想要清楚理解,还是要从TCP报文段首部格式说起

TCP的介绍

传输控制协议(TCP,Transmission Control Protocol)是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。

TCP报文段首部格式

首部固定部分各字段意义如下:

源端口和目的端口 : 各占2个字节,分别写入源端口和目的端口。


序号 :占4字节。序号范围是【0,2^32 - 1】,共2^32(即4294967296)个序号。在TCP连接中传送的字节流中的每一个字节都按顺序编号,本字段表示本报文段所发送数据的第一个字节的序号
例如,一报文段的序号是201,而接待的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是201,最后一个字节的序号是300。显然,下一个报文段(如果还有的话)的数据序号应当从301开始,即下一个报文段的序号字段值应为401。这个字段的序号也叫“报文段序号”。


确认号 :占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。
例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。注意,现在确认号不是501,也不是700,而是701。
简而言之:若确认号为= N,则表明:到序号N-1为止的所有数据都已正确收到。


数据偏移 : 占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的,但应注意,“数据偏移”的单位是32位字(即以4字节的字为计算单位)。由于4位二进制数能表示的最大十进制数字是15(最大的四位二进制1111),因此数据偏移的最大值是60字节,这也是TCP首部的最大字节(即选项长度不能超过40字节因为有20字节的固定首部)。


紧急URG: 当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。
例如,我们发送文件好好的,突然接收方说我没办法接受文件了,快停止,发送方收到信息就会赶快停止,发送方叫停需要发送命令,就会进入TCP缓存中,因为我们的URG设置为1,就需要尽快发送,说白了就是允许插队,不用去排队

当URG置为1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。


确认ACK(ACKnowledgment): 仅当ACK = 1时确认号字段才有效,当ACK = 0时确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。


推送 PSH(PuSH) :当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。


复位RST(ReSeT) :当RST=1时,表名TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。


同步SYN(SYNchronization) : 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1,因此SYN置为1就表示这是一个连接请求或连接接受报文。(后面三次握手的时候具体来看)


终止FIN(FINis):用来释放一个连接。当FIN=1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接。(后面四次挥手的时候具体来看)


窗口 :占2字节。窗口值是【0,2^16-1】之间的整数。窗口指的是发送本报文段的一方的接受窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。

例如,发送了一个报文段,其确认号是701,窗口字段是1000.这就是告诉对方:“从701算起,我(即发送方报文段的一方)的接收缓存空间还可接受1000个字节数据(字节序号是701~1700),你在给我发数据时,必须考虑到这一点。”

简而言之窗口字段明确指出了现在允许对方发送的数据量。窗口值经常在动态变化。


检验和 :占2字节。检验首部+数据,检验时要加上12B伪首部,第四个字段为6


紧急指针 :占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据) 。因此,在紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0时也可以发送紧急数据


选项 (长度可变)最大报文段MSS,窗口扩大,时间戳,选择确认;


TCP连接管理

TCP连接传输三个阶段


TCP连接的建立采用客户服务器方式(c/s),主动发起连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫服务器

TCP的三次握手

TCP的三次握手


刚开始客户端和客户端都处于关闭的CLOSED状态,先是服务端主动监听某个端口,处于LISTEN状态

ROUND 1客户端会随机初始化序列号 ISN(c)对应上图的seq=x将序列号置于TCP首部的【序号】字段中。同时把SYN标志位置为1,表示SYN报文,接着把第一个SYN报文发送给服务器端,表示服务器发起连接,之后客户端处于SYN_Send 状态。

注意该报文不包括应用层数据 并且此时的ACK是为0,因为客户端此时没有收到服务器端发出的报文段因为此时客户端不知道期待什么所以确认号是没意义的 ,前面我们也说过了,只有接受请求报文和确认请求报文SYN才为1


ROUND 2:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)对应上图的seq=y,同时会把客户端的 ISN + 1 对应上图的ack=x+1作为 ack的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。

注意该报文不包括应用层数据 ,因为我们说过了SYN两种情况下才为1,分别是连接请求和连接请求的确认,所以我们现在是连接请求的接受SYN就是1,从二次握手以后连接,已经建立好了,就不需要SYN了,接下来的连接SYN都为0,当连接建立以后ACK为1,我们的确认号ack就有效了,之所以ack=x+1,就是因为我们接下来期待的收到的下一个字段为发送端之前发送的X字段+1


ROUND 3:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态
服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。

Ps:
(1)SYN=1表示该报文不携带数据,但消耗一个序号 seq=x,seq=x是客户端的初始化序列号,因为tcp是面向字节流的
(2)SYN=1 表示该报文不携带数据,但消耗一个序号 seq=y,seq=y是服务器的初始化序列号,ACK=1是一个确认号
ack=x+1,表示服务器下次接收到的序号希望是x+1。然后服务器进入到SYN-RCVD等待的状态
(3)ACK=1是一个确认号,seq=x+1是上一次服务器回应的序号要求,ack=y+1表示客户下一次接收到的序号希望是y+1

tcp通信需要确保双方都具有数据收发的能力,得到ACK响应则认为对方具有数据收发的能力,因此双方都要发送SYN确保对方具有通信的能力。第一次握手是客户端发送SYN,服务端接收,服务端得出客户端的发送能力和服务端的接收能力都正常;第二次握手是服务端发送SYN+ACK,客户端接收,客户端得出客户端发送接收能力正常,服务端发送接收能力也都正常,但是此时服务器并不能确认客户端的接收能力是否正常;第三次握手客户端发送ACK,服务器接收,服务端才能得出客户端发送接收能力正常,服务端自己发送接收能力也都正常。


大白话来谈TCP的三次握手

举两个栗子🌰

村里有个贫困的人叫老许,一直单身没有对象,这一天好友老王来给他介绍对象,但是老许还在睡觉


👴 老王:老许!老许!我是老王,你能听到吗?
👨‍🦳 老许猛的惊醒一听是老王的声音:老王!老王!我是老许,我能听到你说话,你能听到我说话嘛!!
👴 老王一听,嗯,这是老许的声音:老许!我能听到,我给你说个事。

“老许你要老婆不要!”
老许连忙穿上衣服,激动的跑出门了。

上面就是简单的三次握手建立连接然后传输数据的过程,是不是很有趣!!


再来个栗子🌰:
我们大家都写过💌情书,我们要怎么知道对方的答案就要经历这些

将小明当作客户端,小红当作服务器端,两人写信告白:
第一次握手
小明写信告诉小红:我喜欢你很久了,可不可以和我在一起。
第二次握手
小红收到信以后写信告诉小明:我知道了,其实我也喜欢你。
此时小红并不确定小明是否收到了告白信(因为我们的表白信在传送的时候可能被那些传信的人,拦截了,你们上学的时候遇到这样的嘛!哈哈),然后就等待
第三次握手
小明打开信很开心回信:我知道了,那我们在一起吧。

此时才真正建立恋爱的关系。
这样你才算和对方完成,男女朋友才经常做的事情,别想太多,就是腻歪腻歪!!!!!


看到这里不知道大家有啥疑惑没有,如果没有我们来看看这个问题

①为什么是三次握手?不是两次,四次?

我们学完了可能就会说,因为三次握手才能保证双方具有接收和发送的能力,emmmm,这样说好像是没毛病,但是比较片面,并没说出主要的原因

主要原因是防止失效的连接请求报文段被服务端接收,从而产生错误。
注意:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。
a. 若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。
此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

采用“三次握手”的办法可以防止上述现象发生 ,例如上述情况,客户端没有向服务器端的确认发出确认,服务器由于收不到确认,就知道客户端并没有要求建立连接。


②三次握手可以携带数据吗?

第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。假设第一次可以携带数据,如果有人恶意攻击服务器,每次都在第一次握手中的SYN报文放入大量数据,重复发送大量SYN报文,此时服务器会花费大量内存空间来缓冲这些报文,服务器就更容易被攻击了


TCP的四次挥手

在介绍“四次挥手之前先看个例子”

举个栗子

还是上面的小明和小红,现在他们两个处于热恋状态


恋爱之后,小明和小红经常打电话煲电话粥。依旧将小明当作客户端,小红当作服务器端。小明跟小红说话,
第一次挥手
小明说:我说完了,也不早了,该睡觉了。
第二次挥手
小红还不想睡还想继续说:好的,我知道了,我还没说完。
小红继续吧啦吧啦,说完情话之后
第三次挥手
小红告诉小明:我说完了。
第四次挥手
小明收到后告诉小红:好的,我知道了,但是小明也不舍得挂电话。等了2MSL之后小明才挂断了。
如果此时小红说完,等了2MSL,小明一直不出声,这个时候就会重新说一次:我说完了。直到收到小明最后的回复,才挂断电话。


那么我们回归TCP的"挥手”


ROUND 1:
客户端打算关闭连接,此时发送一个TCP首部FIN标志位被设置为1的报文,也就是FIN报文,之后客户端进入FIN_WAIT_1状态
ROUND 2:
服务器收到该报文以后,就向客户端发送ACK应答报文,接着服务器进入CLOSE_WAIT状态,这样客户端到服务器这个方向的连接就释放了–半关闭状态,此时客户端器不用给予回复,因为主机已经结束通话了,只需要等到服务器客户端告诉自己他也要结束
ROUND 3:
服务器端发完数据,就会发出连接释放报文段,主动关闭TCP连接,之所以第二次和第三次的ack是一样的,是因为客户端没有发送数据,所以ack期待的下一个报文段不变
ROUND 4:
客户端回送一个确认报文段,在等到时间,再等到时间等待计时器设置的2MSL(最长报文段寿命)后,连接彻底关闭


看到这里不知道大家有啥疑惑没有,如果没有我们来看看这些问题

为什么我们第一次连接已经释放了,可是最后还能回送一个报文段那?

这个问题是我在学习的过程中,弹幕上提到的问题,我也有些疑惑其实,断了连接只是不发送数据而已,但是对服务器还是要回复的,在CLOSED状态之前,都不能算是真正的关闭

为什么客户端发送ACK之后不直接关闭,而要等待一阵子才关闭

这个是面试的高频考点,这其中的原因就是,要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态

------🎃 欢迎点赞 👍 收藏 ⭐留言 📝-----
<-----希望大家假期🐟🎊🎊🎊🎊🎊----->
<------------🍻2022新年快乐🥂----------->
<----------✨2022大家一起加油✨---------->

有关“三次握手,四次挥手“作为当代文明青年怎么能不会【计网】的更多相关文章

  1. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  2. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  3. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  4. ruby - Ruby 中的隐式返回值是怎么回事? - 2

    所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

  5. ruby - 怎么来的(a_method || :other) returns :other only when assigning to a var called a_method? - 2

    给定以下方法:defsome_method:valueend以下语句按我的预期工作:some_method||:other#=>:valuex=some_method||:other#=>:value但是下面语句的行为让我感到困惑:some_method=some_method||:other#=>:other它按预期创建了一个名为some_method的局部变量,随后对some_method的调用返回该局部变量的值。但为什么它分配:other而不是:value呢?我知道这可能不是一件明智的事情,并且可以看出它可能有多么模棱两可,但我认为应该在考虑作业之前评估作业的右侧...我已经在R

  6. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  7. ruby-on-rails - 我该怎么办 :remote location validation with CarrierWave? - 2

    我在我的Rails3示例应用程序上使用CarrierWave。我想验证远程位置上传,因此当用户提交无效URL(空白或非图像)时,我不会收到标准错误异常:CarrierWave::DownloadErrorinImageController#createtryingtodownloadafilewhichisnotservedoverHTTP这是我的模型:classPaintingtrue,:length=>{:minimum=>5,:maximum=>100}validates:image,:presence=>trueend这是我的Controller:classPaintingsC

  8. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  9. ruby-on-rails - 应用程序的名称是否可以作为变量使用? - 2

    当我创建一个Rails应用程序时,控制台:railsnewfoo我的代码可以使用字符串“foo”吗?puts"Yourapp'snameis"+app_name_bar 最佳答案 Rails.application.class将为您提供应用程序的全名(例如YourAppName::Application)。从那里您可以使用Rails.application.class.parent获取模块名称。 关于ruby-on-rails-应用程序的名称是否可以作为变量使用?,我们在StackOve

  10. ruby-on-rails - 使用作为方法的值在 ruby​​ 中搜索哈希 - 2

    我在搜索我的值是方法的散列时遇到问题。我只是不想运行plan_type与键匹配的方法。defmethod(plan_type,plan,user){foo:plan_is_foo(plan,user),bar:plan_is_bar(plan,user),waa:plan_is_waa(plan,user),har:plan_is_har(user)}[plan_type]end目前如果我传入“bar”作为plan_type,所有方法都会运行,我怎么能只运行plan_is_bar方法呢? 最佳答案 这个变体怎么样?defmethod

随机推荐