1.0 UDP
udp 基本特点
- 无连接,不可靠传输,面向数据报,全双工(一个socket既能接收又能发送)
- UDP 包含 UDP报头 和 UDP 载荷(完整的应用层数据)
报头协议端格式
- 这个图画的并不准确,更准确的是拼成一个长条
- 一次通信涉及5元组, 源端口, 目的端口, 源ip, 目的ip 协议类型
- 源端口: 16bit==2字节 , 发件人的端口, 无符号 216 -1 ,所以端口号的有效范围是0- 65545 不能更大了, 实际上0一般不使用, 1-1024 这个范围的端口号,系统赋予了特定的含义(知名端口号 known port),一般不建议使用,是给一些名气大的服务器提前安排好的 如22 ssh 21ftp 80 http 443 https mysql默认3306 ,你可以让你的程序使用024端口号前的端口号,只要你有管理员权限
- 目的端口 16bit==2字节
- UDP报文长度 16bit==2字节 用来描述后面跟的载荷(数据)的长度 216-1 == 64Kb(bit不是Byte),一个udp数据报最多就是64kb长度(非常小), 如果超了,就用多个udp数据报传输
- UDP检验和(check sum) 16bit==2字节 ,用于检查数据是否在网络传输中出错了, 实际的校验和,是根据数据的内容来生成的,让数据内容改变的时候,就能感知到
- 运用了 CRC 校验算法(也叫循环冗余校验和,发送方把udp数据报每个字节累加,累加结果保存在2个字节的变量中,就算溢出也无所谓,所有字节加完后就是校验和,接收方收到数据报也全加一次和旧的校验和比较,一定能证明不同,但是不能证明相同的时候一定为真), 网络传输本质是 光信号/电信号/电磁波 因此传输容易错误
基于UDP的应用层协议
- NFS:网络文件系统
- TFTP:简单文件传输协议
- DHCP:动态主机配置协议
- BOOTP:启动协议(用于无盘设备启动)
- DNS:域名解析协议
当然,也包括你自己写UDP程序时自定义的应用层协议。
缓冲区
UDP只有接收缓冲区,没有发送缓冲区:
- UDP没有真正意义上的 发送缓冲区。发送的数据会直接交给内核,由内核将数据传给网络层协议 进行后续的传输动作;
- UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一 致;如果缓冲区满了,再到达的UDP数据就会被丢弃;
2.0 TCP
TCP,即Transmission Control Protocol,传输控制协议。人如其名,要对数据的传输进行一个详细的 控制。
2.1 TCP协议段格式
-
这个图画的并不准确,更准确的是拼成一个长条的字符串
-
**源/目的端口号:**表示数据是从哪个进程来,到哪个进程去 (端口号属于传输层的概念)
-
32位序号:由于 TCP 是面向字节流的, 不是按照" 条" 为单位来传输, 而是按字节流, 所以要对字节进行编号,由于传输数据是长条的串行数据,所以只要知道传输的这段数据字节的开始编号,以及数据的长度,则每个字节的编号也就知道了,然而在tcp当中,把这一串字节的第一个字节的编号表示出来,再结合报文长度,则每个字节的编号就确定了,这便是TCP报文中的32位序号 — 报文第一个字节的编号,如果超出了32位(4字节,表示的数据范围为42亿九千万,大概4GB),则从头开始算,相当于循环
-
32位确认序号 : 应当方,收到传输过来数据字节的最后一个字节的编号 +1,表示已经收到前多少个字节了,规定传送方传送从编号+1开始的字节,可能你会好奇TCP报文中没有长度这个填写位, 其本质原因是感知载荷长度是在ip协议的报头中, 所以 IP的载荷长度 -TCP的报头长度 == TCP载荷长度
-
**4位TCP首部长度:**这个是用来表示TCP报头有多长的 ,TCP报头是变长的, 单位是4字节 ,4位首部长度有4位(0-15) ,表示该TCP头部有多少个32位bit(有多少个4字节), 所以TCP报头最大长度是 15 * 4 = 60 字节 ,TCP报头的前20个字节是固定的(TCP报头最短长度,20字节)
-
选项: 选项部分可有可无,可以有一个,也可以有多个,应为4位首部长度是变长的,所以要通过确认4位首位长度来确认选项报头到哪里结束,载荷数据在哪里开始
-
保留(6位): 现在虽然不用,先占个位置,后面可能需要用(但tcp这么多年还没用过),给未来扩展和升级
-
**6个标志位(途中URG那个)**用来确认是普通报文还是应答报文
- URG:紧急指针是否有效
- ACK:(acknowledge 应答报文) 确认号是否有效(为0表示这是普通报文,则只有32位序号有效, 如果为1,则 表示是一个应答报文, 这个报文的序号 和 确认序号都是有效的)
- PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST:对方要求重新建立连接,为1的话代表复位报文,此时想要重置TCP; 我们把携带RST标识的称为复位报文段
- SYN:申请请求建立连接;我们把携带SYN标识的称为同步报文段 ,如果为1就是一台主机尝试与另一台主机建立连接
- FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
-
16位窗口大小: 这个字段只对ack报文有意义,这个数字就表示了当前接收方缓冲区剩余空间大小,这个数字返回给发送方,就可以作为发送方下一轮发送的参考依据 (16位并不代表窗口最大为64kb,应为tcp协议还有选项这一栏,选项里面有窗口大小扩展因子,实际的窗口大小是 16窗口大小+扩展因子<<(移位) )
-
16位校验和:
-
16位紧急指针:
-
40字节头部选项:
2.2 TCP完整的标准设定
哪里能看到TCP完整的标准设定? ---- RSC标准文档中,UDP同理
2.3 TCP的缓冲区
- tcp会在内核当中给每个socket 对象都安排一个内存空间,相当于一个队列,我们也称为**“接收缓冲区”**,我们收到的数据都会被放到接收缓冲区里,并且按照序号排列好顺序
- 这个队列也是生产者消费者,接收方网卡把数据放到socket的接收缓冲区中(内核中),应用程序调用read ,就是从这个接收缓冲区消费数据,当数据被read走了,默认就可以从队列中删除掉了
- 解决数据传输先后问题: 且有了这个缓冲区队列,也不必考虑数据传输的先后顺序,随便传过去会在缓冲区队列排好队,再依次被应用程序读出来).
2.4 可靠传输机制
概念
- 可靠是传输稳定等概念
- 传输安全是是否加密,容易被截获等问题
确认应答机制
概念
-
你发个短信给对方,对方回了你一个讯息表示收到了(这就成功),如果没回答(就是失败),这就是确认应答机制
-
确认应答机制这是保证"可靠性" 最核心的机制, 而不是三次握手
后发现至问题:
- 后发先至,当连续发多条数据的时候,这多条数据,可能会出现"后发先至"的情况(数据报A先先于数据报B,但是数据报B先到达)
**出现后发限制的原因: **
- 网络上从A->B的路劲有很多,不能保证先走的A报 和 后走的B报 走的路线相同,且转发过程也可能不同 比如经过的路由器.交换机 性能/繁忙程度 可能不同,从而导致后发先至
如何解决后发先至
- 给每个数据字节引入序列号,应答报文也引入序列号,确认谁在谁前面,且由于有按序号排序的缓冲区队列,因此可以保证拿出来的数据的顺序是正确的
- 传输数据: 由于 TCP 是面向字节流的, 不是按照" 条" 为单位来传输, 而是按字节流, 所以要对字节进行编号,由于传输数据是长条的串行数据,所以只要知道传输的这段数据字节的开始编号,以及数据的长度,则每个字节的编号也就知道了,然而在tcp当中,把这一串字节的第一个字节的编号表示出来,再结合报文长度,则每个字节的编号就确定了,这便是TCP报文中的32位序号 — 报文中字节的编号
- 应答报文 也是要和收到的数据的序列号相关联的,但不是"相等"的 ,比如传输数据给了500-1000的字节流数据, 那应答报文回应的序列号 可能是1001, 代表着1001之前的数据都都收到了
超时重传机制
概念
- 本质为丢包的情况
- 丢包在网络上很可能出现, 发了一个数据 , 然后丢了. 本质原因是 交换设备–路由器/交换机 设备过于繁忙,后面来的数据等太久就丢了
- 必须要传输数据等待超过了一定时间之后,在进行重传
两种情况
- 传输数据 丢包 (发送方压根没发出去)
- 应答数据 丢包 (接收方接收到了,但是发送方不知道自己发送成功)
解决方案:
-
如何保证数据不重复:,直接尝试重传,重传时接收方不能读取现有的数据(read方法会导致阻塞),接收方接收到数据之后,需要对数据进行去重,把重复的数据丢弃,保证应用程序调用inputStream.read 的时候,读到的不会出现重复
-
如何高效的判定当前收到的数据是否是重复: 可以使用tcp的序号作为判断依据,tcp会在内核当中给每个socket 对象都安排一个内存空间,相当于一个队列,我们也称为**“接收缓冲区”**,我们收到的数据都会被放到接收缓冲区里,并且按照序号排列好顺序
- 这个队列也是生产者消费者,接收方网卡把数据放到socket的接收缓冲区中(内核中),应用程序调用read ,就是从这个接收缓冲区消费数据,当数据被read走了,默认就可以从队列中删除掉了
- 且有了这个缓冲区队列,也不必考虑数据传输的先后顺序,随便传过去会在缓冲区队列排好队,再依次被应用程序读出来).
-
超时的时间范围的大小的影响
- 最理想的情况下,找到一个最小的时间,保证 “确认应答一定能在这个时间内返回”。
- 但是这个时间的长短,随着网络环境的不同,是有差异的
- 如果超时时间设的太长,会影响整体的重传效率;
- 如果超时时间设的太短,有可能会频繁发送重复的包;
- 当重传次数达到一定程度,也就放弃,此时会尝试重新连接(tcp的复位报文)
-
如何动态规划最大超时时间:
-
Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定 超时重发的超时时间都是500ms的整数倍。
-
如果重发一次之后,仍然得不到应答,等待 2*500ms 后再进行重传。
-
如果仍然得不到应答,等待 4*500ms 进行重传。依次类推,以指数形式递增。
-
如果仍然得不到应答,等待 4*500ms 进行重传。依次类推,以指数形式递增。
-
超时时间不是一个固定值,会随着超时轮次增加而进一步增加(应为几次都没传输成功说明当前网络本身的丢包率已经级高了,网络怕是遇到一些比较严重的问题了此时如果不提高超时时间间隔,则会导致频繁传输,拉长时间也是子啊给网络恢复六有一个时间上的余地)
连接管理机制 – 三次握手
概念
- 建立连接 — 三次握手
- 断开连接 — 四次挥手
- 在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接
三次握手简单过程
- 握手: handshake 本质就是一次网络通讯,发一个打没携带任何业务的打招呼数据,使用打招呼来触发"特定场景"
- 建立连接的条件: A机器想与B机器建立内核的连接,就需要"三次"这样的打招呼的数据交互
- 三次握手过程如下 :
- A给B发送我想和你建立连接**(发送带SYN的数据报)**
- B给A发送应答报文收到,并给A发送我也想跟你建立连接 (这里会合并到一起为1次), 合并的好处: 发一个报只用封装一次,而没合并则需要封装两次你的数据报— 即节省了封装和分用的过程,降低了成本,提高了效率
- A发送应答报文 表示收到
- java客户端创建socket的时候就会完成三次握手,三次握手是内核完成的工作.应用程序这里无法干预,同时服务器这边争对三次握手配合,是不需要涉及到任何的应用层代码的,只要你这个进程绑定了对应的tcp端口,就可以在内核中自动的配合完成三次握手,服务器的accept是在三次握手完成之后,客户端和服务器都行了"连接"后,从连接队列中取出队首元素,进一步获取其中的socket对象,来和对端通信
三次握手服务器状态转化
- [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接;
- [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列 中,并向客户端发送SYN确认报文。
- [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状 态,可以进行读写数据了。
- [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束 报文段,服务器返回确认报文段并进入CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前 的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入 LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
- [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接。
三次握手客户端状态转化
- [CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段;
- [SYN_SENT -> ESTABLISHED] connect调用成功,则进入ESTABLISHED状态,开始读写数 据;
- [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时进 入FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2, 开始等待服务器的结束报文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发 出LAST_ACK;
- [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life,报文最大生存时 间)的时间,才会进入CLOSED状态。
三次握手的意义
- tcp要想保证可靠传输,可靠传输的前提,是网络路径通畅
- tcp 三次握手, 就是要验证网络通信是否通畅工行,以及验证每个主机的发送能力(麦克风)和接收能力(耳机)是否正常,并且把能否通信的信息同步给了双发
- 三次握手还能起到"消息协商"这样的效果,通信的时候涉及到一些参数,需要客户端和服务器双方保持一致的,通过协商,来确定参数具体是多少
- 比如双方序号从几开始(一般不从0/1 开始),这样做为了保证两次连接,消息的序号能够有较大的差异,从而好去判定出某个消息是否属于这个连接
连接管理机制 – 四次挥手
概念
- 建立连接 — 三次握手
- 断开连接 — 四次挥手
- 在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接
四次挥手简单过程
- 三次握手,必然是客户端主动发起的,四次挥手不一定,服务器也可以主动发起,不过大多数是客户端主动的
- 客户端向服务器发起一个带FIN(结束报文段)的数据报文段
- 服务端会应答一个含ACK的数据报,并且再发一个带FIN的数据包文段给客户端,这里发的两次不一定能合并,应为FIN的发送是通过应用程序代码socket.close()触发的或者进程结束触发的 FIN,而不是内核触发的,相比之下ACK则是由内核控制的,收到FIN就可以立马返回ACK,如果socket.close()触发的快则可能和上一个ack合并,如果socket.close()触发的慢,此时就无法和上一个ack合并
- 客户端又会向服务器发送含ACK的报文段,但是这个ack报文段也可能丢到,但如果客户端发完这个报文立马释放资源,那服务端就会无法重传FIN报文导致无法释放资源,因此最后一次挥手(客户端发ACK时要等一段时间(2*MSL),对方没有重传FIN则代表对方以收到ACK就代表四次挥手结束)
- 经过上述四个步骤之后,连接就彻底不再使用了,双方就可以把各自保存对端消息的空间释放了
存在的问题
- close成功,四次挥手正常结束,如果服务器始终进行close成功,此时的TCP状态就处于CLOSE_WAIT , 只有当close进行完服务器就会变成LAST_ACK 的状态
- 站在服务器的角度,虽然这里没有直接关闭,但是这个连接其实已经不能正常使用了
- 争对当前socket 进行读操作 如果数据没读完(缓冲区还有数据 )则还能读到数据,如果数据读完了(缓冲区没有数据了),此时就会读到一个eof,(对于自己留来说就会返回一个-1,如果scanner, hasNext就会为false)
- 争对当前socket 进行写操作,则会触发异常
- close失败,没四次挥手完异常结束,如果服务器始终不进行close,此时的TCP状态就处于CLOSE_WAIT
- 此时站在客户端的角度,这边迟迟收不到对方的 FIN , 也会进行等待超时重传,如果重传多次任然失败或者一直等不到,就会单方面放弃连接(客户端会直接把自己保存的对端信息释放)
- 这样即使对方释放失败或者没释放,另一方也会主动释放
四次挥手意义
- 连接通信后,通信双方会在各自内存中保存对端的相关信息,如果不需要连接了,就要及时的释放上述存储的空间
TTL 和 MSL
- 两者都是描述数据报在网络上存在多少时间
- MSL是一个理论数据报在网络上存在多少时间, 单位是 s/ms ,每个报都一样的最大时间情况
- TTL 是IP协议涉及到的描述数据报在网络上存在多少时间(每个包都不一定一样),时间单位是**“次”**(路由器上的转发次数),对每个报都会设置不同的存活时间
流量控制机制(基于滑动窗口)
流量控制是考虑接收方的能力
概念
- 接收端处理数据的速度是有限的(其实是一个生产者消费者模型,接收端会有个缓冲区队列接收发送端发的数据报,再一次read读出来)。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发 送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
- 因此TCP支持根据接收端的处理能力(用接收缓存区的剩余空间大小,来作为衡量指标),来决定发送端的发送速度。相当于给滑动窗口机制踩一下刹车,这个机制就叫做流量控制**(Flow Control)**
- 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段,通过ACK端通知发 送端;
- 窗口大小字段越大,说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接受到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一 个窗口探测数据段,使接收端把窗口大小告诉发送端。
过程
- 当接收端返回ack报文的窗口大小是0,代表着缓冲区满了
- 发送端不知道接收方会满到什么时候,因此会使用 窗口探测 机制, 会周期性的发送"窗口探测包"(不会携带任何数据), 用于触发ACK 来查询接收端的接收缓存区大小
拥塞控制机制
拥塞控制是考虑中间转发节点的能力
概念
- 与流量控制配合
- 虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大 量的数据,仍然可能引发问题,
- 因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。比如: 由A 发送 数据 到 B,中间会有多次 转发,如果中间某个点的转发能力很差,那么A的发送速度阈值就不应该超过这个节点 (木桶效应), 因此在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。但我们不应该对中间设备的转发能力进行量化,而是采取"实验"的方式,动态调整,从而产生出一个合适的窗口大小
- TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传 输数据;
拥塞窗口控制的流程
- 慢启动: 刚开始进行通信的时候,会使用一个非常小的窗口,试试网络状况
- 指数增长:在传输通畅的过程中,拥塞窗口就会指数增长(*2)
- 线性增长: 指数增长当拥塞窗口达到一个阈值(慢启动阈值)的时候,就会从指数增长转换成线性增长(+n 常熟级)
- 拥塞窗口回归小窗口: 当线性增长增着增着发现传输出现丢包,则认为当前网络出现拥堵,此时就会把窗口调整成慢启动的小窗口,然后继续 再慢启动+指数增长+线性增长 另外此处会根据当前出现丢包的窗孔大小,调整阈值(拿出现网络丢包的窗口大小/2 )(指数增长到线性增长到的最大值)
拥塞窗口意义:
- 可以非常好的适应多变的网络环境
实际发送方的窗口:
- 实际发送方的窗口 = min(拥塞窗口,流量控制窗口)
2.5 保证效率机制
滑动窗口
概念
- 提高传输效率(更准确的说,是让TCP 在可靠传输的前提下,效率不要太拉跨)
- 使用滑动窗口能提升一定的效率(缩短不是没有,任然需要花时间等,传输效率依旧没有UDP高)
滑动窗的实现
- 对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送 下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
- 既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多 个段的等待时间重叠在一起了,不需要多次等待ACK,如图相当于使用"一次等待时间" 等4个ack,但这里不代表着是发四收1个ack,而是发四个收到一个立马再发下一个,保持窗口为4个(其实根算法中的滑动窗口类似))。
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000 个字节(四个段)。
- 发送前四个段的时候,不需要等待任何ACK,直接发送;
- 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
- 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应 答;只有确认应答过的数据,才能从缓冲区删掉;
- 窗口越大,则网络的吞吐率就越高;
存在的问题一
-
数据包已经抵达,ACK被丢了。
-
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认(比如1001 数据报丢失, 但是后续又有 1002的数据报发送证明 对方已经收到了1001 不然也不会发送1002, 就可以确认虽然 ACK丢了,但是对方是接收到了我发的数据报的)
存在的问题二:
- 数据包就直接丢了。
当某一段报文段丢失之后(假如发送端给服务端的1001数据报丢失了),发送端会一直收到 1001 这样的ACK,就像是在提醒发送端 “我想 要的是 1001” 一样;
如果发送端主机连续三次收到了同样一个 “1001” 这样的应答,就会将对应的数据 1001 - 2000 重新发送;
这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端 其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中,返回ACK为7001就是告诉发送端,我现在需要的是7000以后的数据了;
- 这种机制被称为 “高速重发控制”(也叫 “快重传”), 应为只是把丢的数据报重传,没丢的存在了缓冲区,并不是从丢的数据报开始重新往下传,而是只重传了丢失的----- 利用了滑动窗口 + 超时重传机制产生的变形操作,但本质还是超时重传。
如果你通信双方大规模传输数据,肯定是滑动窗口 (此时按照快速重传来操作)
如果通信双方传输的数据规模小 ,这个时候就不会滑动窗口了 (按照普通的超时重传)
延迟应答(基于滑动窗口)
概念:
-
基于滑动窗口的提高传输效率的机制
-
本质是让让下一次的窗口尽可能大
-
返回ack的时候,拖延一点时间,利用拖延的这一点时间,就可以给程序腾出来更多的消费数据的时间,这样接收缓冲区的剩余空间就更大了
流程:
- 加入接收方此时的接收缓存区剩余空间是3kb,则此时返回ack,则此时窗口大小为3kb
- 如果稍等500ms,此时再看会ack,就有可能在这500ms之内,应用程序有消费掉了2kb,此时返回的窗口大小,就是5kb了
延迟限制
- 数量限制:每隔N个包就应答一次,主要与滑动窗口的情况有关;
- 时间限制:超过最大延迟时间就应答一次; 主要争对非滑动窗口情况
意义:
- 减少了可靠传输对效率的影响
捎带应答
概念
- 捎带应答是在延时应答的基础上,引入的一个进一步提高效率的机制
- 延迟应答,是让ack 传输的实际更慢
- 捎带应答, 是基于延时应答,让数据进行合并
四次挥手和捎带应答的关系
- 为啥说四次挥手可能是三次,主要就是延迟应答+捎带应答起到效果
能合并的原因
- 一方面是时机上可以同时发送
- 一方面是ack数据本身不需要携带载荷,和正常数据不冲突(所以可以用一个数据报既携带载荷数据又带有ack信息), 及(ack标志位,窗口大小,确认信号)组合成一个数据报
意义:
- 数据报从两个合并成一个,效率会很明显提升的,主要是应为这里每次传输数据都要分装+分用
2.6面向字节流
面向字节流会存在的问题
粘包问题
概念
-
这里粘的包是应用层数据报,不仅仅是tcp由,只要是面向字节流的机制(文件) 也都有同样的问题
-
通过tcp类 read/write 的数据,都是tcp 报文的载荷,也就是应用层数据,且read和write都是以字节为单位从缓冲区中读取,可能会一次没读完一个报的情况,应为按字节读取的,这就导致发送方一次性发送多个应用层数据报,但是接收方不能确定哪里是一个完整的应用数据报—这就是粘包问题
解决的方法:
- 站在应用层的角度,明确设计好两个包之间的边界。
- 对于定长的包,保证每次都按固定大小读取即可;例如上面的Request结构,是固定大小 的,那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
- 对于变长的包,可以在包头的位置,约定一个包总长度的字段(字节长度),从而就知道了包的结束位置;
- 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可,**举个例子:**比如一个发送方在每个报文的结尾都加上’\n’,读取放每次用scanner.next就能刚好读到’\n’就停止读取;
- 前面所学的 mxl和/json 都是通过分隔符来区分
2.7 TCP异常情况的处理
- **进程终止/进程崩溃:**进程没了 ==> PCB也就没了 ==> 文件描述符 =也就会释放 ==> 相当于调用socket.close() [socket在系统内核也是一个文件,也会放到文件描述符表中 ] ==> 仍然可以发送FIN,进一步的触发四次挥手,此时连接就正常释放了 。和正常关闭没有什么区别。
- 主机关机/机器重启(正常步骤的关机) : 正常关机,就会先尝试干掉所有进程(强制终止进程) 就和进程终止类似,也不影响释放资源,如果关机还没有挥手完,则服务器会超时重传,还没有服务器就会自动close了
- 机器掉电/网线断开 (拔电源拔网线,没有反应空间):
- 接收方掉电, 发送方(客户端)给接收方(主机)发消息, 但是由于接收方掉电,也就没有ACK,发送方就会触发复位报文(RST 字段 为1),尝试重置连接,如果重置操作仍然失败,此时就会继续但方便释放连接
- **发送方掉电,**发送端(客户端)突然不发消息了,此时接收端(服务器)不知道发送端等会还会不会发,此时接收端就会阻塞等待, 然后周期性的给对方发起一个不携带任何业务数据(载荷)的tcp数据报(“心跳包”),用于触发ack报文,从而确定发送端是否正常工作与网络是否通畅.如果发送端不在了,也会把连接释放。这种机制是由tcp内置的保活定时器来处理的
- 虽然tcp中已经由心跳包的支持(保活定时器),但这还不够,我们通常还要在应用程序中重新实现心跳包(TCP 的心跳包周期太长,TCP心跳包是分钟级的,不足以当前高并发的场景下不适应,高并发要毫米级),例如HTTP长连接中,也会定期检测对方的状态。 例如QQ,在QQ断线之后,也会定期尝试重新连接。
2.8 基于TCP应用层协议
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
3.0 书本推荐
图解tcp/ip
4.0 TCP/UDP对比
TCP
- tcp优势在于可靠性
- 适用于绝大部分的场景
UDP
- UDP优势子啊与效率
- 适用于机房内不得主机之间通信(机房内部,带宽比较充裕,不太容易遇到网络拥堵丢包的情况,又希望主机之间通信速度能比较快)
适用于竞技类的游戏的协议
- 既需要可靠性有需要效率
- UDP和TCP都不一定合适
- 比如KCP这样的协议, 虽然是传输层协议,但是其往往在应用层保证安全,然后传输层传输用UDP
而确定发送端是否正常工作与网络是否通畅.如果发送端不在了,也会把连接释放。这种机制是由tcp内置的保活定时器来处理的
- 虽然tcp中已经由心跳包的支持(保活定时器),但这还不够,我们通常还要在应用程序中重新实现心跳包(TCP 的心跳包周期太长,TCP心跳包是分钟级的,不足以当前高并发的场景下不适应,高并发要毫米级),例如HTTP长连接中,也会定期检测对方的状态。 例如QQ,在QQ断线之后,也会定期尝试重新连接。
2.8 基于TCP应用层协议
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
3.0 书本推荐
图解tcp/ip
4.0 TCP/UDP对比
TCP
- tcp优势在于可靠性
- 适用于绝大部分的场景
UDP
- UDP优势子啊与效率
- 适用于机房内不得主机之间通信(机房内部,带宽比较充裕,不太容易遇到网络拥堵丢包的情况,又希望主机之间通信速度能比较快)
适用于竞技类的游戏的协议
- 既需要可靠性有需要效率
- UDP和TCP都不一定合适
- 比如KCP这样的协议, 虽然是传输层协议,但是其往往在应用层保证安全,然后传输层传输用UDP