当前位置: 首页 > news >正文

so域名网站校园网站建设的系统分析

so域名网站,校园网站建设的系统分析,wordpress不同页面布局,西宁网站建设兼职目录 一#xff0c;关于Tcp协议 二#xff0c;Tcp报头字段解析 2.0 协议字段图示 2.1 两个老问题 2.2 16位窗口大小 2.3 32位序号和确认序号 2.4 6个标记位 三#xff0c;Tcp保证可靠性策略 3.1 确认应答机制#xff08;核心#xff09; 3.2 超时重传机制 3.3 …目录 一关于Tcp协议 二Tcp报头字段解析 2.0 协议字段图示 2.1 两个老问题 2.2 16位窗口大小 2.3 32位序号和确认序号 2.4 6个标记位 三Tcp保证可靠性策略 3.1 确认应答机制核心 3.2 超时重传机制 3.3 Tcp连接管理机制 3.3.1 操作系统对连接的管理 3.3.2 三次握手重点 3.3.3 四次挥手 3.4 流量控制 四Tcp提高传输效率策略 4.1 滑动窗口 4.2 快速重传 4.3 延迟应答 4.4 捎带应答 五拥塞控制 六扩展 6.1 面向字节流 VS 面向数据报 6.2 数据包粘包问题 6.3 Tcp连接异常情况 七Tcp总结 一关于Tcp协议 Tcp全称“传输控制协议Transmission Control Protocol”是当今互联网使用最广泛的传输层协议因为它基于通信时保证可靠性并且对于高效传输也有一定策略是目前应用层底层使用的协议中非常常见的一种协议 我们来认识下“传输控制协议”的“控制”二字含义 之前实现的网络版计算器用的是http/https底层就是Tcp在通信双方内部都有着发送缓冲区和接收缓冲区我们调用writereadrecvsend等操作其实不是把数据都发送到网络中而是把数据拷贝到操作系统的内核缓冲区中至于数据什么时候发送发送多少出错了怎么办这个完全由Tcp协议自主决定所以上面那些函数本质应该叫做拷贝函数这和我们文件操作一样用write通过文件描述符写入时都是先把数据拷贝到文件缓冲区里然后数据从文件缓冲区刷新到磁盘文件上完全由操作系统和磁盘驱动决定。这么看来只要把磁盘设备换成网卡就可以完成数据远程发送或者IO了。而数据发送本质也就是把数据从我们的发送缓冲区拷贝到对方的接收缓冲区所以网络发送“本质也是拷贝”。所以发送就是要通过网络而传输距离很长网络可能会出错所以TCP就是为了应对这些出错做的策略因为Tcp既能接收也能发送所以双方的地位是对等的服务器构建响应其实也就是把响应处理好然后通过应用层的各种接口把响应拷贝到TCP发送缓冲区而双方也有接收缓冲区和发送缓冲区所以发送也是拷贝你给我拷我给你拷你不给我拷我也可以给你拷这个过程就叫做“全双工”。所以文件描述符和套接字既可以写也可以读因为双方的发送和接收缓冲区是独立的不会相互影响 背景缓冲区也是内存空间操作系统为了管理内存把整个内存在逻辑上分成很多的4KB的空间所以内存内部有大量的内存块为了管理内存块就有很多struct_page这样的结构。所以发送接收缓冲区是由很多个4KB的内存块和struct_page构成的然后再先描述再组织。所以打开一个网络套接字就是打开了一个文件描述符而打开的文件也一定有struct_file对象而这个对象在以前里面就有指针指向磁盘而有了网络之后struct_file就直接指向网卡此时就能上层不变下层直接切换成网络这样的功能 所以“传输控制协议”的“控制”应用层把数据拷贝到发送缓冲区里什么时候发发多少出错了怎么办其实本质也就是在控制如何发送的问题这些工作由Tcp协议自主决定所以我们把TCP叫做“传输控制协议”。  二Tcp报头字段解析 2.0 协议字段图示 三部分标准报头前20字节选项暂时忽略有效载荷要传输的数据。前两个字段16位源端口号和16位目的端口号和UDP一样能够讲报文数据交付给上层的某个协议  Tcp报头在内核代码中就是一个位段类型给数据封装Tcp报头时实际上就是用这个位段类型定义一个变量然后往该变量中填充Tcp报头的各个属性字段这个步骤和我们之前填充 sockaddr结构体 的步骤大差不差  可以看到Tcp协议的报头字段相比Udp多了几倍因为Tcp要保证可靠性所以Tcp会在底层做更多的动作下面我们来详细解释下各字段的作用  2.1 两个老问题 前面说过学习任何协议都离不开两个问题 报头如何和有效载荷分离有效载荷如何交付给上层 在TCP协议中标准报头的长度是20个字节在缓冲区中我们把前20个字节信息分离就拿到了报头报头中有一个“4位首部长度”表示报头的总长度是多少因为我们还有选项可以不带4位首部长度范围为00001111-- (015)15是小于20的乍一看不能表示报头的长度因为4位首部长度在计算的时候有基本的大小单位4字节报头的宽度所以真正的表示范围为0 -- 60个字节报头长度是20所以选项最长是40个字节大部分时间我们不谈选项所以4位首部长度一般为0101是5就能准确代表标准报头长度就能有效分离报头了然后就和Udp一样对有效载荷做解析就能得到16位端口号然后就可以交付给上层应用了 2.2 16位窗口大小 客户端要发送数据给服务器先把http请求拷贝到发送缓冲区拷贝到服务器接收缓冲区在这个过程中客户端和服务器基于TCP协议进行通信的时候发送的是完整的报文即一定携带完整的TCP报头如果发送方一直发数据但是接收方不读所以客户端是并不知道服务器对数据的读取情况的所以客户端就会一直发但是服务器来不及收导致服务器接收缓冲区被写满就会出现数据丢包所以双方必须想办法在服务器“接收缓冲区快满”的情况下让客户端发慢一点或者干脆不发了这种由发送方往接收方发消息通过控制发送方发送的速度来让对方来得及接收从而防止大面积丢包的情况我们叫做“流量控制”所以我们现在的重点就是“如何让客户端知道接收端缓冲区还要多少剩余空间” 重谈通信过程 发送消息后对方会立即对该报文进行相应对方就会给我发送确认应答所以这个确认应答一定也是一个完整的TCP报文可以不带数据但至少要有报头。那么服务器让客户端发数据慢一点点那么慢一点的依据是什么由谁决定由对方的接收缓冲区的剩余大小决定。那客户端咋知道接收方剩余空间大小呢确认应答机制也是完整的报文这个报文的16位窗口大小填充的就是对方接收缓冲区的剩余大小而双方互发消息 互相进行确认应答那么双方就能都及时知道对方接收缓冲区的大小这样双方就能都进行流量控制 -- 所以窗口大小填充的就是自己的接收缓冲区的剩余大小它的作用就是来进行流量控制的 第一次给对方发报文不知道对方的缓冲区剩余空间大小该咋办呢我们不管只要一段时间对方没给我发响应我就认为对方没收到重发  TCP有重传机制如果有大面积丢包TCP可以重传但是不合理因为网络传输是有代价的数据已经消耗了资源到达了对方但是却要无缘无故丢掉这就是一种浪费所以“流量控制”比“丢包重传”更好 2.3 32位序号和确认序号 问题这个世界上存不存在100%可靠的网络协议 场景我收到了消息然后我要告诉对方我收到了然而我告诉了对方“我收到了”这条消息我无法保证对方也收到了所以对方也要再次给我发“我收到了你收到了的消息”然后这条消息再次需要应答。。。。。。没有应答的数据我们无法保证可靠性所以最新的一条消息是没有应答的 解答我们无法保证发出去的消息是100%可靠的所以这个世界上是不存在100%可靠的协议虽然我们无法保证最新消息的可靠性但是我们可以保证最新消息之前的消息是可靠的 客户端发tcp数据给服务器服务器会给客户端发应答当服务器发了应答后就能保证服务器到客户端这条方向传输的可靠性服务器发数据给客户端客户端发应答给服务器当服务器接收到应答也就能保证服务器到客户端这条方向的可靠性所以我们宏观上最新的消息不能保证可靠性但是在局部上两个应答可以间接相互弥补可靠性一段时间客户端没有收到应答客户端认为数据丢失会进行重传服务器同理 下面我们来正式介绍一下序号的作用  捎带应答 场景“我们一起吃饭吧”“好的我们吃什么鸭”“我们吃饺子吧”“好的”这样聊天效率有点低所以我们一般是这样搞“我们一起吃饺子吧”“好的”。把四次交流缩短至两次交流 我们的应答报文中同时携带应答和要发送的消息这叫做捎带应答就能够提高效率所以客户端发数据时直接发送一批报文只要这批报文我全部收到应答也能保证对方收到了我的一批报文 如果捎带应答同时发多个报文也会有乱序问题而乱序本身也是不可靠的一种所以会在报文带上序号保证数据的按序到达保证可靠性所以这个32位序号就是保证数据的按序到达 问题什么是序列号解答用户层有tcp发送缓冲区char outbuffer[N]上层拷贝下来的数据在这个数组一个字节一个字节存储的而天然每个字节都会有自己的编号本质就是数组下标发送的数据块的最后一个字符的下标就是序号 至于确认序号就是填充的是收到报文的序号1假如我发1000200030004000那么服务器给我发的确认序号就是1001200130014001为什么要这样规定呢确认序号的意义表示确认之前的数据我已经收到了我给客户端返回1001表示前面的1000我已经收到了下一次发送“请从确认序号指定的数字开始发送”所以下次客户端就开始从1001开始发应答是允许有少量的丢失假如我发了1000和2000和3000但是服务器没有给我发1001和2001只发了3001也是可以的3001表示3000的数据我已经收到了因为3000包括1000和2000所以Tcp允许应答有少量的丢失具体的细节我们后面再讲 面试题客户端把序号搞成1000然后服务器可以把1000变成1001然后返回给我为什么还要搞一个32位确认序号呢场景服务器给我应答可能服务器给我的应答也会携带数据捎带应答假设客户端发1000然后服务器的捎带应答中包含了2000数据所以需要同时客户端发1001和2000的序号因为tcp双方地位是等同的所以协议必须把两个序号分开不能复用 应答可能是捎带应答有双重身份既有应答也有数据所以需要分开服务器在给客户端发消息同时客户端可能也在对服务器发消息  对于丢包问题我们到滑动窗口再详细解释  总结 32位序号作用就是保证数据按序到达同时也是作为对端发送报文时填充32位确认序号的根据32位确认序号也是为了告诉当前已经收到的字节数据有哪些发送发下一次发送数据时该从哪一字节序列开始发送序号和确认序号是确认应答机制的数据化表示确认应答机制就是由序号和确认序号来保证的此外序号还可以判断是否有数据丢包 2.4 6个标记位 问题为什么有标记位 解答 一个服务器可能会收到来自多个客户端的的请求Tcp通信时要建立连接最后Tcp还要断开连接在这中间它还要进行正常的数据通信所以Tcp有的是建立连接的有的是进行数据发送的有些是进行断开连接的所以服务器收到的Tcp报文本身是有类型的不同的报文类型决定了服务器要做不同的动作。 所以接收方如何得知报头的类型是什么呢 -- Tcp里面存在6个标志位。标志位存在的意义区分Tcp报文的类型 ①SYN 当报文中的SYN被设置成1时表面该报文是一个请求建立连接的报文就是三次握手时发送的报文只要在三次握手阶段才会设置SYN正常通信时SYN不会被设置 ②ACK 报文中的ACK被设置为1时标明该报文是一个确认应答报文而且除了三次握手的第一次握手没有设置ACK往后其余的报文都会设置ACK因为在进行响应时会把响应和下一次要发送的数据一起发过去而且我们发回去的数据本身就对对方发过来的数据具有一定的确认能力 ③FIN FIN被设置成1表示这个报文是请求断开连接的报文就是四次挥手时发的只有在断开连接阶段才会被设置正常通信不会设置 ④PSH 该标志位表示提醒接收端应用立刻从TCP缓冲区把数据读走因为流量控制导致上层一直把数据不读走那么缓冲区的数据会变得越来越多可用空间越来越小操作系统把接收到的数据放到缓冲区里然后用户去读取这也是一个生产者消费者模型所以所谓的流量控制就是对发送过程的一个同步的过程当对方缓冲区写满的时候我写进程就阻塞了然后我就要等但是要等多久我不知道对方缓冲区啥时候会有空位我也不知道就出现了僵持。所以有两种策略1发送方定时询问对方接收缓冲区大小    2一旦接收方缓冲区空间有更新给对方发一个通知。两种策略同时存在。但是如果接收方就是不把数据拿走所以我们可以继续给对方发TCP报文把PSH标记位设置成1表示提示对方马上把缓冲区的数据读走你要是不读走我就认为你当前不想和我通信了可能会关闭连接等其它操作 ⑤RST 问题TCP是保证可靠性的那么三次握手和四次挥手必须要是成功的吗也就是建立连接必须得成功 解答虽然tcp保证可靠性但是也允许连接建立失败。 一个服务器可能对应多个客户端所以服务器可能会有多个对应的客户端建立连接的文件描述符所以服务器就要对这些连接进行管理 建立连接就是服务器生成对应的描述该连接的结构体然后先描述再组织。 三次握手 第一次客户端给服务器发请求报文会带一个SYN标记位表示请求建立链接第一次握手然后服务器发回SYNACK表示服务器同意和客户端链接第二次握手ACK是对上一个报文的应答然后客户端再次发送ACK表示确认第三次握手这就是三次握手经过网络 由于三次握手的每次握手都有时间差所以重点不在“握手”而在“三次”上那么客户端认为链接建立好了是第三次握手我把ACK发出去就确认了还是我要确认服务器把消息收到了才确认呢事实上最后一个ACK是没有应答的所以客户端只要把第三次报文发出去了就认为链接建立好了所以三次握手其实是在“赌”因为第三次ACK可能会丢前面两个都有应答不怕丢所以Tcp是允许链接建立失败的当客户端发送第三次ACK成功但是服务器没有收到ACK那么客户端和服务器对与链接是否已经建立好了的认知不一致导致客户端认为三次握手已经完成直接开始传输但是服务器还停留在第二次握手上此时服务器直接收到了来自客户端的数据咋办服务器就会猜测以上情况可能发生于是服务器在下一次给客户端应答的报文中将RST标志位设置为1告诉客户端刚刚的链接没有建好就让客户端重新发起三次握手所以RST标志位的作用就是“当链接异常情况下让双方重新建立链接”上面说的“第三次握手报文丢失”只是众多链接异常情况下的一种比如浏览器连接被重置服务器压力过大等 ⑥URG  URG表示紧急标记位一般在某些特殊情况下才会使用 场景TCP是按序到达的对方会根据序号排序但是一些情况下我们想让一些数据优先处理也就是“插队”被优先处理在原始TCP规定下“插队”情况是不可能存在的但是我们就是想让一些数据优先处理那么我们就可以设置URG标记位当没有数据要优先处理时URG为016位紧急指针无效当URG为1时16位紧急指针就有效紧急指针表示这个报文中要优先处理的数据在有效载荷中的偏移量但是紧急数据多大呢在TCP协议中紧急数据默认只允许携带一个字节什么样的情况下我们才会用这个URG呢一个机房机房里有一个服务器服务器上搭载了一个服务里面有接收和发送缓冲区。有一个客户端连接了但是用着用着服务器突然不给我响应了但是没有四次挥手服务器也没挂所以客户端很疑惑服务器为什么不给我响应所以客户端要询问服务器发生了什么所以我们需要让服务器支持“读取紧急数据”然后我在服务器里面的软件功能添加某些服务状态计算IO同时做等给每个状态编号当服务器又卡顿时客户端就给它发一个紧急数据所以服务器会优先处理紧急数据服务器就会把当前状态也用紧急指针发给客户端紧急数据在应用层上叫做“带外数据”报头中的“16位紧急指针”代表的就是紧急数据在报文中的偏移量只能表示数据端的一个位置所以Tcp的紧急数据只能是一个字节 recv函数的第四个参数flags有一个叫做MSG_OOB的选项可供设置OOB表示带外数据out-of-band的简称就是紧急数据就可以在使用recv函数进行读取并设置MSG_OOB选项 与之对应的send函数的第四个参数也有MSG_OOB选项就可以用send函数写入紧急数据 三Tcp保证可靠性策略 3.1 确认应答机制核心 确认应答机制就是由Tcp报头字段中的32位序号和32位确认序号来保证的 注意确认应答机制不是保证双方的通信的可靠性而是通过收到对方应答来保证一方通信的可靠性但是如果双方都这样搞就是间接保证了双方通信的可靠性 确认应答机制前面已经大量涉及了这里不再赘述了只要记住确认应答机制是Tcp保证可靠性的核心或者说是基础其余绝大部分的保证可靠性机制都是建立在确认应答机制基础上的 3.2 超时重传机制 定义双方在进行网络通信时如果发送方发出数据如果在一个特定时间间隔内收不到应答那么发送方就认为“我发的数据对方没有收到”就会进行数据重发 附Tcp保证可靠性一部分是通过Tcp协议报头体现出来的还有一部分是通过实现Tcp的代码逻辑实现出来的比如超时重传机制就是在发送方发出数据后开启了一个定时器这就是通过Tcp的代码逻辑体现出来的这在Tcp报头中是体现不出来的 上面的问题就是“丢包”丢包有两种情况 发送方发出去的数据直接丢了接收方没收到数据接收方收到数据了但是发给发送方的应答报文丢了 但是无论是什么情况统一规定为超时或者丢包都会进行补发但是如果补发次数多了会判定链接出问题了会申请重新建立链接所以服务器可能会收到重复报文所以服务器需要“去重”所以报文的序号还有一个重要的作用就是“去重”当发送缓冲区的数据发出去后操作系统不会立即将数据从缓冲区中删除或覆盖会暂时保存在需要时进行超时重传具体实现可以看后面的“滑动窗口” 问题这个规定的超时时间是如何设置的 解答 超时时间不能设置太长会影响整体效率也不能设置太短会造成资源浪费。 所以最理想的情况就是找到一个最小时间使“确认应答能在这个时间内返回”所以这个时间是和网络环境有关的网络好的时候重传时间可以设短一些网络卡的时候重传长一些所以重传时间是动态计算的  Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时 时间都是500ms的整数倍如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接 3.3 Tcp连接管理机制 3.3.1 操作系统对连接的管理 面向连接是Tcp可靠性的一种只有在通信建立好连接才会有各种可靠性机制而一台服务器上可能会存在很多连接此时操作系统就不得不对这些连接进行管理 操作系统在管理这些连接时需要“先描述再组织”所以在操作系统中一定有一个描述连接的结构体里面包含了关于连接的各种属性字段最后有很多个这样的结构体就通过数据结构组织起来这之后操作系统对连接的管理就变成了对该数据结构的增删查改建立连接在操作系统层面就是定义了一个结构体变量然后填充各种属性字段最后将其插入到管理连接的数据结构当中断开连接也就是将对应的结构体从数据结构中删除然后释放该连接增加占用的各种资源所以连接的建立管理和释放都是有成本的就是管理结构体的时间成本以及存储连接结构体的空间成本 3.3.2 三次握手重点 前面我们一直在说三次握手但都只是简单的概括了所以下面我们来详细了解下三次握手的过程 双方在进行网络通信前需要先建立连接这个建立连接的过程我们称之为三次握手 上面的SYN和ACK就是Tcp报头字段中的那两个标志位而对于第二次握手除了应答服务器也要主动建立连接所以服务器也要发个SYN给客户端因为Tcp协议中双方主机地位是相等的“你要和我通信那么我也要和你通信”总结Tcp是全双工的 问题为什么是三次握手而不是四次五次或者更多次 解答有两个理由 ①三次握手是验证双方通信信道的最小次数能够快速建立连接 在客户端客户端收到服务器发来的第二次握手后就说明第一次握手成功在客户端看来从我到服务器以及服务器到我的这两条通信道路没有问题此时从服务器看来客户端发给我的消息我能收但是我发给客户端的消息不知道所有就有了第三次握手于是客户端再次给服务器发ACK只要服务器收到就证明三次握手成功那么既然三次握手就已经能够验证双方通信是否正常了也就没必要在进行更多次的握手了 ②奇数次握手可以确保一般情况下握手失败的连接成本是嫁接在客户端的能保证服务器本身的稳定性 服务端维护链接是有成本的每一个连接都要被服务器消耗内存管理起来如果一次握手可以的话客户端直接给服务器发大量的报文那么服务器就要全部接着当这样的客户端数量一多服务器挂掉是迟早的事我们称为“SYN洪水”如果是两次握手绝对是服务器必须得先把链接建立好万一客户端出了问题直接挂了服务器不知道它就得一直维护这个链接维护链接是有成本的造成资源浪费。服务器是一对多的不应该把客户端出问题造成的后果嫁接到服务器上这样不好今天我们三次握手一旦第三次握手失败了那么客户端认为它把它的链接维持住了服务器认为链接没有成功这样就把问题转移到了客户端上服务器在握手失败的过程中没有付出太多的代价能保证服务器本身的稳定性 总结三次握手次数是确认全双工的最小次数 三次握手时双方的状态变化 最开始客户端和服务器都处于CLOSED状态但是服务器为了能够接收客户端发来的连接请求所以服务器变为了LISTEN监听状态客户端发起三次握手当客户端发出第一个SYN后状态变为SYN_SENT处于监听状态的服务器收到SYN后将连接放进内核等待队列中并向客户端发起第二次握手发出SYNACK后服务器状态变成SYN_RCVD当客户端收到服务器的第二次握手紧接着发送三次握手的最后一个ACK之后客户端状态变为ESTABLISHED服务器收到最后一个SYN后状态也变为ESTABLISGED 下面是一系列问题有点长但是如果理解了对三次握手的细节了解更进一步 问题sock API和三次握手的关系是什么 解答 服务器最开始是CLOSED状态然后调用listen监听函数就会进入LISTEN状态然后客户端就可以发起三次握手了此时客户端调用的套接字接口就是connect函数需要注意的是connect只负责发起三次握手三次握手底层的工作都是操作系统做的所以当connect函数返回时要么三次握手建立成功要么底层三次握手失败如果三次握手成功服务器就建立了一个描述连接的结构体但是此时这个连接还在内核的等待队列中所以服务器就需要调用accept函数将这个连接拉取到上层进行操作当服务器将获取到的连接拉上来后就可以用read/recv和write/send函数进行数据交互了 问题什么是全连接队列listen函数的第二个参数有什么用 解答我们在Tcp简单网络程序中就提到过listen的第二个参数和一个队列有关 计算机网络四 —— 简单Tcp网络程序_tcp client 收发 程序-CSDN博客现在我们就来谈谈 我们申请链接时服务器就一定会存在大量已经建立好的链接但是如果服务器的上层没有accept把链接拿到上层去此时这些连接叫做“全连接”所以操作系统就必须对这些“临时维护”的联机管理起来每一个链接都是是数据结构所以底层采用队列的形式来管理所以这个队列也类似于生产者消费者模型所以listen的第二个参数表示这个已经建立好的链接队列的最大长度这个队列叫做“全链接队列”长度为 backlog 1队列里的链接都是三次握手全部完成的链接然后等待上层accept调用但是当队列满了即使三次握手成功了服务器也无法把新连接放到队列里了。 问题什么是半连接队列 解答 那么服务器链接从SYN_RCVD状态变为ESTABUSHED是服务器收到了客户端的SYN就变状态还是服务器发出SYNACK后才会变状态呢是否变状态取决于下一个状态是否就绪所以SYN_RCVD变为下一个状态就是看下一个状态是否就绪所以当全连接队列满的时候服务器为了保证一直在SYN状态服务器就完成第二次握手之后对于客户端的第三次握手不做响应也就是客户端发ACK过来我服务器直接丢弃假如服务器接收ACK就变为ESTABUSHED状态所以前两次三次握手允许握手完成但是第三次就不准你握手完成所以服务端就一直是SYN状态但是服务器不能长时间呆在SYN状态因为客户端判定链接没成功就会一直发SYN而服务器就一直丢SYN一定时间后服务器就会把这个链接直接释放掉服务器端不会长时间维护SYN_RECV被建立连接的一方处于SYN_RECV状态我们称为半链接所以也有半连接队列它里面的节点不会长时间存在操作系统会不定时释放它的长度由内核自己决定上面的问题都统一称为客户端和服务端链接建立不一致问题本质上全连接都是由半连接变来的类似递进的关系由于半连接也有长度所以你来多少链接我都不怕怕的就是一堆非法链接占着半连接队列消耗半连接队列的长度非法链接多了其它正常的链接是连不进来的这个就是“SYN洪水”的细节“服务器繁忙请稍后再试”就是两个队列满了然后不给我访问SYN洪水就是非法链接占着队列不让正常用户访问 问题listen的第二个参数为什么不能太小也不能太大 解答 太长会导致服务器会存在有些链接来不及被上层处理但依旧需要操作系统花代价维护服务器很忙的时候没有时间从底层拿数据那么服务器本身很忙了你还要占用系统资源不做事这不合理所以第二个参数不能太长是因为没必要太长会降低系统整体效率。而且也不能没有全连接队列。故事一个餐厅很忙人很多没空闲桌子了这时候来客户了接待员说店里没位子了你去别家吃吧。这时候来一个就走一个但是这时一个桌子空了我想让外面的客人立马进来吃饭但是这个时候外面的客人全被接待员打发走了造成资源浪费所以老板会在门口摆一些凳子让客户坐着等一下等店里面有空桌子了就能让外面等的客人立马去补上位置就能保证资源最大化利用 3.3.3 四次挥手 由于双方维护连接都是需要成本的所以Tcp通信结束之后需要断开连接这个 过程我们称之为四次挥手其过程如下图按照时间轴每一个箭头都是一次挥手 问题为什么挥手是四次而不是三次挥手 解答 三次握手的第二次握手时服务器给我发的报文是SYHACK的同时设置了两个标记位这是捎带应答所以三次握手本质也是四次握手只是把第二三次的握手放在一起了。所以四次挥手的第二三次也可以放在一起变成“三次挥手”所以从朴素的角度来看挥手和握手其实是一来一回的可靠性保证双方都至少给对方发了一次消息所以原因非常朴素就是为了保证TCP协议的可靠性。握手被设置成三次是因为服务器就是要为客户端服务的服务器必须无条件同意不存在时间差上的协商必须保证服务器立马对客户端做应答。挥手被设置成四次是因为断开连接是有协商在里面的客户端要断开连接代表客户端要发的消息发完了但是服务器可能还有数据要给客户端发所以一方想断开连接另一方不想断开数据没法玩所以要想四次挥手变成三次是有巧合性的所以四次挥手的第二次和第三次一般是分开就变成了四次挥手。 四次挥手时双方主机的状态变化 在挥手前客户端和服务器都处于正常通信的ESTABLISHED状态客户端最先发起断开连接请求把FIN标志位设置的报文发给了服务器然后客户端变为FIN_WAIT_1状态服务器收到了FIN发送ACK应答给客户端同时状态变为CLOSE_WAIT状态当服务器没有数据再发给客户端时轮到服务器主动断开连接了所以服务器主动发送FIN给客户端之后状态变为LASE_ACK客户端收到了第三次挥手然后向服务器发送最后一个ACK应答之后客户端进入TIME_WAIT状态服务器收到最后一个响应报文后就彻底关闭连接也就是将该连接的结构体从数据结构中删除再释放资源变为CLOSED状态而客户端变为TIME_WAIT状态后并不会直接关闭连接而是会等待一个2MSLMaximum Segment Lifetime报文最大生存时间才会进入CLOSED状态 套接字和四次挥手之间的关系  客户端主动断开连接对应的就是客户端先调用close函数服务器同理所以一个close对应两次挥手双方都要close所以是四次挥手但是要注意的是服务器只有调用close才会使状态由CLOSE_WAIT变为LASE_ACK如果服务器忘记调用close或者由于某些错误导致close调用失败会导致系统内出现大量处于CLOSE_WAIT状态的连接会占用服务器资源是一种内存泄漏问题  问题主动发起断开连接的一方会变成TIME_WAIT状态 那么为什么要等待一段时间才会变成CLOSED状态呢 解答原因有很多我们一个一个来 ①假设客户端发送第四次挥手后直接变为CLOSED万一第四次挥手的报文丢了服务器会进入超时重传但是此时服务器无法再收到应答了这样四次挥手就会失败并且把失败的成本转移到了服务器上这样不好②一个报文被服务器发出被客户端接收中间都在网络里所以MSL就代表报文在网络里存活的最大时长。呆的最长时间就是一个MSL它也是动态的取决于网络状况所以一般等待是2个MSL发出报文和接收应答是两段路程。断开连接时网络上可能还会有报文存活着所以需要等待双方通信的数据在网络里进行消散。 总结为什么要有TIME_WAIT状态等待 让通信双方历史数据得以消散为了让服务器和客户端丢弃历史报文不让它对后续通信产生影响    让我们断开连接的四次挥手具有良好的容错性 一个报文的在网络里的最大存在时长在协议的规定里一般是两分钟一个报文的最大传送时长一般是30到60秒之间可以通过命令cat /proc/sys/net/ipv4/tcp_fin_timeout 命令来查看系统的默认等待时间 一个服务器只能有10个连接当第11个连接来的时候服务器终于不堪负重挂掉了第11个连接自然请求失败但是对前面10个连接来说服务器就变成了主动断开连接的一方服务器立马就会有10个连接处于TIME_WAIT状态带来的后果就是服务器无法立即重启重启时间取决于TIME_WAIT消失的时间大约为30 -- 60秒但是我们不允许服务器重启时间这么久所以我们需要设置套接字属性让服务器允许我们进行地址复用 int opt 1; setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt)); // 防止偶发性的服务器无法立即重启TIME_WAIT状态时立即重启不要再等了 客户端不会出现这样的问题是因为客户端每次启动用的都是随机端口但是服务器每次启动都必须绑定一个端口号而且主动断开连接的一方大部分都是客户端  3.4 流量控制 定义Tcp根据接收方的接收能力来决定发送方发送数据的速度这个机制叫做流量控制Flow Control具体的解释前面的2.2 16位窗口大小已经讲解过这里不再赘述 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 窗口大小 字段, 通过ACK端通知发送端窗口大小字段越大, 说明网络的吞吐量越高接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端发送端接受到这个窗口之后, 就会减慢自己的发送速度如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端 问题三次握手成功后第一次发送的时候怎么保证发送数量是合理的 解答不要理解三次握手只是建立链接三次握手期间双方也是交换了TCP报头了所以在三次握手期间双方已经协商了双方的接收能力 问题第三次握手的时候可以携带数据吗 解答可以的捎带应答因为前两次就已经协商好双方的接收能力了间接证明上一个问题保证可靠性的时候也提高效率 问题流量控制属于“可靠性”还是属于“效率” 解答直接上保证可靠性因为可以防止大面积丢包但是流量控制也间接地提高了效率会让正常报文不会再重传了超时重传少很多所以也间接提高了效率 四Tcp提高传输效率策略 4.1 滑动窗口 双方主机在Tcp通信时可以一次性发送多条数据这样可以将等待多个响应的时间重叠进而提高效率 滑动窗口背景  TCP允许发送方一次性能发送多个报文然后接收方再一个个应答回去如果没收到应答就会进行超时重传 -- 已经发出去暂时没有收到应答的报文要被TCP暂时保存起来所以发送方就会存在着多个已经发出去但是暂时还没收到应答的报文。那么这些已经发出去但是还没收到应答的报文会被保存到哪里呢根本不需要保存因为报文就是在缓冲区里的所以我们只需要“把缓冲区做一个简单的区域划分”即可。把缓冲区当作数组来看的话只需要有一个数组的下标就可以标记区域了所以缓冲区可以分为三部分已发送已确认已发送未确认待发送三个部分对于已发送已确认的部分这部分可被覆盖用简单的话说就是这个部分里面的数据可以从缓冲区移除或者设置成无效对于已发送未确认理解为可以发/已经发但是就是没有收到应答我们就把这部分区域叫做“滑动窗口”是发送缓冲区的一部分 问题如何理解区域划分 解答 由于区域划分可以用数组来搞所以区域大小的变化就可以用“双指针”来完成可以参考双指针算法至于滑动窗口作用两端如何移动的细节也可以参考滑动窗口算法 双指针算法——部分OJ题详解_指针oj题集-CSDN博客 滑动窗口算法——部分OJ题详解_滑动窗口 题目-CSDN博客  滑动窗口描述的就是发送方不用等待ACK一次能发送的数据最大量  问题既然有滑动窗口那么为什么不把报文一次性全部发出去而是一次发多个报文呢每一次发送本质就是一次拷贝和IO那么不是说IO次数多的话效率会降低的吗 解答滑动窗口越大意味着网络吞吐量越高同时往对方发送的数据量越大。但是滑动窗口的大小都是根据对方接收缓冲区的剩余大小来定的不能超过对方的接收能力即应答报文的窗口大小。 问题如何理解向左移动向右移动移动的时候大小会变化吗如何变化大小会保持不变吗 解答不能向左移动左侧是已经发送已经确认的只会往右滑大小根据对方的接收能力来定所以窗口大小是会动态变化的 变大变小不变 所以向右移动也有三种方式 右指针不变左指针一直移动说明对方上层一直不取数据然后就是窗口变小的情况左指针移动右指针也移动说明对方接收能力提高窗口会变大同样的如果左指针移动比右指针慢表示对方接受能力减小所以右指针移速也会动态减少窗口也会动态变小 对于滑动窗口的范围大小在代码层面上可以理解为两个指针int* start (确认序号)  和  int* end (确认序号 应答报头里的窗口大小也就是win) 所以流量控制就是通过滑动窗口实现的流量控制不仅仅是限制发送接收如果主机的接收能力非常强流量控制也可以把滑动窗口搞大使其能发送更多数据提高效率 问题如果窗口一直往右移动会不会越界 解答  Tcp对于滑动窗口的时间采用了类似“环状算法”到了结尾后通过环形算法重新计算start和end的位置从左边再次开始移动双方开始通信时序号并不是从0开始的假设我断开链接了我进入TIME_WAIT等待数据消散但是如果我断开连接后后悔了于是立马由重新建立连接但是数据可能还没有消散于是服务器对新链接可能会收到旧数据概率很低但是也有所以三次握手时也会协商序号当发送端收到对方响应时读取确认序号x和窗口大小win此时就可以将start更新为x将end更新为start win 4.2 快速重传 我们先来讲讲滑动窗口对于下面两种丢包问题做的措施 情况一数据包已经抵达ACK应答丢包 假设发6个报文100020003000400050006000假设我收到了100120014001的应答3001和5001的没收到那么滑动窗口应该是往右滑动到2001就停下来的但是序号的定义是假设我收到了6001的应答那么前面6000的报文我全部收到了所以滑动窗口会直接滑到6001的位置如果6001也丢了那么也没关系滑倒5001然后等待5001和6001超时重发即可如果全丢了呢那就全超时重发 所以发送端连续发送多个报文数据部分ACK丢包不要紧可以根据后续的ACK进行确认 情况二发送方发送的数据丢包接收方接受的数据不完整 6个报文序号是1到6000接收方只有1001 - 200的报文没收到丢包了2001后面的的全收到了那么后面所有的报文返回应答时确认序号就全填的1001表示2001的报文没收到此时发送方会受到很多相同序号的应答所以发送方有个原则当收到了3个相同序号的应答报文会立即对丢包的数据进行重发就把1001到2001重发后面的不重发了万一5001丢了也没关系直接针对5001进行重发这种策略我们叫做 ——“快重传”当发送端补发了1001-2000的数据后对方发来的确认序号就会变为6001表示1-6000的数据我全部收到了 问题已经有了快重传了为什么还有超时重传呢 解答主要是因为快重传是有条件的收到三个相同序号的应答快重传的本意是为了提高效率的而一旦数据丢失了但是我收不到对方三次重复的应答此时快重传机制就不能触发只能进行超时重传 所以超时重传也不能丢弃因为超时重传是兜底的是底线 4.3 延迟应答 客户端和服务器都有接收和发送缓冲区双方的发送缓冲区里都有滑动窗口客户端发消息给服务器服务器要给客户端应答 发送方一次发送更多的数据代表它发送的效率越高也就是一次IO往网卡里塞更多数据效率高但是发送方一次发送多少数据取决于对方告诉我它能接收更多数据如果接收方给发送方通告一个更大的窗口大小TCP报头那个发送方才能发更多数据 问题如何让接收方给发送方通告一个更大的窗口呢 解答那么就在服务器收到报文的时候我不立即发应答我等一等这个等不是上层等是TCP协议层在等在不超时的情况下我收到第二个或者第三个报文时再给客户端应答而在我等的时候上层就有较大的概率把数据取走缓冲区的剩余空间就会变大。 我们把这种收到报文不着急应答的策略叫做延迟应答是一种提高效率的方式但是不是一定提高效率如果上层一直不取数据那么效率会降低所以这种应答提高效率是有概率的。 假设接收端缓冲区为1M. 一次收到了500K的数据如果立刻应答, 返回的窗口就是500K但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M 编程推荐建议以后我们写TCP服务器的时候建议尽快把通过readrecv尽快把数据全部从内核拿上来。 4.4 捎带应答 捎带应答是Tcp通信时最常规的一种方式主机A给主机B发送了一条消息当主机B收到消息后要发送ACK但是如果此时主机B刚好要发数据那么这个ACK就可以“搭个顺风车”此时就把“数据 ACK”一起发过去了完成了数据发送又完成了ACK应答这叫做捎带应答所以捎带应答不仅能确保发送的数据被对方可靠地收到了同时也确保了捎带地ACK应答也被对方收到在保证可靠性地同时又提高了效率 五拥塞控制 发送数据如果出现问题可能是一方主机出现问题也可能是网络出了问题两种情况 出现少量丢包出现了大量丢包 场景一个班有30个人考C语言只有两个人挂科后面考网络挂了28个人所以前面考C语言可能就是我的问题后面考网络可以认为是校方问题对应的也就是客户端-服务器的问题和网络的问题。 而网络出现问题 硬件设备出问题    网络中数据吞吐量太大引起阻塞。 如果通信双方出现大量数据丢包问题Tcp就会判断网络出了问题 -- 我们叫做“网络拥塞”。    问题  如何发现是网络出了问题 解答滑动窗口内大量数据都超时了。 问题当识别到网络拥塞发送方应该怎么办呢 解答 肯定不能对报文进行超时重发因为会加重网络的拥塞而且一个网络也不仅仅是一个CS在通信会有很多个CS在通信而且网络资源本身是共享的都是用的TCP/IP协议网络拥塞影响的不是你一个主机影响的是大家所以是大家都减少对数据的发送使网络拥塞的数据快速消散你好我好大家好 -- “用TCP协议实现了多主机面对网络拥塞时的共识”可能也不是所有的发送方都能识别到网络拥塞了比如我只发两个报文那么也就丢两个报文所以不是所有主机都能立马识别到网络拥塞但是能保证一个主机一旦识别到网络拥塞就立即减少发送所以网络这个东西是搏概率的拥塞严重程度的不同也是自适应的。识别到网络拥塞时启动慢启动机制发送少量数据如果又丢了进入超时重发如果得到了应答第二次发送多一点点数据然后重复前面的操作每次重发时都发送上一次发送的两倍报文 所以我们现在就要推出一个概念拥塞窗口 每次开始的时候定义拥塞窗口大小为1每次收到ACK应答后拥塞窗口大小 * 2拥塞窗口大小是主机判断网络健康程度的指标超过拥塞窗口会引发网络拥塞否则不会。因为网络是动态的所以拥塞窗口本身也是动态的所以滑动窗口大小 min(16位窗口大小 有效数据 拥塞窗口)16位窗口大小是对方主机的接受能力但是拥塞窗口考虑的是动态的网络的接收能力通过调整拥塞窗口的大小来动态控制滑动窗口的大小让它的发送数据量按照我们的要求指数级增长叫做“慢启动” 问题指数级增长咋叫“慢启动”呢 解答 这个慢是指初始的时候慢网络出拥塞的时候发送少量的报文如果都OK那么表示网络已经趋于健康了应该尽快恢复正常通信而且为了不让后期拥塞窗口过大所以也不是单纯地让拥塞窗口加倍增长慢启动具有阈值当拥塞窗口大小超过阈值时就以线性增长不再指数增长了。而这个阈值这个数字代表最近一次发生网络拥塞时拥塞窗口的大小/2 作为慢启动“阈值”然后将拥塞窗口直接干到1从1重新开始增长 滑动窗口 接收窗口 拥塞窗口三个窗口相互配合就可以在Tcp传输的时候既考虑接收问题又考虑网络问题 六扩展 6.1 面向字节流 VS 面向数据报 写数据和读数据互不相关。数据发多少读多少没有那种我发多少你就必须收多少的那种匹配机制。UDP就是你发几次我就必须收几次所以UDP就是面向数据报发送本质就是把数据从我的发送缓冲区“拷贝”到对方的接收缓冲区但是对方数据被上层取走多少由用户决定但是发送方发送多少数据由操作系统和TCP协议决定TCP也不管你上层要发送的数据是什么在我缓冲区里它就是二进制就是字节数据TCP的任务就是保证这个数据能成功被对面接收到同时对方的TCP也只认字节数据分离报头等等工作由用户层自己做 -- TCP只管发送对发送的数据不做任何处理全由上层自己做 -- 所以TCP只有字节的概念UDP报头里面是有数据长度的但是TCP没有因为TCP的序号能保证数据段本身的按序性TCP也不区分什么报头和载荷那是上层的事情TCP只有字节流的概念你的缓冲区里什么我的缓冲区里就有什么就像家里的自来水管自来水公司只负责把水送到你家你怎么用这个水自来水公司不管。字节流也是类似的概念用户对报文进行处理必须一个一个处理需要将字节流变成一个一个完成的请求那就是应用层的事了与Tcp无关了这样也很好地进行了功能解耦 6.2 数据包粘包问题 我收到的报文有时候并不是一个完整的报文可能是半个或者一个半个报文这时候上层对报文边读边解析那么上层就可能读到半个报文的情况这时候再进行处理时会多处理或少处理请求叫做粘包问题 场景蒸包子包子可能黏在一起我拿一个包子可能拿出三个包子。 Tcp没有粘包问题它是上层的问题。要解决粘包问题就是定协议自定义协议的时候搞过 问题如何解决粘包问题 解答明确报文与报文之间的界限 采用定长报头定特殊字符作为报文的边界使用自描述字段定长报头使用自描述字段特殊字符 完成分离后再进行反序列化用户才算真正拿到的请求 6.3 Tcp连接异常情况 进程终止 链接其实和进程没有直接关系它本身和文件是直接相关的因为获取套接字其实就是获取一个文件描述符也就是打开了一个文件而我们曾经讲过文件的生命周期是随进程的所以链接间接上也是和进程相关的所以进程退出 -- 关掉文件描述符  -- 链接也进行正常的四次挥手自动断开 机器重启 如果是正常重启操作系统会在关机前干掉所有进程而这也是进程退出所以机器重启和进程终止地情况是一样地 机器断电/网线断开 当客户端正常访问服务器时客户端突然s了但是服务器在短时间内不知道所以会维持与客户端地连接但是不会一直维护因为Tcp是有保活策略的 服务器会定期检查客户端的在线情况的如果连续多次没有ACK应答服务器会自动关闭连接此外客户端也会定期向服务器“报平安”所以如果服务器一段时间内没有收到客户端的消息服务器也会关闭连接 七Tcp总结 可以看到Tcp比起Udp复杂了不止一点点因为Tcp既要保证可靠性又能尽可能提高效率 可靠性保证 检验和序列号确认应答核心超时重传连接管理流量控制拥塞控制 提高性能 滑动窗口快速重传延迟应答捎带应答  Tcp当中还设立了各种定时器 重传定时器为了控制丢失的报文和丢弃的报文也就是对报文段确认的等待时间坚持定时器专门为对方零窗口通知而设立的也就是向对方发送窗口探测的时间间隔保活定时器为了检查空闲连接的存在状态也就是向对方发送探查报文的时间间隔TIME_WAIT定时器双方在四次挥手之后主动断开连接的一方需要等待的时长 下面是一些基于Tcp的常见的应用层协议 HTTP超文本传输协议HTTPS安全数据传输协议SSH安全外壳协议Telnet远程终端协议FTP文件传输协议SMTP 电子邮件传输协议 当然也包括我们自己Tcp程序时定义的应用层协议 计算机网络五 —— 自定义协议简单网络程序-CSDN博客
http://www.hkea.cn/news/14412141/

相关文章:

  • 九江建网站报价微商营销宝最新版
  • 目前我们的网站正在建设中搜索引擎登录入口
  • 河南单位网站建设作为一个专业的网页制作人员
  • 全包胶衣网站南昌seo搜索排名
  • 手机网站适配代码深圳网站建设销售前景
  • 网站建设信息表课程网站建设
  • 网站开发有名的公司个人求职简历模板免费下载
  • 把做的网站发布打万维网上网站建设界面建议
  • 郑州前端开发培训机构seo价格是多少
  • 公司网站怎么申请怎么注册wordpress文章不显示全文
  • 国外的电商网站有哪些方面淘宝网站建设图片素材
  • 选择常州网站建设公司网页制作文字素材
  • 大连h5建站优秀国外网站
  • asp网站开发教案注册公司需要交多少税
  • 广州高端品牌网站建设后台管理便捷cms管理什么意思
  • 不用fash做的视频网站软件工程师的发展前景
  • 网站设计价格前端开发用什么软件好
  • 四川住房城乡建设厅官方网站wordpress流量统计
  • 湖北洈水水利水电建设公司网站做公司网站用哪个空间好
  • 网站建设流程范文虚拟主机代理商的网站打不开了怎么办
  • 垂直网站怎么建设用什么工具做网站视图
  • 网站需要加wordpress大连网页设计
  • 电影vip免费网站怎么做的网站建设哪家公司
  • 网站开发 京东深圳建站哪家专业
  • 网站建设制作软件扁平风格网站 模板
  • 比较网站建设专业做物业网站的公司
  • 网站登录入口大全58招聘网站官网
  • 优秀专题网站企业做网站需要注意事项
  • 黑龙江省建设厅网站站长石河子做网站的公司
  • 黑龙江省建设银行网站网站建优化