文章目录
- ————————预备知识————————
- 数据段
- netstat
- pidof
- —————UDP协议报头即相关概念分析—————
- UDP协议端格式
- UDP 特点
- 全双工
- send / rec 函数的本质
- UDP的缓冲区
- 基于UDP的应用层协议
- —————TCP协议报头即相关概念分析—————
- TCP格式及解析
- 32位序号/32位确认号确认TCP完整性的原理
- ——————各种TCP机制的分析——————
- ACK机制(确认应答机制)
- ACK丢失
- 超时重传机制
- 重发引起的数据重复
- 超时重传的时间确定
- TCP是全双工原理图
- 连接的本质
- SYN洪水(SYN Flood)
- 连接管理机制
- 三次握手 (建立连接)
- 四次挥手(断开连接)
- 如何使断开的服务端可以立刻重启
- TCP面向字节流的浅略理解:
————————预备知识————————
数据段
数据段=报头+数据
netstat
netstat是一个用来查看网络状态的重要工具.
语法:netstat [选项]
功能:查看网络状态
常用选项:
n 拒绝显示别名,能显示数字的全部转化成数字
l 仅列出有在 Listen (监听) 的服務状态
p 显示建立相关链接的程序名
t (tcp)仅显示tcp相关选项
u (udp)仅显示udp相关选项
a (all)显示所有选项,默认不显示LISTEN相关
pidof
在查看服务器的进程id时非常方便.
语法:pidof [进程名]
功能:通过进程名, 查看进程id
—————UDP协议报头即相关概念分析—————
UDP协议端格式
UDP报头:也就是指图中标识的前8字节内容。
16位UDP长度: 表示整个数据报(UDP首部+UDP数据)的最大长度。
16位UDP检验和:16位UDP长度 — 接收到的UDP整体 = 0 ; 则表示UDP结构完整,校验正确。如果校验和出错, 就会直接丢弃。
UDP报头和数据分离的方案就是限定报头占前八字节,其他全为正文数据。
UDP 特点
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层
返回任何错误信息;
面向数据报: 不能够灵活的控制读写数据的次数和数量
全双工
能同时进行读写操作
send / rec 函数的本质
send() rec() 函数的本质是拷贝函数,从应用层拷贝如内核层,再从内核层拷贝到传输层。
UDP的缓冲区
UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;
UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃
注意:
UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部). 然而64K在当今的互联网环境下, 是一个非常小的数字. 如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装;
基于UDP的应用层协议
(目前只做了解,因为没见过体)
NFS: 网络文件系统;
TFTP: 简单文件传输协议;
DHCP: 动态主机配置协议;
BOOTP: 启动协议(用于无盘设备启动);
DNS: 域名解析协议;
写UDP程序时自定义的应用层协议;
—————TCP协议报头即相关概念分析—————
TCP格式及解析
TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制。
源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
32位序号/32位确认号:用于确认报文是否完整,具体原理再做详细说明。
4位首部长度:表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60
6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段
16位窗口大小: 后续详细介绍
16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
16位紧急指针: 标识哪部分数据是紧急数据;紧急指针指向的数据,发送优先级得到提升
32位序号/32位确认号确认TCP完整性的原理
ACK机制(确认应答机制)依托于32位序号/32位确认号。工作原理如图:
32位序列号:TCP将每个字节的数据都进行了编号,即为序列号
32位确认号:每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
一些细节:
即使程序员按照时间发送的数据,实际发出的时间和实际接收到的时间都不确定的, 32位序号/32位确认号和ACK是否可以辅助数据排序? 答: 是
确认序号和位序列号不合并位一个东西的原因是:全双工,收的同时可能发送,如果和并未一个序号,这个序号可能被同时使用,产生矛盾。
——————各种TCP机制的分析——————
ACK机制(确认应答机制)
ACK(Acknowledgment)机制是一种用于确认数据包或消息是否成功传输的机制。在网络通信中,当发送方向接收方发送数据包时,接收方会发送一个ACK信号作为确认,告知发送方数据包已经成功接收。
ACK机制的工作原理如下:
- 发送方发送数据包给接收方。
- 接收方收到数据包后,会发送一个ACK信号给发送方。
- 发送方在接收到ACK信号后,确认数据包已经成功传输。
- 如果发送方在一定时间内没有接收到ACK信号,会认为数据包丢失或未成功传输,并会重新发送数据包。
ACK丢失
简单来说就是 ACK标志位失效
ACK丢失指的是在网络通信中,发送方发送了数据包给接收方,但接收方没有及时发送ACK(Acknowledgement)报文进行确认的情况。ACK报文是用来确认接收到的数据包的,它通常会在接收方成功接收到数据后发送给发送方。
当发送方发送数据包后,它会等待一段时间来接收ACK报文。如果在规定的时间内没有收到ACK报文,发送方就会认为数据包丢失,并触发重传机制,重新发送相同的数据包。
ACK丢失可能发生在以下情况下:
- 网络延迟:网络延迟可能导致ACK报文在传输过程中被延迟或丢失,使得发送方无法及时收到确认。
- 数据包丢失:在传输过程中,数据包可能会由于网络拥塞、错误的路由或其他原因而丢失,导致接收方无法接收到数据包和发送ACK报文。
- 故障设备:接收方的网络设备、操作系统或应用程序可能存在故障,导致无法发送ACK报文。
- 防火墙或路由器配置问题:防火墙或路由器的配置问题可能会阻止ACK报文的传输,导致丢失。
特别注意:ACK丢失不代表数据包丢失
超时重传机制
主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发.
重发引起的数据重复
机A未收到B发来的确认应答, 也可能是因为ACK丢失了,那么A会触发重传,发出重复的内容,那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉. 这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果。
超时重传的时间确定
最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”.但是这个时间的长短, 随着网络环境的不同, 是有差异的.如果超时时间设的太长, 会影响整体的重传效率;如果超时时间设的太短, 有可能会频繁发送重复的包。
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.
Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时
时间都是500ms的整数倍.
如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传.
如果仍然得不到应答, 等待 4500ms 进行重传. 依次类推, 以指数形式递增.
累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接。
举例:网络环境拥挤时,会设置更长的超时时间
TCP是全双工原理图
注意:缓冲区属于TCP协议
连接的本质
建立链接的本质是建立一种数据结构,是数据结构就会造成负载。
SYN洪水(SYN Flood)
SYN Flood是互联网上最原始、最经典的DDoS(Distributed Denial of Service)攻击之一。 它利用了TCP协议的三次握手机制,攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源IP地址或变源端口的TCP SYN报文,服务器响应了这些报文后就会生成大量的半连接,当系统资源被耗尽后,服务器将无法提供正常的服务。
连接管理机制
三次握手(建立连接)与四次挥手(断开连接)。
三次握手 (建立连接)
三次握手的过程如下:
- 客户端向服务器发送连接请求(SYN包),其中包含一个初始序列号(ISN)用于后续数据传输的顺序标识。
- 服务器收到连接请求后,如果接受连接,则发送确认(ACK包)和自己的初始序列号给客户端,同时也发送一个连接请求(SYN包)给客户端。
- 客户端收到服务器的确认和连接请求后,向服务器发送确认(ACK包),确认服务器的初始序列号,并发送自己的初始序列号。
四次挥手(断开连接)
四次挥手的过程如下:
- 主动关闭方(可能是Client方,也可能是Server方)发送连接释放请求(FIN包)给被动关闭方,表示主动关闭方不再发送数据。
- 被动关闭方收到连接释放请求后,发送确认(ACK包)给主动关闭方,表示已收到连接释放请求。
- 被动关闭方进入CLOSE_WAIT状态,继续发送剩余的数据(如果有),并等待主动关闭方发送连接释放请求。
- 主动关闭方收到确认后,进入TIME_WAIT状态,等待一段时间(2倍的最大报文段生存时间,即MSL)以确保被动关闭方收到确认。补充说明:(MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK))
- 在等待时间结束后,主动关闭方发送最后的连接释放请求(FIN包)给被动关闭方。
- 被动关闭方收到连接释放请求后,发送确认(ACK包)给主动关闭方,表示已收到连接释放请求。
- 被动关闭方进入CLOSED状态,主动关闭方等待一段时间后也进入CLOSED状态,完成连接的断开。
上述过程可以连系到一些TCP的demo代码产生的现象:
现象一:同端口号Server在断开后不能立马重启,因为此时上次刚断开的Server还处于TIME_WAIT状态,并没有真正断开。
现象二:代码层面,在完成所有服务/应答后,必须调用close()函数关闭套接字,如果没有关闭,则会无法退出,连接将一直保持打开状态,占用网络资源。这可能导致其他应用程序无法使用该端口或无法建立新的连接。
原因是不使用close()关闭套接字,就会卡在close_wait状态
如何使断开的服务端可以立刻重启
使用setsockopt()设置socket描述符的 选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符
setsockopt 函数可以使用 SO_REUSEADDR 选项来设置端口复用。当一个套接字处于 TIME_WAIT 状态时,如果另一个套接字想要绑定到同样的地址和端口,通常会报错。使用 SO_REUSEADDR 选项可以允许地址和端口的重用,即使套接字处于 TIME_WAIT 状态。
下面是一个使用 setsockopt 函数设置端口复用的示例:
```cpp
c复制代码#include <sys/socket.h>
#include <netinet/in.h>int enable_port_reuse(int sockfd) {int reuse = 1;if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {// 设置失败return -1;}return 0;
}
TCP面向字节流的浅略理解:
应用层向TCP发送缓冲区写入时,是将数据以字符串的格式拷贝