淄博著名网站开发方法,wordpress豆瓣小站,怎么建设网站首页,河南基安建设集团有限公司网站你是一台电脑#xff0c;你的名字叫 A
很久很久之前#xff0c;你不与任何其他电脑相连接#xff0c;孤苦伶仃。 直到有一天#xff0c;你希望与另一台电脑 B 建立通信#xff0c;于是你们各开了一个网口#xff0c;用一根网线连接了起来。 用一根网线连接起来怎么就能…你是一台电脑你的名字叫 A
很久很久之前你不与任何其他电脑相连接孤苦伶仃。 直到有一天你希望与另一台电脑 B 建立通信于是你们各开了一个网口用一根网线连接了起来。 用一根网线连接起来怎么就能通信了呢我可以给你讲 IO、讲中断、讲缓冲区但这不是研究网络时该关心的问题。
如果你纠结要么去研究一下操作系统是如何处理网络 IO 的要么去研究一下包是如何被网卡转换成电信号发送出去的要么就仅仅把它当做电脑里有个小人在开枪吧~ 反正你们就是连起来了并且可以通信。
第一层 物理层集线器
有一天一个新伙伴 C 加入了但聪明的你们很快发现可以每个人开两个网口用一共三根网线彼此相连。 随着越来越多的人加入你发现身上开的网口实在太多了而且网线密密麻麻混乱不堪。而实际上一台电脑根本开不了这么多网口所以这种连线只在理论上可行所以连不上的我就用红色虚线表示了就是这么严谨哈哈~ 于是你们发明了一个中间设备你们将网线都插到这个设备上由这个设备做转发就可以彼此之间通信了本质上和原来一样只不过网口的数量和网线的数量减少了不再那么混乱。 你给它取名叫集线器它仅仅是无脑将电信号转发到所有出口广播不做任何处理你觉得它是没有智商的因此把人家定性在了物理层。 由于转发到了所有出口那 BCDE 四台机器怎么知道数据包是不是发给自己的呢
首先你要给所有的连接到集线器的设备都起个名字。原来你们叫 ABCD但现在需要一个更专业的全局唯一的名字作为标识你把这个更高端的名字称为 MAC 地址。
这样A 在发送数据包给 B 时只要在头部拼接一个这样结构的数据就可以了。 B 在收到数据包后根据头部的目标 MAC 地址信息判断这个数据包的确是发给自己的于是便收下。
其他的 CDE 收到数据包后根据头部的目标 MAC 地址信息判断这个数据包并不是发给自己的于是便丢弃。 虽然集线器使整个布局干净不少但原来我只要发给电脑 B 的消息现在却要发给连接到集线器中的所有电脑这样既不安全又不节省网络资源。
第二层 数据链路层交换机
如果把这个集线器弄得更智能一些只发给目标 MAC 地址指向的那台电脑就好了。 虽然只比集线器多了这一点点区别但看起来似乎有智能了你把这东西叫做交换机。也正因为这一点点智能你把它放在了另一个层级数据链路层。 如上图所示你是这样设计的。
交换机内部维护一张 MAC 地址表记录着每一个 MAC 地址的设备连接在其哪一个端口上。
MAC 地址端口bb-bb-bb-bb-bb-bb1cc-cc-cc-cc-cc-cc3aa-aa-aa-aa-aa-aa4dd-dd-dd-dd-dd-dd5
假如你仍然要发给 B 一个数据包构造了如下的数据结构从网口出去。 到达交换机时交换机内部通过自己维护的 MAC 地址表发现目标机器 B 的 MAC 地址 bb-bb-bb-bb-bb-bb 映射到了端口 1 上于是把数据从 1 号端口发给了 B完事~
你给这个通过这样传输方式而组成的小范围的网络叫做以太网。
当然最开始的时候MAC 地址表是空的是怎么逐步建立起来的呢
假如在 MAC 地址表为空是你给 B 发送了如下数据 由于这个包从端口 4 进入的交换机所以此时交换机就可以在 MAC地址表记录第一条数据 MACaa-aa-aa-aa-aa-aa-aa 端口4 交换机看目标 MAC 地址bb-bb-bb-bb-bb-bb在地址表中并没有映射关系于是将此包发给了所有端口也即发给了所有机器。
之后只有机器 B 收到了确实是发给自己的包于是做出了响应响应数据从端口 1 进入交换机于是交换机此时在地址表中更新了第二条数据 MACbb-bb-bb-bb-bb-bb 端口1 过程如下 经过该网络中的机器不断地通信交换机最终将 MAC 地址表建立完毕~ 随着机器数量越多交换机的端口也不够了但聪明的你发现只要将多个交换机连接起来这个问题就轻而易举搞定~ 你完全不需要设计额外的东西只需要按照之前的设计和规矩来按照上述的接线方式即可完成所有电脑的互联所以交换机设计的这种规则真的很巧妙。你想想看为什么比如 A 要发数据给 F。
但是你要注意上面那根红色的线最终在 MAC 地址表中可不是一条记录呀而是要把 EFGH 这四台机器与该端口端口6的映射全部记录在表中。
最终两个交换机将分别记录 A ~ H 所有机器的映射记录。
左边的交换机
MAC 地址端口bb-bb-bb-bb-bb-bb1cc-cc-cc-cc-cc-cc3aa-aa-aa-aa-aa-aa4dd-dd-dd-dd-dd-dd5ee-ee-ee-ee-ee-ee6ff-ff-ff-ff-ff-ff6gg-gg-gg-gg-gg-gg6hh-hh-hh-hh-hh-hh6
右边的交换机
MAC 地址端口bb-bb-bb-bb-bb-bb1cc-cc-cc-cc-cc-cc1aa-aa-aa-aa-aa-aa1dd-dd-dd-dd-dd-dd1ee-ee-ee-ee-ee-ee2ff-ff-ff-ff-ff-ff3gg-gg-gg-gg-gg-gg4hh-hh-hh-hh-hh-hh6
这在只有 8 台电脑的时候还好甚至在只有几百台电脑的时候都还好所以这种交换机的设计方式已经足足支撑一阵子了。
但很遗憾人是贪婪的动物很快电脑的数量就发展到几千、几万、几十万。
第三层 网络层路由器
交换机已经无法记录如此庞大的映射关系了。
此时你动了歪脑筋你发现了问题的根本在于连出去的那根红色的网线后面不知道有多少个设备不断地连接进来从而使得地址表越来越大。
那我可不可以让那根红色的网线接入一个新的设备这个设备就跟电脑一样有自己独立的 MAC 地址而且同时还能帮我把数据包做一次转发呢
这个设备就是路由器它的功能就是作为一台独立的拥有 MAC 地址的设备并且可以帮我把数据包做一次转发你把它定在了网络层。 注意路由器的每一个端口都有独立的 MAC 地址
好了现在交换机的 MAC 地址表中只需要多出一条 MAC 地址 ABAB 与其端口的映射关系就可以成功把数据包转交给路由器了这条搞定。
那如何做到把发送给 C 和 D甚至是把发送给 DEFGH… 的数据包统统先发送给路由器呢
不难想到这样一个点子假如电脑 C 和 D 的 MAC 地址拥有共同的前缀比如分别是 C 的 MAC 地址FFFF-FFFF-CCCC D 的 MAC 地址FFFF-FFFF-DDDD 那我们就可以说将目标 MAC 地址为 FFFF-FFFF 开头的统统先发送给路由器。
这样是否可行呢答案是否定的。
我们先从现实中 MAC 地址的结构入手MAC地址也叫物理地址、硬件地址长度为 48 位一般这样来表示 00-16-EA-AE-3C-40 它是由网络设备制造商生产时烧录在网卡的EPROM一种闪存芯片通常可以通过程序擦写。其中前 24 位00-16-EA代表网络硬件制造商的编号后 24 位AE-3C-40是该厂家自己分配的一般表示系列号。只要不更改自己的 MAC 地址MAC 地址在世界是唯一的。形象地说MAC地址就如同身份证上的身份证号码具有唯一性。
那如果你希望向上面那样表示将目标 MAC 地址为 FFFF-FFFF-开头的统一从路由器出去发给某一群设备后面会提到这其实是子网的概念那你就需要要求某一子网下统统买一个厂商制造的设备要么你就需要要求厂商在生产网络设备烧录 MAC 地址时提前按照你规划好的子网结构来定 MAC 地址并且日后这个网络的结构都不能轻易改变。
这显然是不现实的。
于是你发明了一个新的地址给每一台机器一个 32 位的编号如 11000000101010000000000000000001 你觉得有些不清晰于是把它分成四个部分中间用点相连。 11000000.10101000.00000000.00000001 你还觉得不清晰于是把它转换成 10 进制。 192.168.0.1 最后你给了这个地址一个响亮的名字IP 地址。现在每一台电脑同时有自己的 MAC 地址又有自己的 IP 地址只不过 IP 地址是软件层面上的可以随时修改MAC 地址一般是无法修改的。
这样一个可以随时修改的 IP 地址就可以根据你规划的网络拓扑结构来调整了。 如上图所示假如我想要发送数据包给 ABCD 其中一台设备不论哪一台我都可以这样描述“将 IP 地址为 192.168.0 开头的全部发送给到路由器之后再怎么转发交给它”巧妙吧。
那交给路由器之后路由器又是怎么把数据包准确转发给指定设备的呢
别急我们慢慢来。
我们先给上面的组网方式中的每一台设备加上自己的 IP 地址 现在两个设备之间传输除了加上数据链路层的头部之外还要再增加一个网络层的头部。
假如 A 给 B 发送数据由于它们直接连着交换机所以 A 直接发出如下数据包即可其实网络层没有体现出作用。 但假如 A 给 C 发送数据A 就需要先转交给路由器然后再由路由器转交给 C。由于最底层的传输仍然需要依赖以太网所以数据包是分成两段的。
A ~ 路由器这段的包如下 路由器到 C 这段的包如下 好了上面说的两种情况A-BA-C相信细心的读者应该会有不少疑问下面我们一个个来展开。 A 给 C 发数据包怎么知道是否要通过路由器转发呢 答案子网 如果源 IP 与目的 IP 处于一个子网直接将包通过交换机发出去。 如果源 IP 与目的 IP 不处于一个子网就交给路由器去处理。 好那现在只需要解决什么叫处于一个子网就好了。 192.168.0.1 和 192.168.0.2 处于同一个子网 192.168.0.1 和 192.168.1.1 处于不同子网 这两个是我们人为规定的即我们想表示对于 192.168.0.1 来说 192.168.0.xxx 开头的就算是在一个子网否则就是在不同的子网。 那对于计算机来说怎么表达这个意思呢于是人们发明了子网掩码的概念 假如某台机器的子网掩码定为 255.255.255.0 这表示将源 IP 与目的 IP 分别同这个子网掩码进行与运算相等则是在一个子网不相等就是在不同子网就这么简单。 比如 A电脑192.168.0.1 255.255.255.0 192.168.0.0B电脑192.168.0.2 255.255.255.0 192.168.0.0C电脑192.168.1.1 255.255.255.0 192.168.1.0D电脑192.168.1.2 255.255.255.0 192.168.1.0 那么 A 与 B 在同一个子网C 与 D 在同一个子网但是 A 与 C 就不在同一个子网与 D 也不在同一个子网以此类推。 所以如果 A 给 C 发消息A 和 C 的 IP 地址分别 A 机器配置的子网掩码发现不相等则 A 认为 C 和自己不在同一个子网于是把包发给路由器就不管了之后怎么转发A 不关心。 A 如何知道哪个设备是路由器 答案在 A 上要设置默认网关 上一步 A 通过是否与 C 在同一个子网内判断出自己应该把包发给路由器那路由器的 IP 是多少呢 其实说发给路由器不准确应该说 A 会把包发给默认网关。 对 A 来说A 只能直接把包发给同处于一个子网下的某个 IP 上所以发给路由器还是发给某个电脑对 A 来说也不关心只要这个设备有个 IP 地址就行。 所以默认网关就是 A 在自己电脑里配置的一个 IP 地址以便在发给不同子网的机器时发给这个 IP 地址对应的MAC地质。 仅此而已 路由器如何知道C在哪里 答案路由表 现在 A 要给 C 发数据包已经可以成功发到路由器这里了最后一个问题就是路由器怎么知道收到的这个数据包该从自己的哪个端口出去才能直接或间接地最终到达目的地 C 呢。 路由器收到的数据包有目的 IP 也就是 C 的 IP 地址需要转化成从自己的哪个端口出去很容易想到应该有个表就像 MAC 地址表一样。 这个表就叫路由表。 至于这个路由表是怎么出来的有很多路由算法本文不展开因为我也不会哈哈~ 不同于 MAC 地址表的是路由表并不是一对一这种明确关系我们下面看一个路由表的结构。 目的地址子网掩码下一跳端口192.168.0.0255.255.255.00192.168.0.254255.255.255.2550192.168.1.0255.255.255.01192.168.1.254255.255.255.2551我们学习一种新的表示方法由于子网掩码其实就表示前多少位表示子网的网段所以如 192.168.0.0255.255.255.0 也可以简写为 192.168.0.0/24 目的地址下一跳端口192.168.0.0/240192.168.0.254/320192.168.1.0/241192.168.1.254/321这就很好理解了路由表就表示192.168.0.xxx 这个子网下的都转发到 0 号端口192.168.1.xxx 这个子网下的都转发到 1 号端口。下一跳列还没有值我们先不管 配合着结构图来看这里把子网掩码和默认网关都补齐了图中 笔误结果应该是 .0 刚才说的都是 IP 层但发送数据包的数据链路层需要知道 MAC 地址可是我只知道 IP 地址该怎么办呢 答案arp 假如你A此时不知道你同伴 B 的 MAC 地址现实中就是不知道的刚刚我们只是假设已知你只知道它的 IP 地址你 该怎么把数据包准确传给 B 呢 答案很简单在网络层我需要把 IP 地址对应的 MAC 地址找到也就是通过某种方式找到 192.168.0.2 对应的 MAC 地址 BBBB。 这种方式就是 arp 协议同时电脑 A 和 B 里面也会有一张 arp 缓存表表中记录着 IP 与 MAC 地址的对应关系。 IP 地址MAC 地址192.168.0.2BBBB一开始的时候这个表是空的电脑 A 为了知道电脑 B192.168.0.2的 MAC 地址将会广播一条 arp 请求B 收到请求后带上自己的 MAC 地址给 A 一个响应。此时 A 便更新了自己的 arp 表。 这样通过大家不断广播 arp 请求最终所有电脑里面都将 arp 缓存表更新完整。
总结一下
好了总结一下到目前为止就几条规则
从各个节点的视角来看
电脑视角
首先我要知道我的 IP 以及对方的 IP通过子网掩码判断我们是否在同一个子网在同一个子网就通过 arp 获取对方 mac 地址直接扔出去不在同一个子网就通过 arp 获取默认网关的 mac 地址直接扔出去
交换机视角
我收到的数据包必须有目标 MAC 地址通过 MAC 地址表查映射关系查到了就按照映射关系从我的指定端口发出去查不到就所有端口都发出去
路由器视角
我收到的数据包必须有目标 IP 地址通过路由表查映射关系查到了就按照映射关系从我的指定端口发出去不在任何一个子网范围走其路由器的默认网关也是查到了查不到则返回一个路由不可达的数据包
如果你嗅觉足够敏锐你应该可以感受到下面这句话
网络层IP协议本身没有传输包的功能包的实际传输是委托给数据链路层以太网中的交换机来实现的。
涉及到的三张表分别是
交换机中有 MAC 地址表用于映射 MAC 地址和它的端口路由器中有路由表用于映射 IP 地址(段)和它的端口电脑和路由器中都有 arp 缓存表用于缓存 IP 和 MAC 地址的映射关系
这三张表是怎么来的
MAC 地址表是通过以太网内各节点之间不断通过交换机通信不断完善起来的。路由表是各种路由算法 人工配置逐步完善起来的。arp 缓存表是不断通过 arp 协议的请求逐步完善起来的。
知道了以上这些目前网络上两个节点是如何发送数据包的这个过程就完全可以解释通了 那接下来我们 趁热打铁 一下请做好 战斗 准备 这时路由器 1 连接了路由器 2所以其路由表有了下一跳地址这一个概念所以它的路由表就变成了这个样子。如果匹配到了有下一跳地址的一项则需要再次匹配找到其端口并找到下一跳 IP 的 MAC 地址。
也就是说找来找去最终必须能映射到一个端口号然后从这个端口号把数据包发出去。
目的地址下一跳端口192.168.0.0/240192.168.0.254/320192.168.1.0/241192.168.1.254/321192.168.2.0/24192.168.100.5192.168.100.0/242192.168.100.4/322
这时如果 A 给 F 发送一个数据包能不能通呢如果通的话整个过程是怎样的呢 思考一分钟…
详细过程动画描述 详细过程文字描述 首先 A192.168.0.1通过子网掩码255.255.255.0计算出自己与 F192.168.2.2并不在同一个子网内于是决定发送给默认网关192.168.0.254 A 通过 ARP 找到 默认网关 192.168.0.254 的 MAC 地址。 A 将源 MAC 地址AAAA与网关 MAC 地址ABAB封装在数据链路层头部又将源 IP 地址192.168.0.1和目的 IP 地址192.168.2.2注意这里千万不要以为填写的是默认网关的 IP 地址从始至终这个数据包的两个 IP 地址都是不变的只有 MAC 地址在不断变化封装在网络层头部然后发包 交换机 1 收到数据包后发现目标 MAC 地址是 ABAB转发给路由器1 数据包来到了路由器 1发现其目标 IP 地址是 192.168.2.2查看其路由表发现了下一跳的地址是 192.168.100.5 所以此时路由器 1 需要做两件事第一件是再次匹配路由表发现匹配到了端口为 2于是将其封装到数据链路层头部最后把包从 2 号口发出去。 此时路由器 2 收到了数据包看到其目的地址是 192.168.2.2查询其路由表匹配到端口号为 1准备从 1 号口把数据包送出去。 但此时路由器 2 需要知道 192.168.2.2 的 MAC 地址了于是查看其 arp 缓存找到其 MAC 地址为 FFFF将其封装在数据链路层头部并从 1 号端口把包发出去。 交换机 3 收到了数据包发现目的 MAC 地址为 FFFF查询其 MAC 地址表发现应该从其 6 号端口出去于是从 6 号端口把数据包发出去。 F 最终收到了数据包并且发现目的 MAC 地址就是自己于是收下了这个包
你是不是以为到这里就结束了
不好戏才刚刚开始
请休息一分钟我们继续战斗
第四层 传输层
经过刚刚的一番折腾只要你知道另一位伙伴 B 的 IP 地址且你们之间的网络是通的无论多远你都可以将一个数据包发送给你的伙伴 B 这就是物理层、数据链路层、网络层这三层所做的事情。
站在第四层的你就可以不要脸地利用下三层所做的铺垫随心所欲地发送数据而不必担心找不到对方了。 虽然你此时还什么都没干但你还是给自己这一层起了个响亮的名字叫做传输层。
你本以为自己所在的第四层万事大吉啥事没有但很快问题就接踵而至。
前三层协议只能把数据包从一个主机搬到另外一台主机但是到了目的地以后数据包具体交给哪个程序进程呢 所以你需要把通信的进程区分开来于是就给每个进程分配一个数字编号你给它起了一个响亮的名字端口号。 然后你在要发送的数据包上增加了传输层的头部源端口号与目标端口号。 OK这样你将原本主机到主机的通信升级为了进程和进程之间的通信。
你没有意识到你不知不觉实现了 UDP 协议当然 UDP 协议中不光有源端口和目标端口还有数据包长度和校验值我们暂且略过。
就这样你用 UDP 协议无忧无虑地同 B 进行着通信一直没发生什么问题。 但很快你发现事情变得非常复杂…
丢包问题
由于网络的不可靠数据包可能在半路丢失而 A 和 B 却无法察觉。 对于丢包问题只要解决两个事就好了。
第一个A 怎么知道包丢了
答案让 B 告诉 A
第二个丢了的包怎么办
答案重传
于是你设计了如下方案A 每发一个包都必须收到来自 B 的确认ACK再发下一个否则在一定时间内没有收到确认就重传这个包。 你管它叫停止等待协议。只要按照这个协议来虽然 A 无法保证 B 一定能收到包但 A 能够确认 B 是否收到了包收不到就重试尽最大努力让这个通信过程变得可靠于是你们现在的通信过程又有了一个新的特征可靠交付。
效率问题
停止等待虽然能解决问题但是效率太低了A 原本可以在发完第一个数据包之后立刻开始发第二个数据包但由于停止等待协议A 必须等数据包到达了 B 且 B 的 ACK 包又回到了 A才可以继续发第二个数据包这效率慢得可不是一点两点。
于是你对这个过程进行了改进采用流水线的方式不再傻傻地等。 顺序问题
但是网路是复杂的、不可靠的。
有的时候 A 发出去的数据包分别走了不同的路由到达 B可能无法保证和发送数据包时一样的顺序。
在流水线中有多个数据包和ACK包在乱序流动他们之间对应关系就乱掉了。
难道还回到停止等待协议A 每收到一个包的确认ACK再发下一个包那就根本不存在顺序问题。应该有更好的办法
A 在发送的数据包中增加一个序号seq同时 B 要在 ACK 包上增加一个确认号ack这样不但解决了停止等待协议的效率问题也通过这样标序号的方式解决了顺序问题。 而 B 这个确认号意味深长比如 B 发了一个确认号为 ack 3它不仅仅表示 A 发送的序号为 2 的包收到了还表示 2 之前的数据包都收到了。这种方式叫累计确认或累计应答。 注意实际上 ack 的号是收到的最后一个数据包的序号 seq 1也就是告诉对方下一个应该发的序号是多少。但图中为了便于理解ack 就表示收到的那个序号不必纠结。
流量问题
有的时候A 发送数据包的速度太快而 B 的接收能力不够但 B 却没有告知 A 这个情况。 怎么解决呢
很简单B 告诉 A 自己的接收能力A 根据 B 的接收能力相应控制自己的发送速率就好了。
B 怎么告诉 A 呢B 跟 A 说我很强这三个字么那肯定不行得有一个严谨的规范。
于是 B 决定每次发送数据包给 A 时顺带传过来一个值叫窗口大小win)这个值就表示 B 的接收能力。同理每次 A 给 B 发包时也带上自己的窗口大小表示 A 的接收能力。 B 告诉了 A 自己的窗口大小值A 怎么利用它去做 A 这边发包的流量控制呢
很简单假如 B 给 A 传过来的窗口大小 win 5那 A 根据这个值把自己要发送的数据分成这么几类。 图片过于清晰就不再文字解释了。
当 A 不断发送数据包时已发送的最后一个序号就往右移动直到碰到了窗口的上边界此时 A 就无法继续发包达到了流量控制。 但是当 A 不断发包的同时A 也会收到来自 B 的确认包此时整个窗口会往右移动因此上边界也往右移动A 就能发更多的数据包了。 以上都是在窗口大小不变的情况下而 B 在发给 A 的 ACK 包中每一个都可以重新设置一个新的窗口大小如果 A 收到了一个新的窗口大小值A 会随之调整。
如果 A 收到了比原窗口值更大的窗口大小比如 win 6则 A 会直接将窗口上边界向右移动 1 个单位。 如果 A 收到了比原窗口值小的窗口大小比如 win 4则 A 暂时不会改变窗口大小更不会将窗口上边界向左移动而是等着 ACK 的到来不断将左边界向右移动直到窗口大小值收缩到新大小为止。
OK终于将流量控制问题解决得差不多了你看着上面一个个小动图给这个窗口起了一个更生动的名字滑动窗口。
拥塞问题
但有的时候不是 B 的接受能力不够而是网络不太好造成了网络拥塞。 拥塞控制与流量控制有些像但流量控制是受 B 的接收能力影响而拥塞控制是受网络环境的影响。
拥塞控制的解决办法依然是通过设置一定的窗口大小只不过流量控制的窗口大小是 B 直接告诉 A 的而拥塞控制的窗口大小按理说就应该是网络环境主动告诉 A。
但网络环境怎么可能主动告诉 A 呢只能 A 单方面通过试探不断感知网络环境的好坏进而确定自己的拥塞窗口的大小。 拥塞窗口大小的计算有很多复杂的算法就不在本文中展开了假如拥塞窗口的大小为 cwnd上个问题中流量控制的滑动窗口大小为 rwnd那么窗口的右边界受这两个值共同的影响需要取它俩的最小值。
窗口大小 min(cwnd, rwnd)
含义很容易理解当 B 的接受能力比较差时即使网络非常通畅A 也需要根据 B 的接收能力限制自己的发送窗口。当网络环境比较差时即使 B 有很强的接收能力A 也要根据网络的拥塞情况来限制自己的发送窗口。正所谓受其短板的影响嘛~
连接问题
有的时候B 主机的相应进程还没有准备好或是挂掉了A 就开始发送数据包导致了浪费。 这个问题在于A 在跟 B 通信之前没有事先确认 B 是否已经准备好就开始发了一连串的信息。就好比你和另一个人打电话你还没有喂一下确认对方有没有在听你就巴拉巴拉说了一堆。 这个问题该怎么解决呢
地球人都知道三次握手嘛
A我准备好了(SYN)
B我知道了(ACK)我也准备好了(SYN)
A我知道了(ACK) A 与 B 各自在内存中维护着自己的状态变量三次握手之后双方的状态都变成了连接已建立ESTABLISHED。
虽然就只是发了三次数据包并且在各自的内存中维护了状态变量但这么说总觉得太 low你看这个过程相当于双方建立连接的过程于是你灵机一动就叫它面向连接吧。
注意这个连接是虚拟的是由 A 和 B 这两个终端共同维护的在网络中的设备根本就不知道连接这回事儿
但凡事有始就有终有了建立连接的过程就要考虑释放连接的过程又是地球人都知道四次挥手嘛
A再见我要关闭了(FIN)
B我知道了(ACK)
给 B 一段时间把自己的事情处理完…
B再见我要关闭了(FIN)
A我知道了(ACK) 总结
以上讲述的就是 TCP 协议的核心思想上面过程中需要传输的信息就体现在 TCP 协议的头部这里放上最常见的 TCP 协议头解读的图。 不知道你现在再看下面这句话是否能理解
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议
面向连接、可靠这两个词通过上面的讲述很容易理解那什么叫做基于字节流呢
很简单TCP 在建立连接时需要告诉对方 MSS最大报文段大小。
也就是说如果要发送的数据很大在 TCP 层是需要按照 MSS 来切割成一个个的 TCP 报文段 的。
切割的时候我才不管你原来的数据表示什么意思需要在哪里断句啥的我就把它当成一串毫无意义的字节在我想要切割的地方咔嚓就来一刀标上序号只要接收方再根据这个序号拼成最终想要的完整数据就行了。
在我 TCP 传输这里我就把它当做一个个的字节也就是基于字节流的含义了。 最后留给大家一个作业模拟 A 与 B 建立一个 TCP 连接。
第一题A 给 B 发送 “aaa” 然后 B 给 A 回复一个简单的字符串 “success”并将此过程抓包。
第二题A 给 B 发送 “aaaaaa … a” 超过最大报文段大小然后 B 给 A 回复一个简单的字符串 “success”并将此过程抓包。
下面是我抓的包第二题
三次握手阶段
A - B [SYN] Seq0 Win64240 Len0 MSS1460 WS256
B - A [SYN, ACK] Seq0 Ack1 Win29200 Len0 MSS1424 WS512
A - B [ACK] Seq1 Ack1 Win132352 Len0
数据发送阶段
A - B [ACK] Seq1 Ack1 Win132352 Len1424
A - B [ACK] Seq1425 Ack1 Win132352 Len1424
A - B [PSH, ACK] Seq2849 Ack1 Win132352 Len1247
B - A [ACK] Seq1 Ack1425 Win32256 Len0
B - A [ACK] Seq1 Ack2849 Win35328 Len0
B - A [ACK] Seq1 Ack4096 Win37888 Len0
B - A [PSH, ACK] Seq1 Ack4096 Win37888 Len7
四次挥手阶段
B - A [FIN, ACK] Seq8 Ack4096 Win37888 Len0
A - B [ACK] Seq4096 Ack9 Win132352 Len0
A - B [FIN, ACK] Seq4096 Ack9 Win132352 Len0下面少复制了一行ACK抱歉 参考你管这破玩意叫网络