IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 知识体系之TCP/IP详解 -> 正文阅读

[网络协议]知识体系之TCP/IP详解

目录

1.TCP

1.1.TCP报头

1.2.TCP如何保证可靠性

1.2.1.校验和

1.2.2.确认应答与序列号

1.2.3.超时重传

1.2.4.流量控制(滑动窗口)

1.2.5.拥塞控制

1.3.三次握手

1.3.0.三次握手过程

?1.3.1.为什么序列号syn是随机值?

1.3.2.TCP三次握手,前2次握手的序列号有关系吗?

1.3.3.为什么返回时ack值是seq值加一?

?1.3.4.为什么要进行3次握手?而不是2次或者4次?

1.3.5. listen的第2个参数(两个队列:syn请求队列/已连接队列)

1.3.6. SYN泛洪攻击/DDOC攻击

?1.3.7.??????连接建立是在accept函数么?

?编辑

1.3.8.非阻塞套接字Connect

1.4.四次挥手

1.4.0.四次挥手过程

1.4.1.为什么是4次挥手?不是3次?

1.4.2.close/shutdown、引用计数

1.4.3.TIME-WAIT存在的原因? 为什么主动方在TIME-WAIT阶段要等待2MSL?

1.4.4.TIME-WAIT过多? 危害? 应该怎么解决?

1.4.5.SO_LINGER与RST

1.4.6.CLOSE_WAIT过多?应该怎么解决?

1.4.7. Nagle算法

1.4.8.延迟确认Delay ACK

1.4.9.在Delay ACK开启时,一定要关闭Nagle算法

2.UDP

2.1.UDP报文

2.2. UDP的connect

2.2.1.UDP/TCP的connect的区别

3.QA


1.TCP

1.1.TCP报头

TCP首部20字节 ???“选项和填充”字段的长度最大为40字节

源Port/目的Port

加上IP首部的源IP地址和目的IP地址可以唯一确定一个TCP连接

数据序号

保证发送数据的顺序性

确认序号

仅当ACK标志为1时有效。确认号表示期望收到的下一个字节的序号

6

个标志位

URG

紧急指针有效

ACK

确认序号有效

RST

连接重置

SYN

同步序号用来发起一个连接

FIN

终止一个连接

PSH

接收方应尽快将这个报文交给应用层

校验和

发送主机根据数据计算出一个数值,接受主机接的数值与源主机要一致(证明数据的有效性)

窗口字段

窗口的字节容量, TCP的标准窗口最大为2^16 - 1 = 65535Byte

?TCP粘包\拆包

现象

因为TCP是流式套接字,是没有界限的一串数据,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分。所以,在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把多个小包封装成一个大的包发送。这就是所谓的粘包和拆包问题

本质

原因

要发送的数据小于TCP发送缓冲区的大小,多次写入缓冲区的数据一次发送出去,发生粘包

要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包

待发送的数据大于MSS(最大报文长度),TCP在传输前将进行拆包

解决

方案

由于底层TCP无法理解上层的业务数据,所以在底层无法保证数据包的粘包和拆包,这个问题只能在上层应用协议栈控制

1.

消息定长

发送端将每个数据包封装为固定长度的数据块(长度不够补0填充)

2.

设置消息边界

包尾部添加\n\r,如:FTP协议

3.

消息封装

将消息数据封装为struct {int len; void* data;},指定数据的长度

1.2.TCP如何保证可靠性

  1. TCP的链接是基于三次握手,断开是基于四次挥手,确保链接和断开的可靠性。
  2. TCP的可靠性,还体现在“有状态”
  3. TCP通过校验和、序列号机制、ACK应答、超时重传,来保证数据包的按序到达,保证数据传输不出差错
  4. TCP通过流量控制(滑动窗口)和拥塞控制来控制发送方发送速率

1.2.1.校验和

  1. TCP校验和的计算和UDP一样,在计算时要加上12byte的伪首部,检验和总共计算3部分:TCP首部、TCP数据、TCP伪首部。
  2. 计算方法为:①在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存在校验和字段中;②接收方用相同的方法进行计算,③将计算的结果,与检验字段存储的结果比较,相等则正确,不等则错误

1.2.2.确认应答与序列号

  1. 序列号的作用
    1. 保证可靠性(当接收到的数据总少了某个序号时,能马上知道)
    2. 保证数据的按序到达
    3. 提高效率,可实现多次发送,一次确认
    4. 去除重复数据

1.2.3.超时重传

????????当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传)。

重传时间的确定:

????????报文段发出到收到应答中有一个报文段的往返时间RTT,显然超时重传时间RTO会略大于RTT,TCP会根据网络情况动态的计算RTT,即RTO是不断变化的。在Linux中,超时以500ms为单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。其规律为:如果重发一次仍得不到应答,就等待2500ms后再进行重传,如果仍然得不到应答就等待4500ms后重传,依次类推,以指数形式递增,重传次数累计到一定次数后,TCP认为网络或对端主机出现异常,就会强行关闭连接。

Q: 超时重传时间间隔、重传次数:指数退避方式

A: 第一次发送数据后,设置的超时时间是1.5s,此后每次重传时间都增加1倍,一直到64s,一共重传12次,大约9min才放弃

1.2.4.流量控制(滑动窗口)

目的:控制发送端的发送速度,防止数据发送过快,导致数据接收不过来。(接收端处理数据的速度是有限的,如果发送方发送数据过快,会导致缓冲区满,而发送方继续发送,会造成丢包,继而引起丢包重传等一系列反应)

原理:接收方每次接收到数据,向发送方返回ACK的时候,携带一个接收窗口大小(接收窗口的大小,是接收方根据自己的处理能力来确定的)。这就是滑动窗口机制

为什么引入滑动窗口?

  • 在ACK确认策略中,一发送一应答,应答后才继续发送,否则,将阻塞。这样的缺点是:发送性能差。引入滑动窗口,可以一次发送一个窗口中的多条数据,提高了发送性能

滑动窗口的实现方式

  1. 发送方每次向接收方发送一个窗口大小的数据,发送完成后等待ACK
  2. 发送端接收到ACK后,滑动窗口会一直向右滑动
  3. 在窗口滑动的过程中,如果窗口中的某个pos的数据没有收到ACK,可能会导致窗口不能向前滑动,当超时后,超时定时器会重发数据,直到接收到数据ACK位置
  4. 如此反复执行1~3,直到滑动窗口滑动到数据的尾部,数据全部发送完毕

零窗口

????????当接收缓冲区满时,接收方在回复ACK的时候,会告诉发送方自己的接收窗口rwnd=0了,此时发送方接收到窗口大小为0后,就会停止发送数据。(此时会启动坚持定时器

Q1: 为什么引入坚持定时器?

A1:?① B发送给A窗口为0的确认包后,A之后就不会再给A发送数据。② 等待一段时间后,B缓冲区有数据,就会向A发送一个报文,通知A现在可以发送数据了。③ 但是该报文在传输的过程中一旦丢失了,就会形成下面的“双方死等现象”:A等待B通知它发送数据,B发送报文后等待A发来数据。

Q2: 零窗口探测报文机制

A2:?坚持计时器(在A接收到一个窗口为0的报文后,将每隔一段时间,发送给B探测报文,即探测接收窗口大小的报文,一旦接收窗口大小不为0,就可以继续发送数据)

1.2.5.拥塞控制

为什么要有拥塞控制?

答:流量控制、拥塞控制完全是为了解决不同的问题产生的。① 流量控制主要是接收端为了防止发送方发送数据的速度,一直给发送方应答一个接收窗口。② 拥塞控制主要是通过拥塞窗口cwnd来防止过多的数据注入到网络中,导致网络拥堵

实现手段:通过“发送方”的拥塞窗口cwnd与门限值ssthresh大小对比,根据比对结果,调用响应的拥塞控制算法。

拥塞控制的工作过程:拥塞窗口cwnd/门限值ssthresh

1. 慢启动

????????一开始,建立连接后,将cwnd设为1,cwnd是指数增长的;直到cwnd增大到门限值ssthresh,就会进入拥塞避免阶段

2. 拥塞避免

????????此时,拥塞避免阶段,cwnd是线性增长的(“加法增大”);直到发生【网络超时】,就会进行调整:门限值ssthresh设置为窗口的1/2,cwnd设置为1,继续执行慢开始算法

3. 快重传(3次应答-->丢包-->不等超时,直接重传数据)

????????当发送方收到同一个包的连续3次重复确认时(说明存在网络丢包),此时,不用等待【网络超时】,直接重传数据数据给接收方。(即:接收方向发送方回复了1,2,4包的应答,且连续对包2进行了3次应答,说明包3丢失了)

4. 快恢复(与快重传一起使用)

????????当发送方连续收到同一个包的连续3次重复确认时,执行“乘法减小”算法(即:门限值门限值ssthresh设置为窗口的1/2,但cwnd设为门限值ssthresh,执行拥塞避免算法)

1.3.三次握手

1.3.0.三次握手过程

1.

C向S发送一个同步请求数据包去建立连接,(C进入SYN-SEND状态)

2.

S接收到这个同步请求数据包后(结束LISTEN状态),会对C回复一个同步确认包,(S进入SYN-RCVD)

3.

C收到这个同步确认包后,再对服务器再发送一个同步确认包,C/S一起进入ESTABLISHED状态 ?

  1. 序列号seq=X+1,表示收到S的确认号ack,将其作为自己的seq
  2. 确认号ack=Y+1,表示收到S的序列号seq,将其值加1作为自己的确认号

注意

ACK标记和ack序列号不是一个东西

ack序列号的值,一定是请求号seq+1

C是主动方,S是被动方

步骤1,2中的seq是随机生成的

?1.3.1.为什么序列号syn是随机值?

????????① 防止因为网络延迟导致后到的网络包建立连接,产生错误

????????② 防止黑客伪造攻击

1.3.2.TCP三次握手,前2次握手的序列号有关系吗?

答: 没有关系,都是随机值

1.3.3.为什么返回时ack值是seq值加一?

????????仅当ACK标志为1时有效。确认号ack表示期望收到的下一个字节的序号

?1.3.4.为什么要进行3次握手?而不是2次或者4?

如果只进行2次握手,描述如下:C发送了创建链接后,S端回复了应答ACK后,就认为链接建立成功。

分析:

????????假设S回复的应答ACK丢失了,①因为C没有收到应答ACK,C会一直等待S的应答,②而S认为链接已经建立了,S就会想C发送数据,③C端接收到S端发来的数据,它是不会接收的,因为C要先收到S端的应答ACK后,才能接收S端到来的数据。 ===> 这样就会形成“死锁”,即①C一直等待S的应答ACK,②S一直向C发送数据

1.3.5. listen的第2个参数(两个队列:syn请求队列/已连接队列)

????????listen函数将主动套接字转换为被动监控套接字,其第二个参数backlog决定了内核的连接缓存队列长度。对于一个给定的监听套接字,内核维护两个队列:

① 未就绪队列,存放没有完成三路握手的连接,监听套接字收到SYN并返回ACK+SYN,连接处于SYN_RECV状态,等待对端发送ACK。如果已完成队列非满,则接收ACK,连接握手完成,进入已完成队列;如果已完成队列满则丢弃ACK,对端重发ACK(对端看到的连接是ESTABLISED状态),若未就绪队列中的SYN_RECV等待直到超时还没进入已完成队列则丢弃连接(对端不知道,只有在读写套接字时才知道)。

② 已完成队列,存放已经完成三路握手的连接(ESTABLISHED),等待accept取走连接。


????????backlog决定了两个队列的长度之和(并不是说两个队列之和等于backlog,而是存在个转换,依赖于具体实现)。

如果未就绪队列满则忽略新到来的SYN请求,对端重发,如果一直不能进入未就绪队列则对端connect失败返回。

当监听套接字关闭时:① 会对已完成队列中的每个连接发送复位分节RST,对端捕获RST被动关闭连接;② 直接释放未就绪队列的连接,这时对端不知道,对端的连接状态依然保持ESTABLISHED状态,直到对端主动关闭连接,由于监听端已经关闭连接,所以以RST响应对端的FIN,对端收到RST直接关闭连接。(类似于半打开连接)

1.3.6. SYN泛洪攻击/DDOC攻击

原理

攻击者构造虚假的IP,向服务器发送SYN包,服务器接收到SYN后,将会将该连接请求加入未连接队列,这样,之后再来的连接请求将会被丢弃(导致其他的连接无法进入);另外,维护这些SYN请求,也需要消耗大量的资源

防御

限制SYN的并发数量

防火墙禁止某些恶意的IP地址

tcp_syncookies:将半连接请求数量超出tcp_max_syn_backlog时,内核会自动启用SYN cokkie机制,不再把半连接请求放入队列,而是用SYN cookie来检查。

根据请求包头信息计算cookie,返回给发送端,(此时并不把连接请求加入请求队列);接收发送端回复的ACK,查看ACK和cookie中的序列号是否一致,如果一致,就是正确的连接。

?1.3.7.??????连接建立是在accept函数么?

socket客户端连接上服务端是在listen之后而非在accept之时

在刚刚接触网络编程时,很长一段时间都以为只有服务端调用accept后,客户端才会connect成功,但是实际上①只要服务端开启listen,客户端调用connect时,就会连接成功(即三次握手成功)。②accept,只是将fd从连接队列中移除

【结论】:此时,即使服务端在listen后调用了sleep后,没调用accept。客户端仍然可以调用send发送数据,也会发送成功。

1.3.8.非阻塞套接字Connect

如果connect返回值为-1,如果错误码不是EINPROGRESS,说明出现错误,直接return;如果错误码是EINPROGRESS,意味着:表示此时TCP三次握手仍在进行。之后可以使用select检查连接是否建立成功

给select设置等待时间,并将打开的socket添加至select监控(即使用select函数等待正在后台连接的connect函数):

  1. 如果只可写,说明连接成功
  2. 如果描述符既可读又可写,分为两种情况
    1. socket连接错误(这是系统规定的,可读可写时可能是connect连接成功后远程主机断开了连接close(socket)
    2. connect连接成功,socket读缓冲区接收到了远程主机发送的数据,可以用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int))来得到error的值来判断,如果为0,则说明socket connect成功。否则,也是失败。

1.4.四次挥手

1.4.0.四次挥手过程

1.

C向S发送TCP报文(FIN标记位,seq=随机),试图断开连接,随后C进入FIN-WAIT-1阶段,即半关闭状态(即:停止C向S发送数据,但是C仍然能接收S传来的数据)

半关闭状态:关闭了“写”方向,保留了“读”方向

2.

S接收到C发来的TCP报文后,回复确认报文,结束ESTABLISHED阶段,S进入CLOSE-WAIT阶段,即半关闭状态

此时,C接收到S回复的确认报文后,将进入FIN-WAIT-2阶段

3.

S端经过CLOSE-WAIT后,将发送带有FIN的断开连接的报文,S随即进入LAST-ACK阶段。

C端收到S端发来的报文后,得知了S端已经做好释放连接的准备,随即进入TIME-WAIT阶段

4.

C在进入TIME-WAIT状态时,会立即向S发送最后的确认报文。S收到该报文后进入CLOSED状态。

1.4.1.为什么是4次挥手?不是3次?

三次握手,是因为第二次的ack和seq是同时发送的,而四次挥手不行,因为

  1. 四次挥手要保证全双工的通道断开
  2. 第一对FIN和ACK,只是断开了发送通道,而此时接收方的缓冲区中可能还有数据未接收,因此要等到缓冲区中无数据后,才能发送第二队FIN和ACK,断开接收通道

1.4.2.close/shutdown、引用计数

close

close函数会关闭套接字,但是如果调用close时,有其他进程共享着该套接字,那么该连接仍然是打开的,该连接仍然可以被其他打开的进程读写。

shutdown

shutdown会切断进程共享的套接字的所有连接,不管引用计数是否为0,都会关闭。

关闭的方向由第2个参数决定SHUT_RD/SHUT_WR/SHUT_RDWR。

※ 陈硕之muduo源码

分析:在陈硕写的muduo源码库中的TcpCOnnection,没有提供close,而只是提供了shutdown,这么做的目的是为了保证收发数据的完整性。

它相当于是将“主动关闭连接”这件事分两步做,①A调用shutdown关闭写方向/保留读方向后,B read将返回0,紧接着B将手动调用shutdown关闭读方向/保留写方向。(此时A/B都进入半关闭状态)②当B发送完数据后,B调用shutdown关闭写方向后,A read将返回0,紧接着A将手动调用shutdown关闭读方向(此时A/B都进入全关闭状态)

????显然,这样做的好处是:能够保证数据收发的完整性,即:当某个方向上没有数据收发时,可以手动地控制。保留另外一个方向数据的传递!

1.4.3.TIME-WAIT存在的原因? 为什么主动方在TIME-WAIT阶段要等待2MSL?

2MSL

1MSL,即一段TCP报文在传输过程中的最大生命周期(centos-30s,unix-60s)

存在

原因

等待2MSL的根本原因:为了实现TCP全双工连接的可靠释放。

在第4次挥手时,C端向S端发送最后的ACK,也可能会丢失,导致S端收不到该确认报文。

==> 当S端在1MSL时间内没有接收到C端发送的ACK确认报文时,(由于TCP的重传机制)S端就会再次向C端重新发送FIN报文,这样C端接收到FIN报文后,会重新发送最后一个ACK报文,直到S端能成功的接收到ACK报文后,连接才真正的断开!

为了使旧的数据包在网络中因过期而消失,防止新的连接接收到旧的数据报文

先假设没有TIME-WAIT状态的限制,如果当前有一个TCP连接(local_ip, local_port, remote_ip,remote_port)在断开的同时,以相同的4元组去建立一个新连接。那么TCP协议栈就无法区分前后两条TCP连接是不同的,在它看来,这根本就是同一条连接。(导致==> 前一条TCP已经关闭的连接发送出去的数据,通过之后的新连接,仍然可以发送给S端)。

由于TIME_WAIT阶段会等待2MSL,那么旧的数据包一定会超时而消失,就不存在上面的问题了!

1.4.4.TIME-WAIT过多? 危害? 应该怎么解决?

常识

主动调用close的一方,会在发送最后一个ACK后,进入TIME-WAIT状态

TIME-WAIT过多的原因:大量的短连接

许多短连接正在被快速的创建和关闭,由于关闭时需要等待2MSL,可能导致出现大量的TIME_WAIT

危害

TIME-WAIT的每个套接字都会消耗套接字,导致fd被耗尽

套接字还绑定了端口/地址,如果不设置地址复用,在TIME-WAIT期间不能重用该地址

解决

方案

  1. 优化系统,减少短连接的次数
  2. 参数配置
  3. setsockopt设置地址复用SO_REUSEADDR选项(作用:通知内核,如果端口port忙,但TCP状态为TIME-WAIT状态,可以重用端口)
  4. setsockopt设置 SO_LINGER 选项 (后面详细介绍)

1.4.5.SO_LINGER与RST

作用

设置close()关闭TCP连接时的行为。当socket发送缓冲区有数据残留时,

  • 默认的行为,一直等待数据发送给对方,数据缓冲区中没数据后,才close
  • 可以设置的行为
  1. 立即关闭该连接。直接丢弃发送缓冲区中的数据,并向向对方发送RST标记,来关闭该连接。主动关闭的一方将跳过TIMEWAIT状态,直接进入CLOSED状态。
  2. 给关闭连接设置一个超时时间。[1] 如果超时时间没到,且缓冲区中的数据被全部发送,内核将用FIN/ACK/FIN/ACK的方式关闭连接。[2] 如果超时时间到了,且缓冲区中依然有数据,则采用方式a的处理方式。

【备注】网上很多人想用方式a避免TIMEWAIT,但是,这并不是一个好的注意,这种关闭方式的用途不在这里,实际用于在于服务器在应用层的需求!

分析

从“作用”可以看出,发送RST是一种【异常关闭】的方式,它会丢弃缓冲区中的数据,发送RST强制关闭。

【正常关闭】是指,通过四次挥手,等缓冲区的数据被清空后,再安全可靠的关闭

异常关闭的优点

丢弃任何待发的无意义数据,立即发送RST报文,跳过TIMEWAIT,进入CLOSED状态

RST接收方,可以利用关闭方式来区分主动关闭方是正常关闭还是异常关闭

补充

值得注意的是,接收RST的一方的行为是什么呢?

? ? ? ? 接收RST的一方不会向发送方做任何的响应,它会立即终止连接

1.4.6.CLOSE_WAIT过多?应该怎么解决?

原因

多半是程序的原因,还是交给程序猿吧!是因为被动关闭方未调用close()

危害

大量的CLOSE_WAIT会消耗掉系统资源,导致fd不被释放。

分析

若被动关闭方不关闭发送FIN给主动关闭方,此时,被动关闭方就会进入CLOSE_WAIT状态。一个CLOSE_WAIT会维持至少2个小时的时间(系统默认超时时间的是7200秒,也就是2小时)

场景

Client发送FIN给Server,Server并没有调用close函数发送FIN,那么Server进入了CLOSE_WAIT状态。

解决

方案

修改TCP/IP配置参数

tcp_keepalive_time

CLOSE_WAIT状态的时间,默认2h(改小)

tcp_keepalive_probes

发从探测的次数(改小)

tcp_keepalive_intvl

重新发送探测的频度(改小)

代码需要判断socket,一旦read返回0,断开连接,read返回负,检查一下errno,如果不是AGAIN,也断开连接

心跳检测:定期向socket发送指定格式的心跳数据包,如果接收到对方的RST报文,说明对方已经关闭了socket,那么也关闭这个socket。

1.4.7. Nagle算法

背景

在使用一些协议通讯的时候,比如Talnet,会有一个字节一个字节发送数据的场景,(但是,每次发送一个字节的有用数据,却需要产生20个字节的IP头/20个字节的TCP头),这就导致了发送一个字节的数据需要携带至少40个字节的协议头。当发送频率很高的时候,会有很多小包没得到确认,无疑会产生很大的浪费,造成网络上充斥着很多small packet时,会造成网络拥塞。

思想

Nagle算法思想

发送方发送数据后,在没有收到确认ACK前,不能继续发送其他数据(它保证了TCP连接上最多只能有一个未被确认的发送数据)

目的

Nagle算法的目的是:避免网络中出现大量的Small packet,但与此同时,因为只能一个小包得到ack后,才可以发送下一个小包,所以会造成网络传输速度下降(但是Nagle算法不会那么傻,它会将小包合并,一起发送)

实验

通过实验来看Nagle算法对发送的优化

实验设计:发送方每次发送一个字节的数据给接收方

应用层调用send连续5次发送数据,由于Nagle算法,会导致①多个数据合并成一个数据,一起发送,这样大大减少了small packet的数量,(增加了TCP传输的效率)。②数据不会连续被发出,发送第一个数据后,会等待接收到确认ACK后,才发送第二个数据包,(这样会导致实时性不强)

场景

Nagle算法默认是开启的,该算法比较适用的场景是:发送方发送大批量的小数据,且接收方会及时的做出回应

选项

setsockopt(client_fd, SOL_TCP, TCP_NODELAY,(int[]){1}, sizeof(int));

1.4.8.延迟确认Delay ACK

原理

① A给B发送数据后,B不会直接给A发送应答(Delay ACK);当且仅当B有数据发送给A后,顺便将应答放在数据包中,一起发送给A。

② 如果等了一段时间(约40ms),没有数据从B发送给A,就回复一个“纯”确认ACK给A。

【总结】显然,上面过程,不是直接应答,中间发生了延迟确认。

这样做的好处是:相当于一个司机拉客人,他将客人拉到目的地后,不直接空车返回,而是等接到返程的客人,一起返回。

很明显,这样的方式是存在一定收益的!都是为了提高TCP性能

1.4.9.在Delay ACK开启时,一定要关闭Nagle算法

分析

Nagle算法本身的立意是好的,避免网络充斥着过多的小包,提高网络传输的效率。与此同时,Delay ACK也是为了提高TCP的性能,不过二者遇到了,就比较悲剧了。

如果同时开启了Nagle算法和Delay ACK,对于write(header)-wirte(body)-read(response)场景,会产生极大的副作用。

原因

  • 第一个header一定是能够发送出去的
  • 由代码可知,接收方接收到TCP报文头header后,发现是不完全的,还会再次等待Body数据的到来,由于开启了Delay ACK,因此,不会立即回复ACK给发送方
  • 而,发送方由于开启了Nagle算法,由于没有收到ACK,则不会再发送body给接收方。
  • 直到接收方的Delay ACK超时时,才会向发送方应答确认ACK
  • 接收方接收到应答ACK后,才能继续发送body数据。

分析

显然,发送方每次都要等待Delay ACK超时的应答到来后,才可以继续发送body数据,这会产生巨大的延迟。

这个问题的产生,主要是Nagle、Delay ACK副作用,以及write-write-read程序造成的,一般写程序的时候,不推荐这样的写法。

如何

解决

方案1:禁止掉Nagle算法。但是这样做,会导致网络中充斥着大量的small packet(数据header),降低效率。

方案2:常规的解决方案,都是要避免应用程序出现write-write-read的写法

2.UDP

2.1.UDP报文

与TCP相比

  1. 都有端口
  2. 但是没有复杂的其他标识
  3. 多了数据长度(报文)

可以看到,UDP与TCP相比,没有bind/connect函数。

S端:它在bind后,就可以直接调用sendto/recvfrom函数收发数据;不需要listen/accept

C端:直接就可以调用sendto/recvfrom函数收发数据;不需要connect?

2.2. UDP的connect

????????connect(client_fd,?(struct?sockaddr*)&server_addr,?socklen)

2.2.1.???????UDP/TCP的connect的区别

先说明

UDP中的connect与TCP中的connect有着本质上的区别:

  1. TCP的connect会引起三次握手;UDP不会。
  2. 由于UDP是无连接的,在调用connect时其实没有向外发包,只是在协议栈中记录了该状态,

【补充】采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom

  1. 异步ICMP错误不会返回给unconnect的UDP套接字,调用connect后,可以接收到异步ICMP错误
  2. 提高发送数据的效率
    1. ???????普通的UDP发送两个报文内核做了如下:#1:建立连结#2:发送报文#3:断开连结#4:建立连结#5:发送报文#6:断开连结
    2. 采用connect方式的UDP发送两个报文内核如下处理:#1:建立连结#2:发送报文#3:发送报文另外一点, ?每次发送报文内核都由可能要做路由查询???????

Q1: UDP是否可以多次调用connect?

A1:?UDP可以多次调用connect,而TCP只能调用一次connect

Q2:????????UDP多次调用connect的目的?

A2:?断开和之前的ip,port的连结,与新的ip,port连接

3.QA

Q1: TCP和UDP的区别

A1:

TCP

UDP

面向连接(可靠)

面向非连接(不可靠)

流协议(引起粘包问题)

报文(有边界)

UDP处理/传输速度更快

UDP不具有TCP那样的流量控制,它是尽自己最大可能的速度传输数据

TCP维持数据的可靠有序发送,成本更高,消耗更大

数据流协议,包大小不受限制

包大小不超过MTU(1400),超过会报错or丢包

TCP头大小20

UDP头大小8

Q2: 既然TCP可靠,为什么很多项目仍然选择UDP?

A2:

  1. UDP不用考虑数据的可靠性,传输效率更快,尽最大可能投递数据。很多项目如果不用太考虑可靠性,但是需要很快的传输效率时,就会使用UDP。比如:抖音视频、语音视频,都是采用UDP。这几种应用允许数据丢包,如果非要使用TCP,那么丢包时将会重传数据,造成卡顿!(一般都是在应用层实现可靠UDP方案)
  2. 物联网中的应用:长连接耗电

    音视频通话:TCP延迟大

Q3:?TCP是可靠的,为什么通信会丢包? 怎么解决?

A3:?

????????例如服务器给客户端发大量数据,Send的频率很高,那么就有可能在Send时发生错误(原因可能是又多种,可能是程序处理逻辑问题,多线程同步问题,缓冲区溢出问题等等),如果没有对Send失败做处理重发数据,那么客户端收到的数据就会比理论应该收到的少,就会造成丢数据,丢包的现象。

????????这种现象,其实本质上来说不是丢包,也不是丢数据,只是因为程序处理有错误,导致有些数据没有成功地被socket发送出去。

????????常用的解决方法如下:拆包、加包头、发送,组合包,如果客户端、服务端掉线,常采用心跳测试。

Q4: KCP

A4: 可靠的UDP

Q5: 数据发送的整个过程

A5: 参考链接

Q6: 数据传输的过程中,哪些情况会丢包?

A6:?

  • 在两台 VM 连接之间,可能会发生传输失败的错误,比如网络拥塞、线路错误等;
  • 在网卡收包后,环形缓冲区可能会因为溢出而丢包;
  • 在链路层,可能会因为网络帧校验失败、QoS 等而丢包;
  • 在 IP 层,可能会因为路由失败、组包大小超过 MTU 等而丢包;
  • 在传输层,可能会因为端口未监听、资源占用超过内核限制等而丢包;
  • 在套接字层,可能会因为套接字缓冲区溢出而丢包;
  • 在应用层,可能会因为应用程序异常而丢包;
  • 此外,如果配置了 iptables 规则,这些网络包也可能因为 iptables 过滤规则而丢包

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:53:47  更:2022-10-22 21:54:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 13:10:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码