会泽住房和城乡建设局网站,珠海互联网公司,wordpress备份与还原,小羚羊网站怎么建设文章目录前置知识查看网络状态的工具查看进程idUDP协议协议格式UDP只有接收缓冲区基于UDP的应用层协议TCP协议流的理解协议格式确认应答机制缓冲区序号的作用流量控制超时重传机制6位标志位紧急数据的处理三次握手listen的第二个参数全连接和半连接队列都维护了什么信息#x…
文章目录前置知识查看网络状态的工具查看进程idUDP协议协议格式UDP只有接收缓冲区基于UDP的应用层协议TCP协议流的理解协议格式确认应答机制缓冲区序号的作用流量控制超时重传机制6位标志位紧急数据的处理三次握手listen的第二个参数全连接和半连接队列都维护了什么信息全连接和半连接的优缺点为什么半连接可以阻止SYN洪水攻击四次挥手COLSE_WAIT状态与TIME_WAIT状态滑动窗口快重传拥塞控制延迟应答捎带应答tcp异常处理前置知识
IP端口号可以确定网络中指定计算机上的指定进程传输层负责维护端口网络层负责维护IP作为传输层协议的TCP和UDP肯定包含了端口号的相关信息。而一些端口号是我们要避免使用的它们被称为知名端口号 知名端口号0~1023 非知名端口号1024~65535 一些广为人知的协议SSHHTTPFTP这些应用层协议都会使用固定的端口所以我们不能随意使用0~1023之间的端口号以免产生冲突。 ftp服务端21端口 ssh服务端22端口 telnet服务端23端口 http服务端80端口 https服务端443端口 在/etc/services目录下存储了常见协议的端口可以使用指令 cat /etc/services 了解这些端口。在0 ~ 1023之外的端口号我们就能随意使用了
查看网络状态的工具 netstat n 拒绝显示别名将别名显示成数字 l 仅列出在listen状态的程序 p 显示建立相关连接的程序名 t 仅查看tcp选项 u 仅查看udp相关选项 a 显示所有选项默认不显示listen相关 较经常使用的选项是netstat -nlpt
查看进程id
pidof 进程名查看该进程名相关的pid
UDP协议
协议格式
学习一个协议需要从两个方面入手1.基本传输单元如何封装与解包2.基本传输单元如何分用向上交付要搞清楚这两个问题就要从协议的格式入手 数据段的宽度为32b4个字节前8个字节为报头剩余字节为有效载荷。在报头中前4字节的前两个字节表示源端口后两字节表示目的端口。后4字节的前两字节表示整个数据段的长度后两字节是用来校验数据段的数据。UDP是传输层协议维护端口号的信息再正常不过了正是由于目的端口号的维护数据段的分用被很好的解决了解包得到的有效数据将交付给上层哪个上层端口号都告诉你了当然是找对应端口号的进程了这是分用。那么封装和解包呢这里先解释一下数据报的概念信封总是一封一封的邮递的没有邮递半封信的说法吧。正常情况下我给你邮10封信你也会收到10封信。每封信都有信纸隔开信和信之间有明显的界限。一次UDP数据段的发送就像信封的发送一样每次发送的数据段都是报头有效载荷。报头中有2字节的字段表示了整个数据段的大小最大为64k上层将有效数据交付给传输层时传输层只要计算有效数据的长度填入源端口和目的端口校验和字段将完整的报头添加到有效数据的首部就完成了数据段的封装。至于解包只要取出数据段的前8字节解析报头得到数据段的总长度就能精准的找到有效数据的具体范围取出有效数据交付给上层
UDP只有接收缓冲区
UDP只有接收缓冲区没有发送缓冲区可以理解为UDP数据段较小并且UDP本身就是简单的协议所以UDP直接将数据段交付给下层网络层由网络层对其进行处理。而接收UDP数据段时系统可能被抢占暂时无法直接读取数据段所以这时下层的网络层会将数据段放到UDP的接收缓冲区中达到系统能够处理这些数据时才会将数据读取。要是缓冲区满了后续的数据段将丢失并且UDP是不可靠协议所以不会进行数据段的重传。由此可见UDP协议是全双工的我们可以创建两个线程一个用UDP进行数据发送一个读取UDP接收到的数据
基于UDP的应用层协议
NFS网络文件传输协议TFTP简单文件传输协议DHCP动态主机配置协议BOOTH启动协议用于无盘设备启动DNS域名解析协议
TCP协议
流的理解
面向字节流是tcp协议的特点关于流的概念我们可以理解位水龙头中的水流水龙头的打开和关闭决定了水的流量水的流量由我们控制。tcp也是如此报头中没有指明报文长度的字段我们无法通过报头确定一次通信中tcp的字节大小所以关于两次tcp数据包的分割需要由上层协议进行具体的定制。对于面向数据报的udp上层只要调用一次read就可以像收信封一样接收一次udp报文因为udp报头中有具体的报文长度并且报文最大长度也有限制64K。但是对于面向字节流的tcp上层只能一直调用read函数不断的读取tcp报文填充接收缓冲区然后根据应用层协议其中定制了分割报文的规则将不同报文进行分割。
并且使用tcp协议发送数据时如果发送的数据太少数据会被存储在发送缓冲区中不直接发送等到数据量大的时候再一起发送但是udp不一样udp一拿到数据直接就发送了。当tcp发送的数据量太多时tcp会将其进行拆分将其拆分为多个tcp报文再发送而udp则是明确的限制了报文的长度你不能超过这个长度超了后果自负。
总结一下tcp像水流你只管流我得到的水要怎么使用是我的事要把水装成几杯取决于我。而udp像信封不需要我们考虑如何使用它的数量是确定的。拿到了信封只管把它拆开分离报头和有效载荷。所以说tcp存在粘包问题而udp不会发生粘包
协议格式 确认应答机制 确认应答机制是可靠性的一种实现 协议中有32位序列号和32位确认序列号。这一对序列就涉及到了tcp的确认应答机制如何确认发送的数据被对方接收到了或者说怎么保证通信的可靠性只要对方对这些数据作出有关的响应我们就知道对方接收到了我们的数据。比如说“你吃饭了吗”对方回答“我吃了”做出了有关的响应我们就知道了对方接收到了我们的问题。这样的确认应答机制中最后一次通信肯定无法被确认应答比如说刚才的对话对方回答“我吃了”之后我们就不再回应对方此时就无法知道我们是否听到了他的话。在这样的机制下除了最后一次通信之外的数据都是可以被确认应答的所以我们将重要的数据放到前面最后进行一次无关痛痒的通信或者采用额外的机制保证其确认应答
关于确认应答机制的具体实现tcp将每个字节的数据进行编号即序列号比如服务端发送1000字节给客户端客户端响应1001即确认序列号告知服务端我接收到了前1000字节数据希望下次接收的数据从1001字节开始。这里有一个问题为什么报头中要有序列号和确认序列号呢两者使用一个相同字段不就行了这个问题的本质是tcp是全双工的接收的同时还能发送数据比如我问“你吃了吗”你回到“我吃了你呢”这就是一个接收发送我不仅确认收到了你的信息还向你发送新的信息这样的话接收和发送就要用不同的字段进行表示所以tcp报头中有两个序列号字段
由于安全问题每次tcp通信的序列号都是随机生成的并且以字节为单位进行递增如果序列号太大导致溢出了那么序列号将会回绕从0开始继续递增
回到报头确认序列号之后是
4位的首部长度其基本单位为4字节表示报头的长度最多能表示15*4 60字节由于tcp报头长度标准报头的20字节选项的长度其中标准报头是通信中必须含有的字段所以首部长度从010120开始然后是6位的保留位不表示任何信息接着是6位的标志位URG,ACK,PSH,RST,SYN,FIN这个涉及的东西太多后面细讲先跳过
接着是窗口大小这个展开讲讲首先要明白IO类函数的本质像readwritesendtorecv。这些函数的本质不是发送和接收而是拷贝是将用户态的数据拷贝到内核态或者将内核态的数据拷贝到用户态。这里再理解下缓冲区的概念
缓冲区
和udp不同tcp拥有发送缓冲区和接收缓冲区由于tcp是传输层协议而传输层是位于操作系统中的所以tcp的缓冲区是内核缓冲区用户没有权限访问。调用write函数时write只是将我们的数据从用户空间拷贝到内核空间发送缓冲区接收函数也是如此数据到达接收缓冲区由read将其中的数据拷贝到用户空间我们才能读到这些数据。
序号的作用
回顾确认应答机制其中最核心的部分就是序号双方通过序号进行数据的确认应答。除此之外序号还有另外两个应用去重和数据的按序到达。先说去重很简单如果发送方发送了相同的数据段由于未知的原因那么接收方就可以根据序号判断接收缓冲区是否已经接收过了这个数据段如果数据段发送过了数据段位于已接收且确认应答的窗口中接收方完全可以丢弃它以此来提高效率。
接着说按序达到tcp的一大特性就是可靠性数据段的按序达到是可靠性中的一种。接收方完全可以根据每个数据段的序号对所有的数据段进行排序或者是采用其他的机制保证数据段的有序。总之有了序号不同数据段之间的相对关系也就确定了
流量控制 流量控制也是可靠性中的一种实现其保证了接收方不会因为接收缓冲区的容量限制使数据溢出出现浪费网络带宽的现象 缓冲区肯定有容量上限如果缓冲区满了数据还是不断的发送而操作系统又来不及处理这么多的数据或者说操作系统的处理速度跟不上缓冲区的接收速度这就会导致数据段的丢失由于tcp是个可靠协议数据段丢失了就要重传为了避免不必要的重传我们需要控制发送数据的速度使其能够根据对方接收缓冲区的剩余大小进行灵活的调整剩的多就发快些剩的少就发慢些。这就是16位窗口大小的作用其表示接收缓冲区剩余空间的大小双方每一次的通信都可以用16为窗口大小告知对方自己还能接收多少数据对方根据16位窗口大小及时调整自己的发送速度以充分利用接收缓冲区的空间
当然16位窗口大小也只是流量控制中的一部分滑动窗口拥塞控制会在后续讲解
超时重传机制 超时重传机制也是可靠性中的一种保证数据的完整传输将可能丢失的数据重新发送 由于某些原因导致数据段在发送过程中丢失了为保证tcp传输的可靠性tcp需要对丢失的数据进行重传。当发送方发送数据后会启动一个计时器如果在指定时间内发送没有收到对方的确认应答ACK就会认为该数据段丢失需要对其进行重发。并且指定时间会延长至之前的两倍如果在这段时间内发送方还没有收到接收方确认应答就会再次重发并且指定时间延长为上次的两倍。不断这样的重复操作直到发送方接收到了对方的确认应答才会停止。要是重发达到一定次数这时就会停止tcp连接即因为某些问题导致数据段的传输异常数据总是无法被对方接收此时要关闭双方的连接排查可能存在的问题
6位标志位
我们知道tcp是面向连接的协议要进行tcp通信首先要建立连接要关闭tcp通信肯定要关闭连接。这就意味着通信数据有不同的类型用来请求建立连接的用来关闭连接的用来正常通信的根据不同的类型我们需要对这些数据进行分类这个分类就在6位标志位中体现比如用SYN来建立连接用FIN来关闭连接用ACK对数据进行确认应答
之前我写过tcp通信模型其中的服务端主动调用read从socket的接收缓冲区中拷贝信息如果接收缓冲区中没有信息read就会陷入阻塞直到缓冲区中有信息read才会被唤醒进行数据的拷贝。我要说的是这种IO方式是很低效的在实际开发中我们很少这样使用一般都是在报头中携带PSH标志用来提示上层尽快将数据取走先不等待缓冲区其他数据的递达将已递达的数据向上交付这才是一种高效的IO方式。而一般缓冲区都会有一个最低限度只有数据量超过了这个最低限度上层才会将缓冲区中的数据拷贝走
URG: 紧急指针是否有效ACK: 确认号是否有效PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走RST: 拒绝一个连接我们把携带RST标识的称为复位报文段SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
紧急数据的处理
URG标志为1表示16位紧急指针字段有效此时它表示了从有效载荷的第一个字节开始有多少个字节为紧急数据也就是说紧急指针字段的值减一就是紧急数据最后一个字节的下标。
一般情况下很少应用程序支持或使用tcp的紧急数据所以接收方忽略URG标志将紧急数据与普通数据放在同一缓冲区一起处理如果接收方不忽略URG那么紧急数据就会被提取出来放到一个单独的缓冲区作为一种带外数据供应用程序读取让应用程序自己决定如何处理紧急数据如果接收方不忽略URG那么紧急数据就会被提取出来并且直接交付给应用程序不经过缓冲区这是一种比较危险但安全的作用因为它可能会覆盖普通数据或者使普通数据丢失但是它可以立即让应用程序收到紧急数据
三次握手 客户端向服务端发送含有SYN的报文请求与服务端同步客户端进入SYN_SENT状态。服务端接收SYN后发送SYNACK确认收到客户端的请求并与请求与客户端同步服务端发送之后进入SYN_RCVD状态。与此同时服务端会维护一个半连接队列syn queue将与客户端连接的套接字放到队列中维护起来注意是在SYNACK发送后服务端单方面的将连接放入半连接队列。客户端收到服务端的ACKSYN后发送ACK表示确认服务端的信息并进入ESTABUSHED状态。服务端接收ACK后进入ESTABUSHED状态将处于半连接状态的套接字移动到从半连接队列中移除全连接队列中accept queue至此三次握手完成。
listen的第二个参数 listen是服务端初始化的最后一步使服务端处于监听状态其中sockfd是监听套接字的文件描述符fdlisten将使该文件处于监听状态。那么第二个参数backlog呢它指的是全连接等待队列的长度在等待队列中排队的是与本地三次握手成功的套接字accept就是取出等待队列中第一个套接字。如果处于listen状态的套接字不accept的话这个等待队列肯定会满假设backlog为n等待队列可以维护的最长链接数为n 1也就是说在不accept的情况下最多只有n 1个套接字可以与本地握手成功第n 2个套接字请求将被拒绝。
准确的说全连接队列满了会发生以下的情况
服务端发送SYNACK后忽略客户端的ACK导致客户端的ACK重传这样会造成网络拥塞与带宽的浪费服务端直接发送RST拒绝与客户端的连接这样可以避免资源的浪费但这会导致客户端的不满与疑惑
等待队列的长度应该设置为多少合适
深入理解一下accept的速度取决于服务端提供服务的速度如果服务端服务的客户很多其资源大部分被占用能继续对外提供的服务就很少accept套接字的速度就慢在资源紧张的情况下只有处理完一个客户端的服务才会accept下一个连接此时全连接等待队列中的连接就是待服务的连接。如果等待队列的长度过长我们知道维护等待队列也是需要占用资源的当服务端资源紧张时肯定不能用太多的资源区维护这样一个队列要把资源尽可能的分配给客户端使用。但是等待队列的又不能太短为了保证服务端资源的充分利用我们不仅要充分使用现在的资源还要考虑将来资源的使用情况所以我们要适当的维护等待队列根据服务端的情况选择一个合适的长度充分利用服务端资源
最后补充一下半连接队列满了会发送怎样的情况
服务端忽略客户端的SYN包这会导致客户端的SYN超时重传同样是会浪费带宽和造成网络拥塞问题服务端不发送SYNACK直接发送RST显式拒绝客户端的连接这样不会产生资源的浪费但是会给客户端带来疑惑
全连接和半连接队列都维护了什么信息
半连接队列也称SYN队列是服务端用来存储已经接收客户端SYN但没有接收客户端ACK的TCP套接字。这些套接字处于SYN_RCVD状态全连接队列也称accept队列是服务端用来存储与客户端三次握手成功的TCP套接字这些套接字处于ESTABLISHED状态两者都维护了服务端与客户端的IP端口号确认号应答号它们维护的信息都是一样的
tcp只是用两个队列表示套接字的不同状态如果非要区分它们的不同
全连接队列中的套接字可以进行可靠的双向通信比如进行拥塞控制流量控制而半连接队列中的套接字只能简单的超时重传不能收发数据包全连接队列中的套接字可以被accept取走并使用半连接则不行全连接队列中的套接字可能因为异常或者对方关闭而断开并发送FIN或者RST结束会话而半连接队列中的套接字只会因为超时或者服务端主动或被动拒绝而断开并发送RST包来清除状态
关于半连接队列中的套接字断开的原因
客户端发送SYN后主动关闭套接字网络拥塞导致SYNACK丢失导致超时重传SYN洪水攻击恶意占用服务端资源影响正常半连接套接字的通信使正常套接字超时重传
而拒绝的原因有
客户端的ACK没有携带正确cookie值被服务端拒绝由于防火墙或其他安全措施客户端被服务端拒绝恶意客户端SYN洪水攻击服务端服务端主动拒绝客户端
全连接和半连接的优缺点
全连接的优点避免重复握手提高传输效率半连接的优点可以防止SYN洪水攻击两者的缺点都是队列满了的情况下会导致客户端无法与服务端建立连接即无法完成三次握手
为什么半连接可以阻止SYN洪水攻击
半连接队列有一个SYN等待时间即队列中的套接字经过这个时间没有发送ACK服务端会将该套接字移除等待队列。所以
缩短SYN等待时间在恶意服务端SYN攻击时尽可能快的释放半连接限制每个IP的最大连接数过滤掉恶意IP利用SYN cookies技术验证客户端是否想真正建立连接
关于SYN cookies技术
服务端在收到客户端的SYN请求时不分配资源给它而是根据一些信息时间戳IP地址端口号MSS计算出一个序列号。将该序列号捎带在SYNACK报文中客户端接收SYNACK后将序列号1作为确认序列号捎带在ACK报文中发送给服务端服务端再根据请求中的信息时间戳IP地址端口号MSS重新计算一个序列号将其与客户端确认序列号进行对比如果两者是对应的。说明客户端合法此时再保存客户端的信息分配资源并创建连接维护全连接队列
所以SYN cookies使得服务端不用为半连接分配资源验证客户端合法之后才建立连接
四次挥手
主动断开连接的一方发送FIN报文进行单方面的断开连接被动断开连接的一方以下简称“主动方”和“被动方”发送ACK报文对FIN进行响应。要注意的是此时断开的连接只是单方面的。比如服务端发送FIN客户端返回ACK此时说明服务端不再向客户端发送数据服务端没有了向客户端发送数据的需求但客户端依然可以向服务端发送数据。所以客户端也要发送FIN到服务端服务端返回ACK确认之后四次挥手才算完成
COLSE_WAIT状态与TIME_WAIT状态
主动方发送FIN后会处于FIN_WAIT_1状态被动方接收到FIN请求后会进行ACK响应并进入CLOSE_WAIT状态。之后如果被动方不关闭用来通信的套接字文件不发送FIN给对方会一直处于CLOSE_WAIT状态这是一种资源泄漏。主动方接收到被动方的ACK后进入FIN_WAIT_2状态之后不再主动发送数据给对方。当被动方关闭连接时也会发送FIN给主动方并进入LAST_ACK状态可以理解为退出CLOSE_WAIT状态主动方接收后会响应ACK并进入TIME_WAIT状态经过一段时间后才进入CLOSED状态被动方接收ACK后直接进入CLOSED状态但是四次挥手还未完成只有双方都进入CLOSED状态四次挥手才算完成。所以为什么主动方在进入TIME_WAIT状态后为什么要经过一段时间才进入CLOSED不直接进入CLOSED为什么要有TIME_WAIT状态存在一段时间具体是多长
被动方发送FIN使主动方进入TIME_WAIT状态并使其响应ACK报文但是ACK报文可能丢失此时被动方并没有进入CLOSED状态如果主动方直接进入CLOSED状态完全的断开连接被动方的状态将永远阻塞在LAST_ACK此时连接没有完全关闭被动方没有进入CLOSED并且永远不会进入四次挥手失败。
所以被动方在发送FIN后的一段时间内如果没有收到主动方的ACK就可以认为ACK包丢失此时应当重新发送FIN使主动方再次发送ACK如果主动方进入了CLOSED被动方无法重新发送FIN并在成功接收ACK的情况下使自己进入CLOSED状态。所以主动方需要维护一个TIME_WAIT状态接收来自被动方重新发送的FIN请求考虑自己发送的ACK包丢失的情况也是最后一次通信的可靠性保证。一般情况下TMIE_WAIT状态会持续2MSLmaximum segment lifetime报文最大生存时间超过这个时间报文将被丢弃超过2MSL主动方将进入CLOSED状态默认被动方接收到了ACK早已进入CLOSED状态此时四次挥手彻底完成。
那么为什么TIME_WAIT的时间是2MSL呢MSL是一个极端情况假设主动方的ACK花费了将近MSL时间到达被动方但是报文却在MSL的最后丢失因为时间超过了MSL被动方还没有接收到ACK所以它会重新发送一次FIN请求假设这个请求又花费了MSL时间才到达主动方也就是说考虑了两次通信的最坏情况主动方需要2MSL才能收到被动方重新发送的FIN请求。接收到被动方的FIN请求主动方会重置TIME_WAIT状态的计时并重新发送ACK报文直到TIME_WAIT状态中没有收到来自被动方的FIN主动方才能确认被动方收到我的ACK并进入了CLOSED状态此时主动方才进入CLOSED状态。
也就是说维护TIME_WAIT状态是为了保证tcp通信中第四次挥手的可靠性确保被动方能接收到主动方最后一次发送的ACK报文
滑动窗口 滑动窗口是tcp可靠性机制的又一实现为了完成流量控制而设计的滑动窗口并且它会增加了tcp的传输效率 可以将发送缓冲区想象成一个字节数组每个字节的数据都有属于它们的编号按字节编号在确认应答机制中双方可能这样通信发送方发送一个数据段等待一次应答确认应答后进行下一次数据的发送再等待应答…虽然一个一个的发送数据段虽然保证了传输的可靠性但是这样传输极不合理因为传输效率是很低的。为在保证可靠性的条件下提高传输的效率tcp一次传输要发送多条数据段不再串行式的等待 图片来自网络下面是滑动窗口的具体介绍
滑动窗口是缓冲区中的一部分数据这里以发送缓冲区为例。滑动窗口中的数据段都是等待对方应答的可以分为已发送但等待ACK和待发送但等待ACK两个部分。其左边是已发送且确认应答的数据段右边是待发送且未等待对方ACK的数据段。这样的划分有些绕不好理解可以这样理解
滑动窗口经过的区域左边都是已经发送且对方确认接收也就是没有丢包不需要重发的数据段发送方不再关心可以被清理。滑动窗口未经过的区域右边都是等待发送的数据段连发送都没发送所以肯定没有被确认接收这部分数据不能清理。滑动窗口中的数据都是已发送但等待应答的数据至于说其中为什么会存在待发送但等待应答这一概念我是这样认为的在窗口滑动的过程中原窗口右边等待发送的数据段的数据段被装入了窗口所以产生了一小段的待发送的数据段这些数据段进入了滑动窗口操作系统一看滑动窗口来了新的数据但是还没有发送为了保证窗口中的数据都是已发送状态的所以就会将其打包成数据段进行发送但是这个过程明显是需要时间的在这些数据未被发送之前它们的属性就是未发送或者说将要被发送但等待接收的。所以说滑动窗口的滑动是一个特殊的时间点窗口的不断滑动产生了一些特殊的数据
至于发送窗口中的已发送和未发送的两个数据段的分界操作系统中肯定是有字段维护的。滑动窗口是怎样滑动的呢
假设发送的数据段为32~ 3738~ 4142~ 45接收方返回的ACK报文中显示的最大序号是42就表明32~ 3738~ 41这两个数据段编号之前的数据段已经收到希望发送方下次从42编号开始发送数据。此时滑动窗口会向右滑动41 - 32 1 10编号的大小。当然了接收方的ACK报文中还有字段表明了接收缓冲区的剩余窗口大小滑动窗口的滑动距离会被接收缓冲区的剩余空间所限制这是流量控制。
快重传 快重传是提供传输效率的一种方式 至于说传输过程中丢包的情况如果发送的数据包都被接收了但是有些ACK包丢了此时发送方会根据接收方传回的ACK包中最大的编号来确定哪些数据段是已经被确认的。总不可能每个ACK包都丢失吧只要通过收到的ACK中最大的编号发送方就得知该编号之前的所有数据都被接收了这是因为ACK中的编号表示希望发送方下次从这个编号开始发送数据段换言之就是编号之前的数据都被接收了。
那么数据段直接丢失了呢如果发送方收到的ACK报文中的编号总是同一个就说明该编号代表的数据段丢失了发送方将重新发送这个数据段。可以这样做的原因是在大量tcp数据段的发送过程中其中一个数据段丢失了但后续的数据段被成功地接收那么接收方就会发现数据段的不连续之后发送的ACK包将表明该数据段的丢失希望对方重新发送。如果接收方成功接收到的数据段越多指向同一编号的ACK包就会越多发送方可以根据这一点判断是否有数据段丢失
这样的重传机制被称为高速重发机制也称为快重传。与超时重传机制的不同是快重传的重传发生在MSL中而超时重传的重传是在MSL后显然超时重传的速度更慢这也是高速重发与快重传的名字由来
这里抽象理解一下滑动窗口的滑动过程假设用start和end维护窗口的最左边的最右边当接收到ACK报文将其解析得到下次传输要发送的数据段编号直接让start等于这个编号这样左边界就滑过了已经被确认应答的数据指向了未被应答的数据这个编号之后的数据段都是已经发送但等待应答或者是等待发送的了。end呢根据ACK返回的接收缓冲区剩余大小end 剩余大小如果剩余大小为0说明接收缓冲区满了此时end 0右边界不再滑动
拥塞控制 拥塞控制是tcp可靠性的一种方式也是提高传输效率的一种方式 以上的讨论只是理想情况是建立在网络总是能传输数据的前提下假设发送了100个数据段只丢失了2个数据段这是正常情况。但是丢失了98个数据段就不是正常情况了。如果发生了严重的丢包极有可能是网络出现了拥塞问题使用网络的客户端在同一时间发送了大量的数据段导致网络无法及时处理这些数据网络陷入了瘫痪暂时没有发送数据的能力大部分数据段被丢弃。
所以网络传输不仅要考虑对方的接收能力还需要考虑网络的接收能力tcp关于网络接收能力的控制我们称为拥塞控制。当网络出现了拥塞网络中的客户端不能再发送同样多的数据不能进行重传应当减少发送的数据量这里就引入了慢启动机制与拥塞窗口的概念。tcp传输开始时拥塞窗口的大小为1发送方每次发送的数据量为min(拥塞窗口的大小接收方的剩余窗口大小)每收到一次ACK应答拥塞窗口的大小都会乘以2所以随着不断收到的ACK拥塞窗口的大小将会指数型增长。但是拥塞窗口有一个阈值超过了这个阈值窗口的增长不再是指数型增长而是线性增长因为指数型增长到了后期速度会非常恐怖这很可能导致网络拥塞的出现为了防止可能的拥塞窗口的增加速度需要放慢所以后期是线性增长。
当网络出现拥塞拥塞窗口大小的阈值将减小一半并且被重置为1重新开始增长 图片来自网络
总结一下为了保证最大的效率只能使每次传输的数据量尽可能的多但是网络的吞吐量是有上限的这与处于同一网络下的主机数量有关系为了防止网络出现瘫痪减少拥塞现象的发生tcp采用慢启动方式引入了拥塞控制不断的试探网络崩溃的临界值给网络最大的压力却不使它崩溃从而让它传输最多的数据
有一个形象的比喻拥塞控制就好像热恋
延迟应答 延迟应答是提高通信效率的一种方式 要想提高数据传输的效率就要使每次发送的数据量尽可能的大但是发送方所发送的数据量是受到接收方的剩余窗口大小影响的。一般情况下如果接收方剩余窗口大小越大发送方能发送的数据也就越多。
如果这样进行ACK应答接收方每次收到报文都马上返回ACK告知发送方自己的剩余空间大小。假设接收完报文剩余窗口大小为500K但是上层处理的速度非常快可能10ns就将数据取走了此时的剩余窗口大小为1M如果现在接收的数据长度为1M就可以充分利用缓冲区了。但是ACK报文立马返回了500K下次接收到的数据最大也只是500K此时缓冲区的空间就被闲置了。
所以为了提高传输的效率tcp引入了延时应答机制即接收到报文时不要马上返回ACK报文而是延迟一段时间当然了不能延迟MSL那么长的时间才进行ACK。当然了每次接收报文都延时应答也有些不合理所以延时应答遵循以下两种限制中的一种 数量限制每隔N个数据段就延时应答一次 时间限制每隔一段时间就延时应答一次 操作系统不同限制的条件就会有差异一般情况下N取2时间取200ms。并且大多数操作系统都采用数量限制即每隔2个数据段就延时应答一次。最后延时应答是一种提高传输效率的机制但是这个机制也有不合理的地方所以它有一些限制使它尽可能的合理
总结一下上层处理数据的速度很快适当的延长ACK响应的时间可以充分利用接收缓冲区提供传输的效率但是只有在满足特定条件的情况下才会延迟ACK响应
捎带应答 捎带应答也是提高传输效率的一种机制 即接收方的ACK报文可以携带其他信息。如果每次ACK都只是单纯的ACK当我们需要传输非ACK的数据时必须要与ACK分开发送吗显然这样的做法并不合理所以接收方在发送ACK报文时可以捎带其他的非ACK数据ACK就只是报文中的一个比特位甚至可以说是正常的通信数据中捎带了ACK。最典型的例子就是四次挥手变成三次挥手如果接收方接收到对方的FIN后也希望和对方FIN此时就不需要先发送一个ACK再发送一个FIN而是ACKFIN一起发送将两次挥手合并为一次
tcp异常处理
tcp通信时可能突然出现一些特殊状况比如突然断电网络突然无法连接电脑突然死机这些都可能导致tcp出现异常。这些异常的具体表现是数据段发送后没有及时得到响应或者数据段压根没有发送出去这可能在三次握手四次挥手或者正常通信中发送。除此之外还有一些异常比如接收了无效数据或者RST报文以下是new bing的具体归纳 这里我再总结一下可能发生的异常
三次握手和四次挥手中如果一方陷入异常另一方就会启动超时重传机制它又不知道对方怎么了是否具有重新发送数据段的能力只会认定数据报丢失若重传次数达到上限另一方会强制关闭这个连接并报告错误。处于半连接状态的tcp连接一方直接发送数据给另一方接收方会认为这样的通信是非法的所以发送RST断开与对方的连接在任何时候接收到无效数据这时接收方会自动忽略并且返回一个描述自己当前状态信息的数据段给对方在任何时候接收到RST
当然了还有一种“异常”是程序突然被终止或者说进程被终止。此时操作系统会释放进程打开的所有文件其中包括网络套接字文件。关闭套接字时系统默认会为我们进行四次挥手这与正常的关闭没什么区别。 如果你看到这了这是一些问题检测你的tcp掌握情况
tcp的三次握手
全连接与半连接具体维护了什么有什么不同listen的第二个参数全连接等待队列满了将发生什么情况三次握手是怎么交换彼此的窗口大小与其实序列号的
tcp的6位标志位
为什么read阻塞读取很低效PSH是怎样实现的URG是怎样实现的缓冲区的最低限度是指什么RST清除谁的状态