数据段=报头+数据
netstat是一个用来查看网络状态的重要工具.
语法:netstat [选项]
功能:查看网络状态
常用选项:
n 拒绝显示别名,能显示数字的全部转化成数字
l 仅列出有在 Listen (监听) 的服務状态
p 显示建立相关链接的程序名
t (tcp)仅显示tcp相关选项
u (udp)仅显示udp相关选项
a (all)显示所有选项,默认不显示LISTEN相关
在查看服务器的进程id时非常方便.
语法:pidof [进程名]
功能:通过进程名, 查看进程id
16位UDP长度: 表示整个数据报(UDP首部+UDP数据)的最大长度。
16位UDP检验和:16位UDP长度 — 接收到的UDP整体 = 0 ; 则表示UDP结构完整,校验正确。如果校验和出错, 就会直接丢弃。
UDP报头和数据分离的方案就是限定报头占前八字节,其他全为正文数据。
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层
返回任何错误信息;
面向数据报: 不能够灵活的控制读写数据的次数和数量
能同时进行读写操作
send() rec() 函数的本质是拷贝函数,从应用层拷贝如内核层,再从内核层拷贝到传输层。
UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作;
UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃
注意:
UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部). 然而64K在当今的互联网环境下, 是一个非常小的数字. 如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装;
(目前只做了解,因为没见过体)
NFS: 网络文件系统;
TFTP: 简单文件传输协议;
DHCP: 动态主机配置协议;
BOOTP: 启动协议(用于无盘设备启动);
DNS: 域名解析协议;
写UDP程序时自定义的应用层协议;
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位紧急指针: 标识哪部分数据是紧急数据;紧急指针指向的数据,发送优先级得到提升
ACK机制(确认应答机制)依托于32位序号/32位确认号。工作原理如图:
32位序列号:TCP将每个字节的数据都进行了编号,即为序列号
32位确认号:每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
一些细节:
即使程序员按照时间发送的数据,实际发出的时间和实际接收到的时间都不确定的, 32位序号/32位确认号和ACK是否可以辅助数据排序? 答: 是
确认序号和位序列号不合并位一个东西的原因是:全双工,收的同时可能发送,如果和并未一个序号,这个序号可能被同时使用,产生矛盾。
ACK(Acknowledgment)机制是一种用于确认数据包或消息是否成功传输的机制。在网络通信中,当发送方向接收方发送数据包时,接收方会发送一个ACK信号作为确认,告知发送方数据包已经成功接收。
ACK机制的工作原理如下:
简单来说就是 ACK标志位失效
ACK丢失指的是在网络通信中,发送方发送了数据包给接收方,但接收方没有及时发送ACK(Acknowledgement)报文进行确认的情况。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认为网络或者对端主机出现异常, 强制关闭连接。
举例:网络环境拥挤时,会设置更长的超时时间
建立链接的本质是建立一种数据结构,是数据结构就会造成负载。
SYN Flood是互联网上最原始、最经典的DDoS(Distributed Denial of Service)攻击之一。 它利用了TCP协议的三次握手机制,攻击者通常利用工具或者控制僵尸主机向服务器发送海量的变源IP地址或变源端口的TCP SYN报文,服务器响应了这些报文后就会生成大量的半连接,当系统资源被耗尽后,服务器将无法提供正常的服务。
上述过程可以连系到一些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
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发送缓冲区写入时,是将数据以字符串的格式拷贝