网络相关面试题总结

发布于:2025-08-09 ⋅ 阅读:(13) ⋅ 点赞:(0)

一、网络的分层结构

网络分层结构是为了简化网络设计和实现,将复杂的网络通信过程按功能划分为不同层次,每层专注于特定任务并为上层提供服务。常见的分层模型有七层模型(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 七层模型的简化,合并了部分功能相似的层次,更贴合实际网络应用:

  1. 物理层(同七层模型)
  2. 数据链路层(同七层模型)
  3. 网络层(同七层模型)
  4. 传输层(同七层模型)
  5. 应用层(合并了 OSI 的会话层、表示层和应用层,直接提供应用服务)

二、网络程序的设计模式

网络程序的设计模式定义了客户端与服务器(或节点)之间的通信架构,常见模式包括:

  1. C/S 模式(客户端 / 服务器模式)

    • 客户端与服务器分工明确:客户端负责用户交互(如界面展示、数据输入)和部分业务逻辑,服务器负责数据存储、资源管理和核心业务处理。
    • 特点:客户端需单独安装,与服务器直接通信,响应速度快,安全性高。
    • 示例:QQ、微信 PC 端、数据库客户端(如 MySQL Workbench)。
  2. B/S 模式(浏览器 / 服务器模式)

    • 以浏览器作为客户端,用户通过 URL 访问服务器提供的 Web 服务,无需安装额外软件。
    • 特点:跨平台性强(只需浏览器),维护成本低(只需更新服务器),但依赖网络稳定性。
    • 示例:淘宝网页版、在线文档(如 Google Docs)。
  3. P2P 模式(对等网络模式)

    • 无中心化服务器,每个节点(计算机)既可以作为客户端(请求资源),也可以作为服务器(提供资源),节点之间直接通信。
    • 特点:去中心化,抗故障能力强,资源共享效率高,但节点管理复杂。
    • 示例:区块链网络、BitTorrent(文件共享)、Skype(点对点通话)。
  4. 分布式架构

    • 将系统拆分为多个独立的服务(如用户服务、支付服务),服务之间通过网络协议(如 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 的优点

selectpollepoll都是 Linux 中用于 I/O 多路复用的机制,poll相较于select的优点如下:

维度 select poll
数据结构 位图(Bitmap) 链表(数组)
最大连接数 FD_SETSIZE限制(通常 1024) 无硬性限制(取决于内存)
内核 / 用户空间交互 每次调用需复制整个描述符集合 每次调用需复制整个描述符集合
适用场景 连接数少且活跃的场景 连接数较多但活跃度高的场景

核心优点poll通过链表存储文件描述符,突破了select的 1024 连接数限制,能处理更多并发连接。

七、TCP 三次握手的过程

TCP 三次握手是建立可靠连接的过程,确保双方都能收发数据:

  1. 第一次握手(客户端→服务器)
    客户端发送一个SYN(同步)报文,包含客户端的初始序列号(ISN_c),状态变为SYN_SENT

  2. 第二次握手(服务器→客户端)
    服务器收到SYN后,回复SYN+ACK报文:

    • SYN:包含服务器的初始序列号(ISN_s);
    • ACK:确认客户端的SYN(确认号 = ISN_c+1)。
      服务器状态变为SYN_RCVD
  3. 第三次握手(客户端→服务器)
    客户端收到SYN+ACK后,发送ACK报文(确认号 = ISN_s+1),状态变为ESTABLISHED
    服务器收到ACK后,状态也变为ESTABLISHED,连接正式建立。

八、客户端故障后的连接处理

若 TCP 连接已建立,客户端突然故障,服务器通过保活计时器释放资源:

  • 服务器每收到客户端数据,重置保活计时器(默认 2 小时)。
  • 若 2 小时内无客户端数据,服务器发送探测报文,之后每隔 75 分钟发送一次。
  • 若连续 10 次探测无响应,服务器判定客户端故障,关闭连接。

九、半连接队列

半连接队列(又称 SYN 队列)是服务器在 TCP 三次握手期间暂存未完成连接的队列:

  1. 触发条件与存储内容

    • 当服务器收到客户端的SYN报文(处于SYN_RCVD状态)但未收到ACK时,创建队列条目。
    • 存储信息:源 IP / 端口、目标 IP / 端口、服务器 ISN、TCP 选项(如 MSS)等。
  2. 工作流程

    • 客户端发送SYN→服务器创建条目并加入队列,回复SYN+ACK
    • 客户端发送ACK→服务器将条目从半连接队列移至全连接队列,完成连接。
    • 若超时未收到ACK,服务器重发SYN+ACK(默认重试 5 次),超时后删除条目。
  3. 队列大小控制
    由内核参数net.core.somaxconn和应用层backlog共同决定,满时新SYN会被丢弃,导致连接失败。

十、ISN(初始序列号)

  • 定义:TCP 连接建立时,客户端和服务器各自生成的 32 位随机数,用于标识数据流的起始点(第一个字节的序列号)。
  • 作用:保证数据按序到达、检测重复报文,避免旧连接的延迟报文干扰新连接。
  • 特性:不固定,由操作系统通过随机数生成器产生(不可预测),防止序列号预测攻击。

十一、SYN 攻击及防范

  • SYN 攻击:一种 DoS 攻击,攻击者伪造大量随机源 IP 的SYN报文发送给服务器,占满半连接队列,导致服务器无法处理合法请求。
  • 防范方法
    • 增大半连接队列容量(调整somaxconnbacklog)。
    • 缩短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 通过以下机制保证数据传输的可靠性:

  1. 确认机制:接收方收到数据后发送ACK确认,发送方未收到ACK则重传。
  2. 重传机制:超时重传(未按时收到ACK)、快速重传(收到 3 个重复ACK)。
  3. 滑动窗口:通过窗口大小控制发送速率,实现流量控制(避免接收方缓冲区溢出)。
  4. 拥塞控制:通过慢启动、拥塞避免等算法,防止网络拥塞(如cwndssthresh参数)。
  5. 连接管理:三次握手建立连接、四次挥手关闭连接,确保双方状态同步。
  6. 校验和:对报文段进行校验,检测数据传输中的错误。

十五、大量 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)。

十六、大量 CLOSE_WAIT 产生的原因及解决办法

  • 原因
    CLOSE_WAIT是被动关闭方收到FIN后进入的状态,表明已确认对方关闭请求,但自身未调用closeshutdown关闭连接(可能因应用层未处理完数据、代码漏洞导致资源未释放)。

  • 解决办法

    • 确保应用程序在处理完数据后,及时调用closeshutdown(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 编程的理解

  1. 是一种不同于 SOCK_STREAM、SOCK_DGRAM 的套接字,它实现于系统核心。
  2. 可以接收本机网络上所有的数据帧,对于监听网络流量和分析网络数据很有用。
  3. 开发人员可以发送自己组装的数据包到网络上。
  4. 广泛应用于高级网络编程。
  5. 网络专家、黑客通常会用此来编写奇特的网络程序。

30. 说一下原始套接字的发送流程

一、原始套接字发送数据的核心流程
  1. 创建原始套接字

    int sock = socket(AF_INET, SOCK_RAW, protocol);
    
    • AF_INET:指定 IPv4 协议族。
    • SOCK_RAW:创建原始套接字。
    • protocol:指定 IP 协议类型(如 IPPROTO_IP、IPPROTO_TCP、IPPROTO_UDP)。
  2. 设置套接字选项(可选)

    • IP_HDRINCL:若需自行构造 IP 头部,需设置此选项:
      • 其他选项:如设置超时、绑定接口等。
        int one = 1;
        setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
        
  3. 构造数据包

    • 手动构造协议头部(以 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];
    
  4. 填充数据包内容

    • 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);
    
  5. 指定目标地址

    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
    
  6. 发送数据包

    sendto(sock, packet, total_length, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
    
    • total_length:数据包总长度(IP 头部 + UDP 头部 + 数据)。
  7. 关闭套接字

close(sock);

31. 原始套接字的接收流程

原始套接字接收数据的核心流程如下,主要用于捕获和解析网络中的底层数据包(如 IP、TCP、UDP、ICMP 等):

  1. 创建原始套接字

    int sock = socket(AF_INET, SOCK_RAW, protocol);
    
    • protocol 用于指定接收的数据包类型:
      • IPPROTO_IP:接收所有 IP 数据包;
      • IPPROTO_TCP:仅接收 TCP 数据包;
      • IPPROTO_UDP:仅接收 UDP 数据包;
      • IPPROTO_ICMP:仅接收 ICMP 数据包(如 ping 请求)。
  2. 设置套接字选项(可选)
    根据需求配置选项,常见如:

    • IP_HDRINCL:若需手动解析 IP 头部,可设置此选项(接收时可选,发送时更常用);
    • SO_REUSEADDR:允许地址 / 端口重用;
    • SO_BINDTODEVICE:绑定到特定网络接口(如"ens33"),仅接收该接口的数据包。
    // 示例:绑定到特定接口
    setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "ens33", strlen("ens33"));
    
  3. 绑定套接字(可选)
    若需限制接收特定源 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));
    
  4. 接收数据包
    使用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 则接收失败(如网络错误)。
  5. 解析数据包
    根据协议类型逐层解析头部和数据:

    • 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头部指针
      
  6. 提取关键信息
    从解析的头部中提取源 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));
    
  7. 处理数据
    根据业务需求处理解析后的数据(如打印日志、过滤特定数据包、存储分析等)。

  8. 关闭套接字
    接收完成后关闭套接字释放资源:

    close(sock);
    

32. 判断原始套接字接收的数据包为 ARP 或 ICMP 数据包

通过解析以太网头部的协议类型字段和 IP 头部的协议字段区分:

  1. ARP 数据包判断
    以太网头部(struct ethhdr)的h_proto字段(16 位)表示上层协议类型,若为0x0806(大端序,需用ntohs转换),则为 ARP 数据包。

    struct ethhdr *eth = (struct ethhdr*)buffer;
    if (ntohs(eth->h_proto) == 0x0806) {
        printf("ARP数据包\n");
    }
    
  2. ICMP 数据包判断

    • 首先判断为 IP 数据包(以太网协议类型0x0800);
    • 再解析 IP 头部的protocol字段,若为1IPPROTO_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");
        }
    }
    

33. 判断原始套接字接收的数据包为 TCP 或 UDP 数据包

需先确认是 IP 数据包,再通过 IP 头部的协议字段区分:

  1. 判断为 IP 数据包
    以太网头部h_proto字段为0x0800ETH_P_IP)。
  2. 区分 TCP 和 UDP
    IP 头部的protocol字段(8 位):
    • 6IPPROTO_TCP,表示 TCP 数据包;
    • 17IPPROTO_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 数据包实现,核心是篡改数据包的源地址信息,使接收方误认为消息来自指定主机:

  1. 飞秋通信特点
    飞秋消息通过 UDP 协议传输,数据包中包含发送方的 MAC 地址、IP 地址、用户名等信息,接收方根据这些信息识别发送者。

  2. 欺骗核心手段

    • 伪造源 MAC 地址:通过原始套接字构造以太网帧,将源 MAC 地址改为目标主机信任的 MAC(如网关或其他用户的 MAC);
    • 伪造源 IP 地址:在 IP 头部中,将源 IP 地址改为目标主机信任的 IP(如管理员 IP);
    • 伪造应用层信息:在 UDP 数据部分,伪造飞秋的消息格式(如用户名、消息内容),使接收方解析为正常消息。
  3. 欺骗过程
    攻击者构造包含伪造信息的 UDP 数据包,发送给目标主机。目标主机收到后,根据伪造的源 IP/MAC 判断发送者身份,从而相信消息的真实性(如接收虚假通知、文件等)。

  4. 防御方式

    • 网络层:启用 IP/MAC 绑定(如静态 ARP 绑定),防止 IP 与 MAC 不匹配的数据包;
    • 应用层:飞秋可开启消息验证(如加密或数字签名),验证发送者身份。

简言之,飞秋欺骗利用了 UDP 协议无连接、缺乏身份验证的特点,通过伪造底层地址和应用层数据,实现 “冒充” 通信。

https://github.com/0voice


网站公告

今日签到

点亮在社区的每一天
去签到