一、网络的分层结构
网络分层结构是为了简化网络设计和实现,将复杂的网络通信过程按功能划分为不同层次,每层专注于特定任务并为上层提供服务。常见的分层模型有七层模型(OSI 模型) 和五层模型(TCP/IP 模型)。
1. 七层模型(OSI 参考模型)
物理层
不涉及具体物理设备,而是定义物理设备的通信标准,如网线、光纤的接口类型、信号传输的电压 / 电流强度等。其核心功能是将数据转换为比特流(0 和 1),并通过物理介质传输。数据链路层
负责将比特流封装为完整的数据帧(添加帧头和帧尾,包含 MAC 地址),实现相邻设备间的可靠数据传输。主要功能包括 MAC 地址的封装与解封装、差错检测(如 CRC 校验)、流量控制等。交换机工作在该层。网络层
核心是实现跨网络的逻辑寻址和路由选择,通过IP 地址识别不同网络中的主机,并选择最佳路径将数据从源主机转发到目标主机。路由器工作在该层,负责 IP 地址的封装与解封装。传输层
提供端到端的可靠数据传输,通过端口号区分同一主机上的不同应用程序。主要协议有 TCP(可靠传输,带流量控制、拥塞控制)和 UDP(不可靠传输,速度快)。核心功能包括端口封装 / 解封装、数据分段与重组、可靠性保证等。会话层
负责建立、管理和终止应用程序之间的会话连接,如验证用户身份、控制会话的同步与恢复(如断点续传)。表示层
处理数据的格式转换、加密解密、压缩解压等,确保不同系统(如 Windows 和 Linux)之间的数据兼容性。例如,将 ASCII 码与 Unicode 进行转换,或对数据进行 SSL 加密。应用层
直接为用户提供应用程序接口(API),处理具体的业务逻辑,如 HTTP(网页访问)、FTP(文件传输)、SMTP(邮件发送)等协议都工作在该层。
2. 五层模型(TCP/IP 模型)
五层模型是对 OSI 七层模型的简化,合并了部分功能相似的层次,更贴合实际网络应用:
- 物理层(同七层模型)
- 数据链路层(同七层模型)
- 网络层(同七层模型)
- 传输层(同七层模型)
- 应用层(合并了 OSI 的会话层、表示层和应用层,直接提供应用服务)
二、网络程序的设计模式
网络程序的设计模式定义了客户端与服务器(或节点)之间的通信架构,常见模式包括:
C/S 模式(客户端 / 服务器模式)
- 客户端与服务器分工明确:客户端负责用户交互(如界面展示、数据输入)和部分业务逻辑,服务器负责数据存储、资源管理和核心业务处理。
- 特点:客户端需单独安装,与服务器直接通信,响应速度快,安全性高。
- 示例:QQ、微信 PC 端、数据库客户端(如 MySQL Workbench)。
B/S 模式(浏览器 / 服务器模式)
- 以浏览器作为客户端,用户通过 URL 访问服务器提供的 Web 服务,无需安装额外软件。
- 特点:跨平台性强(只需浏览器),维护成本低(只需更新服务器),但依赖网络稳定性。
- 示例:淘宝网页版、在线文档(如 Google Docs)。
P2P 模式(对等网络模式)
- 无中心化服务器,每个节点(计算机)既可以作为客户端(请求资源),也可以作为服务器(提供资源),节点之间直接通信。
- 特点:去中心化,抗故障能力强,资源共享效率高,但节点管理复杂。
- 示例:区块链网络、BitTorrent(文件共享)、Skype(点对点通话)。
分布式架构
- 将系统拆分为多个独立的服务(如用户服务、支付服务),服务之间通过网络协议(如 HTTP、RPC)协同工作,共同完成业务逻辑。
- 特点:松耦合、可扩展性强,适合大型复杂系统,但需解决服务协调、数据一致性等问题。
- 示例:微服务架构(如电商平台的订单服务、库存服务)。
三、以太网中设备、主机与程序的区分标识
区分网络设备:MAC 地址(媒体访问控制地址)
MAC 地址是网络设备(如网卡)的物理地址,全球唯一,长度为 48 位(6 字节),格式如00:1B:44:11:3A:B7
。用于数据链路层,标识局域网内的设备。区分网络主机:IP 地址
IP 地址是网络层的逻辑地址,标识主机在网络中的位置(如192.168.1.1
)。通过 IP 地址可实现跨网络的主机定位。区分网络程序:端口号
端口号是传输层的标识,用于区分同一主机上的不同应用程序(如 HTTP 默认 80 端口,HTTPS 默认 443 端口)。IP 地址 + 端口号可唯一标识网络中的一个程序。
四、计算机网络的地域分类
按覆盖范围(地域)划分,计算机网络可分为:
局域网(LAN,Local Area Network)
覆盖范围小(如一栋楼、一个办公室),传输速率高(通常 100Mbps~10Gbps),延迟低,所有权归单一组织,如公司内网、家庭 WiFi。城域网(MAN,Metropolitan Area Network)
覆盖范围为一个城市(如几十公里),通常用于连接多个局域网,如城市政务网、校园网集群。广域网(WAN,Wide Area Network)
覆盖范围最大(如国家、全球),传输速率较低(受限于远距离传输),延迟较高,由多个运营商协同维护,如互联网(Internet)、跨国企业专线。
五、世界上第一台电脑
世界上第一台通用电子计算机是ENIAC(电子数字积分计算机),于1946 年在美国宾夕法尼亚大学诞生。它重达 30 吨,占地 167 平方米,主要用于军事弹道计算。
六、poll 相较于 select 的优点
select
、poll
和epoll
都是 Linux 中用于 I/O 多路复用的机制,poll
相较于select
的优点如下:
维度 | select | poll |
---|---|---|
数据结构 | 位图(Bitmap) | 链表(数组) |
最大连接数 | 受FD_SETSIZE 限制(通常 1024) |
无硬性限制(取决于内存) |
内核 / 用户空间交互 | 每次调用需复制整个描述符集合 | 每次调用需复制整个描述符集合 |
适用场景 | 连接数少且活跃的场景 | 连接数较多但活跃度高的场景 |
核心优点:poll
通过链表存储文件描述符,突破了select
的 1024 连接数限制,能处理更多并发连接。
七、TCP 三次握手的过程
TCP 三次握手是建立可靠连接的过程,确保双方都能收发数据:
第一次握手(客户端→服务器)
客户端发送一个SYN
(同步)报文,包含客户端的初始序列号(ISN_c),状态变为SYN_SENT
。第二次握手(服务器→客户端)
服务器收到SYN
后,回复SYN+ACK
报文:SYN
:包含服务器的初始序列号(ISN_s);ACK
:确认客户端的SYN
(确认号 = ISN_c+1)。
服务器状态变为SYN_RCVD
。
第三次握手(客户端→服务器)
客户端收到SYN+ACK
后,发送ACK
报文(确认号 = ISN_s+1),状态变为ESTABLISHED
。
服务器收到ACK
后,状态也变为ESTABLISHED
,连接正式建立。
八、客户端故障后的连接处理
若 TCP 连接已建立,客户端突然故障,服务器通过保活计时器释放资源:
- 服务器每收到客户端数据,重置保活计时器(默认 2 小时)。
- 若 2 小时内无客户端数据,服务器发送探测报文,之后每隔 75 分钟发送一次。
- 若连续 10 次探测无响应,服务器判定客户端故障,关闭连接。
九、半连接队列
半连接队列(又称 SYN 队列)是服务器在 TCP 三次握手期间暂存未完成连接的队列:
触发条件与存储内容
- 当服务器收到客户端的
SYN
报文(处于SYN_RCVD
状态)但未收到ACK
时,创建队列条目。 - 存储信息:源 IP / 端口、目标 IP / 端口、服务器 ISN、TCP 选项(如 MSS)等。
- 当服务器收到客户端的
工作流程
- 客户端发送
SYN
→服务器创建条目并加入队列,回复SYN+ACK
。 - 客户端发送
ACK
→服务器将条目从半连接队列移至全连接队列,完成连接。 - 若超时未收到
ACK
,服务器重发SYN+ACK
(默认重试 5 次),超时后删除条目。
- 客户端发送
队列大小控制
由内核参数net.core.somaxconn
和应用层backlog
共同决定,满时新SYN
会被丢弃,导致连接失败。
十、ISN(初始序列号)
- 定义:TCP 连接建立时,客户端和服务器各自生成的 32 位随机数,用于标识数据流的起始点(第一个字节的序列号)。
- 作用:保证数据按序到达、检测重复报文,避免旧连接的延迟报文干扰新连接。
- 特性:不固定,由操作系统通过随机数生成器产生(不可预测),防止序列号预测攻击。
十一、SYN 攻击及防范
- SYN 攻击:一种 DoS 攻击,攻击者伪造大量随机源 IP 的
SYN
报文发送给服务器,占满半连接队列,导致服务器无法处理合法请求。 - 防范方法:
- 增大半连接队列容量(调整
somaxconn
和backlog
)。 - 缩短
SYN_RCVD
状态超时时间(减少资源占用)。 - 部署 SYN 代理(如防火墙伪装服务器回复
SYN+ACK
,验证客户端合法性)。 - 启用
tcp_syncookies
(用 cookie 代替半连接队列存储,避免队列溢出)。
- 增大半连接队列容量(调整
十二、MSL 与 TIME_WAIT 状态
- MSL(报文最大生存时间):TCP 报文在网络中存活的最长时间(通常 30 秒~2 分钟),超过则被路由器丢弃,防止旧报文干扰新连接。
- TIME_WAIT 状态:TCP 四次挥手时,主动关闭方发送最后一个
ACK
后进入的状态,持续2MSL:- 确保最后一个
ACK
被对方收到(未收到则对方重发FIN
,可在 2MSL 内处理)。 - 等待网络中残留的旧报文过期,避免干扰新连接(复用四元组时)。
- 确保最后一个
十三、listen 函数的功能
listen
函数用于将套接字(socket)转为监听状态,功能包括:
- 为套接字设置半连接队列和全连接队列的最大长度(通过
backlog
参数)。 - 允许套接字接收客户端的连接请求(
accept
函数从全连接队列获取连接)。 - 调用后,套接字从
CLOSED
状态变为LISTEN
状态。
十四、TCP 如何保证可靠性
TCP 通过以下机制保证数据传输的可靠性:
- 确认机制:接收方收到数据后发送
ACK
确认,发送方未收到ACK
则重传。 - 重传机制:超时重传(未按时收到
ACK
)、快速重传(收到 3 个重复ACK
)。 - 滑动窗口:通过窗口大小控制发送速率,实现流量控制(避免接收方缓冲区溢出)。
- 拥塞控制:通过慢启动、拥塞避免等算法,防止网络拥塞(如
cwnd
和ssthresh
参数)。 - 连接管理:三次握手建立连接、四次挥手关闭连接,确保双方状态同步。
- 校验和:对报文段进行校验,检测数据传输中的错误。
十五、大量 TIME_WAIT 产生的原因及解决办法
原因:
- 主动关闭连接的一方会进入
TIME_WAIT
(如 HTTP 服务中,服务器主动关闭短连接)。 - 大量短连接导致
TIME_WAIT
堆积,消耗端口和内存资源。
- 主动关闭连接的一方会进入
解决办法:
- 使用 HTTP 长连接(
Connection: keep-alive
),减少连接关闭频率。 - 复用连接(如连接池),避免频繁创建新连接。
- 调整内核参数:
net.ipv4.tcp_tw_reuse=1
(允许复用TIME_WAIT
状态的端口)、net.ipv4.tcp_tw_recycle=1
(快速回收TIME_WAIT
连接,需谨慎使用)。 - 让客户端主动关闭连接(如 Nginx 反向代理,由代理层承担
TIME_WAIT
)。
- 使用 HTTP 长连接(
十六、大量 CLOSE_WAIT 产生的原因及解决办法
原因:
CLOSE_WAIT
是被动关闭方收到FIN
后进入的状态,表明已确认对方关闭请求,但自身未调用close
或shutdown
关闭连接(可能因应用层未处理完数据、代码漏洞导致资源未释放)。解决办法:
- 确保应用程序在处理完数据后,及时调用
close
或shutdown(SHUT_WR)
关闭连接。 - 排查代码漏洞(如死锁、无限循环导致无法执行关闭操作)。
- 监控
CLOSE_WAIT
数量,设置超时自动关闭机制(如通过心跳检测)。
- 确保应用程序在处理完数据后,及时调用
十七、字节序的理解
定义:多字节数据在内存中的存储顺序,仅在异构计算机通信或跨网络传输时需关注。
分类:
- 本地字节序:主机内部的存储方式,分大端序和小端序:
- 大端序:高位字节存于低地址(如
0x1234
存为12 34
)。 - 小端序:低位字节存于低地址(如
0x1234
存为34 12
,x86 架构默认)。
- 大端序:高位字节存于低地址(如
- 网络字节序:跨网络传输的统一标准,固定为大端序,通过
htons
/htonl
等函数将本地字节序转为网络字节序。
- 本地字节序:主机内部的存储方式,分大端序和小端序:
作用:确保不同字节序的设备之间能正确解析数据(如 IP 地址、端口号的传输)。
18. UDP 和 TCP 协议有哪些应用?
一、基于 UDP 协议的应用层协议
UDP 无连接、低延迟的特性适合实时性要求高、容忍少量丢包的场景:
应用层协议 | 默认端口 | 核心场景 | 特性与典型案例 |
---|---|---|---|
DNS | 53 | 域名解析 | - 将域名(如google.com)转换为 IP 地址 - 递归查询时可并行请求多个服务器 - 案例:浏览器访问网站前的域名解析 |
DHCP | 67/68 | IP 地址分配 | - 动态分配 IP 地址、子网掩码等网络配置 - 客户端(68)→ 服务器(67)的广播通信 - 案例:路由器为新设备分配 IP 地址 |
NTP | 123 | 时间同步 | - 精确同步计算机时钟(误差可达毫秒级) - 采用层级时间服务器(Stratum)架构 - 案例:金融交易系统的时间校准 |
SNMP | 161/162 | 网络管理 | - 监控网络设备(如路由器、交换机)状态 - Trap 机制(异步告警)基于 UDP - 案例:企业网络设备的远程监控 |
RTP/RTCP | 动态端口 | 实时音视频 | - RTP:传输媒体数据(如音频帧、视频帧) - RTCP:反馈质量信息(丢包率、抖动) - 案例:Zoom 视频会议、VoIP 电话 |
QUIC | 443 | 下一代 HTTP | - 基于 UDP 实现的低延迟传输协议 - 集成 TLS 加密,解决 TCP 队头阻塞问题 - 案例:Chrome 浏览器的 HTTP/3 实现 |
二、基于 TCP 协议的应用层协议
TCP 可靠连接的特性适合数据完整性要求高、需按序传输的场景:
应用层协议 | 默认端口 | 核心场景 | 特性与典型案例 |
---|---|---|---|
HTTP/HTTPS | 80/443 | Web 访问 | - HTTP:明文传输网页资源 - HTTPS:TLS 加密的 HTTP - 案例:浏览网页、API 调用(如 RESTful 接口) |
FTP | 20/21 | 文件传输 | - 控制连接(21):发送命令 - 数据连接(20):传输文件内容 - 案例:服务器间大文件传输 |
SMTP/POP3/IMAP | 25/110/143 | 电子邮件 | - SMTP:发送邮件 - POP3/IMAP:接收邮件 - 案例:Outlook、Gmail 的邮件收发 |
SSH | 22 | 远程登录 | - 加密的命令行交互 - 支持端口转发(如本地 / 远程转发) - 案例:Linux 服务器的远程管理 |
Telnet | 23 | 远程登录 | - 明文传输(安全性差,逐步被 SSH 取代) - 案例:老旧网络设备的配置(如交换机) |
MySQL/PostgreSQL | 3306/5432 | 数据库连接 | - 关系型数据库的客户端 - 服务器通信 - 支持事务、查询结果的可靠传输 - 案例:Web 应用连接数据库 |
Redis | 6379 | 缓存 / 消息队列 | - 内存数据库的高速读写 - 单线程模型依赖 TCP 的有序性 - 案例:电商网站的商品缓存 |
19. 套接字的作用以及特点
套接字提供不同主机上的进程间的通信。
特点:socket 也称为套接字,是一种文件描述符,代表了一个通信管道的一个端点;类似于对文件的操作一样可以使用 read、write、close 等函数对 socket 套接字进行网络数据的收取和发送等操作;得到套接字的方法是调用 socket () 函数。
20. UDP 网络程序想要收取数据需要什么条件?
确定的 ip 地址和确定的端口。
21. 我们为什么不直接使用 ip 协议而需要额外增加一个 UDP/TCP 协议呢?
一个重要的原因就是 ip 协议中并没有端口的概念。
ip 协议进行的是 ip 地址到 ip 地址的传输,这意味着两台计算机之间的对话。但是每台计算机中需要有多个通信通道,并将多个通信通道分配给不同的进程使用。而一个端口就代表了这样一个通信通道。UDP 协议实现了端口,从而让数据包可以在送到 ip 地址的基础上,进一步可以送到某个特定的端口。
22. 说出几个应用层协议
HTTP、HTTPS、SSH、DNS、FTP、SMTP、DHCP。
23. 说出几个网络层协议
IP、ICMP、ARP、RARP。
24. 为什么 qq 用 UDP,QQ 是如何保证信息传输安全的?
QQ 的文件传输使用的是 TCP;因为有海量的用户数据需要并发,UDP 的速度快;而可靠性靠上层协议来保证(如应用层),发送失败则提示是否需要重新发送。
25. udp 的 connect 和 tcp 的 connect 函数的区别是什么?
1. 连接本质差异(核心区别)
协议 | connect 函数的本质 | 典型场景 |
---|---|---|
TCP | 触发三次握手,真正建立端到端的可靠连接。连接建立后,双方形成 “一对一” 通信关系,通信受 TCP 可靠机制(确认、重传、有序等)保障 | 如 HTTP 访问(需确保数据不丢、有序)、数据库连接(需事务完整性) |
UDP | 不建立真正的 “连接”,只是在 socket 中记录对端的 IP 和端口,限制当前 socket 收发数据的对象范围(逻辑上 “绑定” 通信目标 ) | 如向固定服务器发实时日志(限制 socket 只给该服务器发,避免误发 )、游戏中固定对手通信 |
2. 执行过程区别
- TCP 的 connect:调用后,客户端会主动发 SYN 包,经三次握手(SYN→SYN+ACK→ACK)完成连接。若服务端无响应或网络异常,会触发超时重试或报错(如 Connection refused)。例:用 telnet 192.168.1.100 80 时,connect 会尝试三次握手,失败则提示无法连接。
- UDP 的 connect:调用时,仅把对端 IP、端口存到 socket 内部,无网络交互(不发任何数据包)。即使对端不存在或端口未监听,调用也会 “成功”(实际通信时才可能发现问题)。例:UDP 客户端 connect 到一个不存在的服务器 IP,调用瞬间返回成功,直到 send 数据时才可能因网络问题报错(如路由不可达)。
26. 试比较分析网络互联设备中的网桥和路由器的异同点
网桥工作在数据链路层,仅能连接两个同类型的网络,用于实现网络帧间的转发;而路由器工作在网络层,可以连接三个或者三个以上的同类型网络,用于实现多个网络间分组的路由选择以及转发功能。
27. 世界上第一个网络是在哪一年产生的?
1969 年。
28. 日常使用的无线传输媒体有哪些?
微波、红外线和激光。
29. 谈谈原始套接字 SOCK_RAW 编程的理解
- 是一种不同于 SOCK_STREAM、SOCK_DGRAM 的套接字,它实现于系统核心。
- 可以接收本机网络上所有的数据帧,对于监听网络流量和分析网络数据很有用。
- 开发人员可以发送自己组装的数据包到网络上。
- 广泛应用于高级网络编程。
- 网络专家、黑客通常会用此来编写奇特的网络程序。
30. 说一下原始套接字的发送流程
一、原始套接字发送数据的核心流程
创建原始套接字
int sock = socket(AF_INET, SOCK_RAW, protocol);
- AF_INET:指定 IPv4 协议族。
- SOCK_RAW:创建原始套接字。
- protocol:指定 IP 协议类型(如 IPPROTO_IP、IPPROTO_TCP、IPPROTO_UDP)。
设置套接字选项(可选)
- IP_HDRINCL:若需自行构造 IP 头部,需设置此选项:
- 其他选项:如设置超时、绑定接口等。
int one = 1; setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
- 其他选项:如设置超时、绑定接口等。
- IP_HDRINCL:若需自行构造 IP 头部,需设置此选项:
构造数据包
- 手动构造协议头部(以 UDP 包为例):
a. IP 头部(struct iphdr):包含源 IP、目标 IP、协议类型等。
b. UDP 头部(struct udphdr):包含源端口、目标端口、数据长度等。
c. 应用层数据:自定义数据内容。 - 示例结构:
struct iphdr ip_hdr; struct udphdr udp_hdr; char data[1024]; char packet[sizeof(ip_hdr) + sizeof(udp_hdr) + 1024];
- 手动构造协议头部(以 UDP 包为例):
填充数据包内容
- IP 头部示例:
ip_hdr.ihl = 5; // IP头部长度(4字节×5=20字节) ip_hdr.version = 4; // IPv4 ip_hdr.tos = 0; // 服务类型 ip_hdr.tot_len = sizeof(ip_hdr) + sizeof(udp_hdr) + strlen(data); // 总长度 ip_hdr.id = htons(12345); // 标识 ip_hdr.frag_off = 0; // 分片偏移 ip_hdr.ttl = 64; // 生存时间 ip_hdr.protocol = IPPROTO_UDP; // 协议类型(UDP) ip_hdr.saddr = inet_addr("192.168.1.100"); // 源IP ip_hdr.daddr = inet_addr("192.168.1.81"); // 目标IP ip_hdr.check = 0; // 校验和(计算后填充)
- 计算校验和:
ip_hdr.check = calculate_checksum((unsigned short*)&ip_hdr, ip_hdr.ihl * 4);
- IP 头部示例:
指定目标地址
struct sockaddr_in dest_addr; memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(80); // 目标端口 dest_addr.sin_addr.s_addr = inet_addr("192.168.1.81"); // 目标IP
发送数据包
sendto(sock, packet, total_length, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
- total_length:数据包总长度(IP 头部 + UDP 头部 + 数据)。
关闭套接字
close(sock);
31. 原始套接字的接收流程
原始套接字接收数据的核心流程如下,主要用于捕获和解析网络中的底层数据包(如 IP、TCP、UDP、ICMP 等):
创建原始套接字
int sock = socket(AF_INET, SOCK_RAW, protocol);
protocol
用于指定接收的数据包类型:IPPROTO_IP
:接收所有 IP 数据包;IPPROTO_TCP
:仅接收 TCP 数据包;IPPROTO_UDP
:仅接收 UDP 数据包;IPPROTO_ICMP
:仅接收 ICMP 数据包(如 ping 请求)。
设置套接字选项(可选)
根据需求配置选项,常见如:IP_HDRINCL
:若需手动解析 IP 头部,可设置此选项(接收时可选,发送时更常用);SO_REUSEADDR
:允许地址 / 端口重用;SO_BINDTODEVICE
:绑定到特定网络接口(如"ens33"
),仅接收该接口的数据包。
// 示例:绑定到特定接口 setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "ens33", strlen("ens33"));
绑定套接字(可选)
若需限制接收特定源 IP 的数据包,可绑定到本地 IP:struct sockaddr_in bind_addr; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_addr.s_addr = INADDR_ANY; // 接收所有源IP的数据包 bind(sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr));
接收数据包
使用recvfrom
阻塞接收数据包,存储在缓冲区中,并获取发送方地址:char buffer[65535]; // 缓冲区需足够大(最大IP数据包约65535字节) struct sockaddr_in src_addr; // 发送方地址信息 socklen_t addr_len = sizeof(src_addr); int recv_len = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&src_addr, &addr_len);
recv_len
为实际接收的字节数,若为 - 1 则接收失败(如网络错误)。
解析数据包
根据协议类型逐层解析头部和数据:- IP 头部解析:
struct iphdr *iph = (struct iphdr*)buffer; // IP头部指针 unsigned short ip_hdr_len = iph->ihl * 4; // IP头部长度(单位:字节)
- TCP 数据包解析(若
iph->protocol == IPPROTO_TCP
):struct tcphdr *tcph = (struct tcphdr*)(buffer + ip_hdr_len); // TCP头部指针 unsigned short tcp_hdr_len = tcph->doff * 4; // TCP头部长度 char *tcp_data = buffer + ip_hdr_len + tcp_hdr_len; // TCP数据部分
- UDP 数据包解析(若
iph->protocol == IPPROTO_UDP
):struct udphdr *udph = (struct udphdr*)(buffer + ip_hdr_len); // UDP头部指针 char *udp_data = buffer + ip_hdr_len + sizeof(struct udphdr); // UDP数据部分
- ICMP 数据包解析(若
iph->protocol == IPPROTO_ICMP
):struct icmphdr *icmph = (struct icmphdr*)(buffer + ip_hdr_len); // ICMP头部指针
- IP 头部解析:
提取关键信息
从解析的头部中提取源 IP、目标 IP、端口等信息:// 源IP地址 char src_ip[16]; inet_ntop(AF_INET, &iph->saddr, src_ip, sizeof(src_ip)); // 目标IP地址 char dst_ip[16]; inet_ntop(AF_INET, &iph->daddr, dst_ip, sizeof(dst_ip));
处理数据
根据业务需求处理解析后的数据(如打印日志、过滤特定数据包、存储分析等)。关闭套接字
接收完成后关闭套接字释放资源:close(sock);
32. 判断原始套接字接收的数据包为 ARP 或 ICMP 数据包
通过解析以太网头部的协议类型字段和 IP 头部的协议字段区分:
ARP 数据包判断:
以太网头部(struct ethhdr
)的h_proto
字段(16 位)表示上层协议类型,若为0x0806
(大端序,需用ntohs
转换),则为 ARP 数据包。struct ethhdr *eth = (struct ethhdr*)buffer; if (ntohs(eth->h_proto) == 0x0806) { printf("ARP数据包\n"); }
ICMP 数据包判断:
- 首先判断为 IP 数据包(以太网协议类型
0x0800
); - 再解析 IP 头部的
protocol
字段,若为1
(IPPROTO_ICMP
),则为 ICMP 数据包。
struct ethhdr *eth = (struct ethhdr*)buffer; if (ntohs(eth->h_proto) == 0x0800) { // 是IP数据包 struct iphdr *iph = (struct iphdr*)(buffer + sizeof(struct ethhdr)); if (iph->protocol == IPPROTO_ICMP) { // 协议类型为ICMP printf("ICMP数据包\n"); } }
- 首先判断为 IP 数据包(以太网协议类型
33. 判断原始套接字接收的数据包为 TCP 或 UDP 数据包
需先确认是 IP 数据包,再通过 IP 头部的协议字段区分:
- 判断为 IP 数据包:
以太网头部h_proto
字段为0x0800
(ETH_P_IP
)。 - 区分 TCP 和 UDP:
IP 头部的protocol
字段(8 位):6
:IPPROTO_TCP
,表示 TCP 数据包;17
:IPPROTO_UDP
,表示 UDP 数据包。
struct ethhdr *eth = (struct ethhdr*)buffer;
if (ntohs(eth->h_proto) == ETH_P_IP) { // 是IP数据包
struct iphdr *iph = (struct iphdr*)(buffer + sizeof(struct ethhdr));
if (iph->protocol == IPPROTO_TCP) {
printf("TCP数据包\n");
} else if (iph->protocol == IPPROTO_UDP) {
printf("UDP数据包\n");
}
}
34. 分析与本机有数据包往来的主机 MAC 地址的程序
该程序通过原始套接字捕获所有以太网数据包,解析源 MAC 和目标 MAC 地址:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netpacket/packet.h> // 以太网套接字相关定义
#include <net/ethernet.h> // ethhdr结构定义
#include <unistd.h>
#include <errno.h>
int main() {
// 创建原始套接字,捕获所有以太网类型的数据包(ETH_P_ALL)
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sockfd < 0) {
perror("socket创建失败");
return -1;
}
unsigned char buf[1500]; // 以太网帧最大约1500字节
while (1) {
// 接收数据包(忽略发送方地址,仅关注数据)
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (recv_len < 0) {
perror("接收数据包失败");
continue;
}
// 检查数据包长度是否足够解析以太网头部(至少6字节源MAC+6字节目标MAC+2字节协议类型)
if (recv_len < sizeof(struct ethhdr)) {
continue;
}
// 解析以太网头部
struct ethhdr *eth = (struct ethhdr *)buf;
// 格式化目标MAC地址(6字节,格式如xx:xx:xx:xx:xx:xx)
char dst_mac[18];
sprintf(dst_mac, "%02x:%02x:%02x:%02x:%02x:%02x",
eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
eth->h_dest[3], eth->h_dest[4], eth->h_dest[5]);
// 格式化源MAC地址
char src_mac[18];
sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x",
eth->h_source[0], eth->h_source[1], eth->h_source[2],
eth->h_source[3], eth->h_source[4], eth->h_source[5]);
// 输出MAC地址信息(本机与其他主机的通信)
printf("目标MAC: %s <--> 源MAC: %s\n", dst_mac, src_mac);
}
// 实际运行中需添加退出逻辑(如捕获Ctrl+C信号)
close(sockfd);
return 0;
}
说明:
- 程序需 root 权限运行(原始套接字需要管理员权限);
ETH_P_ALL
表示捕获所有类型的以太网帧,包括 IP、ARP 等;- 输出结果中,
目标MAC
或源MAC
为本机 MAC 时,即表示与本机有数据往来。
35. 分析与本机有数据包往来的主机 IP 地址的程序
该程序通过原始套接字捕获 IP 数据包,解析源 IP 和目标 IP 地址:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h> // 以太网头部
#include <netinet/ip.h> // IP头部(struct iphdr)
#include <unistd.h>
#include <arpa/inet.h> // inet_ntop函数(IP地址格式化)
int main() {
// 创建原始套接字,捕获所有以太网数据包
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sockfd < 0) {
perror("socket创建失败");
return -1;
}
unsigned char buf[1500];
while (1) {
// 接收数据包
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (recv_len < 0) {
perror("接收数据包失败");
continue;
}
// 解析以太网头部,判断是否为IP数据包
struct ethhdr *eth = (struct ethhdr *)buf;
if (ntohs(eth->h_proto) != ETH_P_IP) { // ETH_P_IP = 0x0800(IP协议)
continue; // 非IP数据包,跳过
}
// 检查长度是否足够解析IP头部(以太网头部14字节 + IP头部最小20字节)
if (recv_len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
continue;
}
// 解析IP头部(位于以太网头部之后)
struct iphdr *iph = (struct iphdr *)(buf + sizeof(struct ethhdr));
// 格式化源IP地址(网络字节序转字符串)
char src_ip[INET_ADDRSTRLEN]; // 足够存储IPv4地址的字符串(如"255.255.255.255")
inet_ntop(AF_INET, &iph->saddr, src_ip, sizeof(src_ip));
// 格式化目标IP地址
char dst_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &iph->daddr, dst_ip, sizeof(dst_ip));
// 输出IP地址信息(本机与其他主机的通信)
printf("源IP: %s <--> 目标IP: %s\n", src_ip, dst_ip);
}
// 实际运行中需添加退出逻辑
close(sockfd);
return 0;
}
说明:
- 仅解析 IP 数据包(协议类型
0x0800
),过滤 ARP 等非 IP 数据; inet_ntop
将网络字节序的 IP 地址(32 位整数)转换为可读性字符串;- 输出结果中,
源IP
或目标IP
为本机 IP 时,即表示与本机有数据往来。
36. 飞秋欺骗原理
飞秋(FeiQ)是基于 UDP 协议的局域网即时通信工具,其欺骗原理主要通过伪造 UDP 数据包实现,核心是篡改数据包的源地址信息,使接收方误认为消息来自指定主机:
飞秋通信特点:
飞秋消息通过 UDP 协议传输,数据包中包含发送方的 MAC 地址、IP 地址、用户名等信息,接收方根据这些信息识别发送者。欺骗核心手段:
- 伪造源 MAC 地址:通过原始套接字构造以太网帧,将源 MAC 地址改为目标主机信任的 MAC(如网关或其他用户的 MAC);
- 伪造源 IP 地址:在 IP 头部中,将源 IP 地址改为目标主机信任的 IP(如管理员 IP);
- 伪造应用层信息:在 UDP 数据部分,伪造飞秋的消息格式(如用户名、消息内容),使接收方解析为正常消息。
欺骗过程:
攻击者构造包含伪造信息的 UDP 数据包,发送给目标主机。目标主机收到后,根据伪造的源 IP/MAC 判断发送者身份,从而相信消息的真实性(如接收虚假通知、文件等)。防御方式:
- 网络层:启用 IP/MAC 绑定(如静态 ARP 绑定),防止 IP 与 MAC 不匹配的数据包;
- 应用层:飞秋可开启消息验证(如加密或数字签名),验证发送者身份。
简言之,飞秋欺骗利用了 UDP 协议无连接、缺乏身份验证的特点,通过伪造底层地址和应用层数据,实现 “冒充” 通信。