TCP 特性
确认应答是可靠传输的最核心机制
接收方反馈一个应答报文(ACK),表示已收到
假设现在 A 想去 B 家里玩游戏,于是 A 给 B 发消息,若消息没有出现错误且顺序正确
结果如下所示:

但网络传输比较复杂,可能存在一种情况"后发先至"
由于数据的长度不同或者传输网络不同,先发送的数据不一定先到达,接收方接收到的数据可能是乱序的,如图:

当 B 回复 A 的消息时,若存在对应关系,那么即使出现了"后发先至"的情况,也能顺利的确立应答

上述方法,虽然可以顺利的确立应答,但额外的信息很多,占用的带宽很多
下面如图,针对发送的请求进行编号,应答的时候也针对编号进行应答,这样既能保证数据传输没有歧义,也不会浪费太多的空间和带宽

序号和确定序号,在前面 TCP报文格式中提到过
上述情况不严谨,真实的 TCP 还不一样,TCP 是面向字节流的,此处的编号并不是按照一条两条来编的,而是按照字节来编号的 (每个字节有一个编号)

确认应答是一种特殊的报文(ACK),所谓的应答报文,本质上就是 ACK 字段为1 的报文,此时报头中的"确认序号"字段才是生效的
初始序号是随机的,为了防止网络攻击;如果发送多个数据,每个数据都会带着一个序号
接收方收到数据后,是知道数据所带着的序号的,根据序号给出确认序号(告诉发送方下次给我发的序号),发送给发送方,发送方就知道接收方收到了哪些数据

确认应答是比较理想的情况,但数据在传输过程中,可能是会丢包的
仍以上面例子为例,A 给 B 发消息,你在家嘛?等了很久,A 也没收到 B 的消息,此时,存在以下几种情况:
① B 不想回 A 的消息
② B 没收到 A 的消息 (丢包情况1: 发的请求丢失)
③ B 回复了消息,但 A 没收到 (丢包情况2: 应答的 ACK 丢失)
②③情况:丢包的两种情况,对于发送方来说无法确定是哪种情况,因此,进行统一处理:当发送了一条数据之后,TCP 内部就会自动启动一个定时器,达到一定时间也没收到 ACK,定时器就会自动触发重传消息的动作 —— 超时重传
①情况:
思考:
假设第二次重发没有成功,那么就存在两个超时时间 t1,t2 如图所示:
那么,t1 和 t2 时间一样长吗??

在 TCP 中,t2 会比 t1 更长
TCP 抱着一种 “悲观的态度”,当一次丢包重传之后,TCP 就觉得大概率后面的重传也没用,所以就隔一个更长的时间,节省带宽
上述丢包有两种情况,一种是请求丢失 —— 重传没有问题;一种是 ACK 丢失,重传就意味着接收方收到了相同的数据
TCP 会在内部进行数据去重 (以序号为 key 进行去重),保证应用层读到的数据不是重复数据
确认应答 和 超时重传是 TCP 可靠性中最核心的机制
为什么要就建立连接?
1.更好的保证可靠性: 建立连接的过程其实就是让通信双方验证各自的发送能力和接受能力是否正常
2.协商一些重要参数 (如: 序号的初始值)
具体怎样建立连接?
举例:A 给 B 打电话,打电话同样要验证自己以及对方的话筒和听筒是否正常工作

第一次握手: 刚开始,A 不知道自己和 B 手机的听筒和话筒是否正常,所以 A说"喂,你能听到吗?"
第二次握手: B 听到后,说明 A 的话筒和 B 的听筒正常,但 B 还需进一步检查自己的话筒和 A 的听筒是否正常;同时 B 把 A 话筒正常和自己听筒正常的消息传递给 A;于是 B “我能听到,你呢?”
第三次握手: A 收到 B 的消息后,就证明了 A 听筒正常,B 话筒正常
以上三次握手就保证了 A、B 的听筒和话筒都正常,也就保证了通话的正常,这就类似于网络建立连接时的三次握手
TCP 中真实的建立连接过程: (假设主机 A 主动发起连接)

第一次握手: 客户端向服务器发送 SYN 报文 (SEQ=x,SYN=1),并进入 SYN_SENT 状态,等待服务器确认
第二次握手: 实际上是分两部分来完成的,即 SYN+ACK (请求和确认) 报文
服务器收到了客户端的请求,向客户端回复一个确认信息 (ack=x+1)
服务器再向客户端发送一个 SYN 包 (SEQ=y)建立连接的请求,此时服务器进入 SYN_RECV 状态
第三次握手: 客户端收到服务器的回复 (SYN+ACK 报文0);此时,客户端也要向服务器发送确认包 (ACK);此包发送完毕客户端和服务器进入 ESTABLISHED 状态,完成 3 次握手
建立连接的过程,相当于通信双方各自给对方发送 SYN,在各自给对方发送给 ACK,只不过中间的 ACK 和 SYN 合二为一了,于是最后就是"三次握手"

几个重要的状态:
1.两次握手可以吗??
不可以
- 防止已失效的请求报文又传送到了服务端,建立了多余的链接,浪费资源
- 两次握手只能保证单向连接是通畅的 (为了实现可靠数据传输, TCP 协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的;三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤;如果只是两次握手,至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认)
.
2.为什么是三次??
主要是为了建立可靠的通信通道,保证客户端与服务端同时具备发送、接收数据的能力
.
3.四次握手可以吗??
可以,但没必要
四次握手可以验证双方的发送接收能力正常,但是这样做效率比较低
.
三次握手: 双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 SYN 和 ACK 能合并在一起
四次挥手: 双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 FIN 和 ACK 不一定能合并在一起
仍以打电话为例,如下图:

TCP 中真实的断开连接过程: (假设主机 A 主动断开连接)


两个重要的状态:
1.四次挥手,三次挥完行不行??
通常情况下不行,若触发了延时应答机制,就可以三次挥完
"不行",即:上述的 ② ③ 为什么没有合并在一起??
因为中间两次操作的时机不一样
ACK 是收到 FIN 之后立刻由内核返回的数据报,FIN 是应用程序处理完接受缓冲区的数据之后,调用的 close 方法触发的
.
2.为什么四次??
因为要确保客户端和服务端的数据能够完成传输
.
3.为什么 TIME_WAIT 状态要等待 2MSL??
假设网络上传输数据的最大时间为 MSL
MSL 就是 ACK / FIN 从主机 A 到主机 B 的最大时间
TIME-WAIT 等待时间,需要分成两个部分:
①等待 ACK 经历一个最大时间到达主机 B
②万一 ACK 丢了,在等待一个最大时间,主机 B 重传 FIN 到达主机 A
因此,TIME_WAIT 就需要等待 2倍的MSL,即:2MSL
原因:
- 确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接
第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端;服务端会超时重传 FIN / ACK 报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN / ACK 报文的确认,就无法正常断开连接
MSL 是报文段在网络上存活的最长时间,客户端等待 2MSL 时间,即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」,就能够收到服务端重传的 FIN / ACK 报文,然后客户端重传一次 ACK 报文,并重新启动 2MSL 计时器;如此保证服务端能够正常关闭
如果服务端重发的 FIN 没有成功地在 2MSL 时间里传给客户端,服务端则会继续超时重试直到断开连接- 防止已失效的连接请求报文段出现在之后的连接中
TCP 要求在 2MSL 内不使用相同的序列号;客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失;这样就可以使下一个连接中不会出现这种旧的连接请求报文段;或者即使收到这些过时的报文,也可以不处理它

为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
我的Gallery模型中有以下查询:media_items.includes(:photo,:video).rank(:position_in_gallery)我的图库模型有_许多媒体项,每个都有一个照片或视频关联。到目前为止,一切正常。它返回所有media_items包括它们的photo或video关联,由media_item的position_in_gallery属性排序。但是我现在需要将此查询返回的照片限制为仅具有is_processing属性的照片,即nil。是否可以进行相同的查询,但条件是返回的照片等同于:.where(photo:'photo.is_processingIS
-if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc
Ocra无法处理需要“tk”的应用程序require'tk'puts'nope'用奥克拉http://github.com/larsch/ocra不起作用(如链接中的一个问题所述)问题:https://github.com/larsch/ocra/issues/29(Ocra是1.9的"new"rubyscript2exe,本质上它用于将rb脚本部署为可执行文件)唯一的问题似乎是缺少tcl的DLL文件我不认为这是一个问题据我所知,问题是缺少tk的DLL文件如果它们是已知的,则可以在执行ocra时将它们包括在内有没有办法知道tk工作所需的DLL依赖项? 最佳答
我正在使用DMOZ的listofurltopics,其中包含一些具有包含下划线的主机名的url。例如:608609TheOuterHeaven610InformationandimagegalleryofMcFarlane'sactionfiguresforTrigun,Akira,TenchiMuyoandotherJapaneseSci-Fianimations.611Top/Arts/Animation/Anime/Collectibles/Models_and_Figures/Action_Figures612虽然此url可以在网络浏览器中使用(或者至少在我的浏览器中可以使用:
我有一个数组:array=['Footballs','Baseball','football','Soccer']而且我需要计算看到Football或Baseball的次数,无论大小写和复数形式如何。这是我尝试做的,但没有成功:array.count{|x|x.downcase.include?'football'||x.downcase.include?'baseball'}编写这段代码的正确或更好的方法是什么?我正在寻找3作为答案。 最佳答案 我会将count与一个block结合使用,该block根据与您正在寻找的约束相匹配的正
在Ruby(1.8.X)中为什么Object既继承了内核又包含了内核?仅仅继承还不够吗?irb(main):006:0>Object.ancestors=>[Object,Kernel]irb(main):005:0>Object.included_modules=>[Kernel]irb(main):011:0>Object.superclass=>nil请注意,在Ruby1.9中情况类似(但更简洁):irb(main):001:0>Object.ancestors=>[Object,Kernel,BasicObject]irb(main):002:0>Object.included
我有两个具有相同方法名称的模块。当我在某个类中包含两个模块时,只执行最后一个模块的方法。我需要在初始化类时同时执行:classMyClassincludeFirstModuleincludeSecondModuledefinitializefoo#fooiscontainedinbothmodulesbutonlytheoneinSecondModulesisexecutedendend可行吗? 最佳答案 正如YusukeEndoh所说,在Ruby中一切皆有可能。在这种情况下,您必须忘记只说“foo”的便利性,并且必须非常明确地说明