当前位置: 首页 > web >正文

IP协议、以太网包头及UNIX域套接字

IP协议、以太网包头及UNIX域套接字

IP包头结构

IP协议是互联网的核心协议之一,其包头包含了丰富的信息来控制数据包的传输。让我们详细解析IPv4包头结构:

  1. 4位版本号(version):标识IP协议版本,IPv4值为4

  2. 4位首部长度(header length):以4字节为单位表示IP头长度,基础长度为20字节(5个单位),最大60字节

  3. 8位服务类型(Type Of Service):
    • 3位优先权字段(已弃用)

    • 4位TOS字段:可设置最小延时、最大吞吐量、最高可靠性或最小成本

    • 1位保留字段(必须为0)

  4. 16位总长度(total length):IP报文(头+数据)的总长度

  5. 16位标识(id):唯一标识主机发送的报文,分片报文id相同

  6. 3位标志字段:
    • 第1位:保留

    • 第2位:禁止分片(DF),置1时若报文超MTU则丢弃

    • 第3位:更多分片(MF),分片时除最后一片外都置1

  7. 13位片偏移(fragment offset):分片相对于原始数据的偏移,实际偏移=值×8

  8. 8位生存时间(TTL):最大跳数,防路由循环,每经一跳减1

  9. 8位协议:标识上层协议类型

  10. 16位首部检验和:仅校验头部,不检验数据部分

  11. 32位源IP地址

  12. 32位目标IP地址

  13. 选项字段:可变长,最多40字节

最大传输单元(MTU)

MTU(Maximum Transmission Unit)是数据链路层能传输的最大数据帧大小,默认1500字节。当IP数据包超过MTU时:

• 若DF标志为0,则进行分片

• 若DF标志为1,则丢弃数据包

分片规则:
• 除最后一片外,其他分片长度必须是8的整数倍

• 接收方根据标识、片偏移和MF标志重组数据

以太网包头结构

以太网是应用最广泛的局域网技术,其帧结构如下:

  1. 前导码(Preamble):7字节0x55,用于时钟同步

  2. 帧起始定界符(SFD):1字节0xD5,标识帧开始

  3. 目的MAC地址:6字节
    • 单播地址:首字节最低位为0

    • 组播地址:首字节最低位为1

    • 广播地址:全FF

  4. 源MAC地址:6字节

  5. 类型/长度字段:2字节
    • <0x0600:表示数据长度

    • ≥0x0600:表示协议类型(如0x0800为IP)

  6. 数据:46-1500字节(MTU)

  7. 校验(FCS):4字节CRC校验

  8. 帧间隙(IFG):帧间最小间隔(96位时间)

UNIX域套接字详解

UNIX域套接字用于同一主机上的进程间通信(IPC),比网络套接字更高效。

流式套接字(SOCK_STREAM)

服务器端流程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>#define SOCKET_PATH "/tmp/unix_socket_example"int main() {int server_fd, client_fd;struct sockaddr_un addr;char buffer[100];// 1. 创建套接字server_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 绑定套接字到文件系统路径memset(&addr, 0, sizeof(struct sockaddr_un));addr.sun_family = AF_UNIX;strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);unlink(SOCKET_PATH); // 确保路径没有被占用if (bind(server_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {perror("bind");close(server_fd);exit(EXIT_FAILURE);}// 3. 监听连接请求if (listen(server_fd, 5) == -1) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}printf("服务器正在等待连接...\n");// 4. 接受客户端连接client_fd = accept(server_fd, NULL, NULL);if (client_fd == -1) {perror("accept");close(server_fd);exit(EXIT_FAILURE);}// 5. 与客户端通信memset(buffer, 0, sizeof(buffer));read(client_fd, buffer, sizeof(buffer));printf("收到客户端消息: %s\n", buffer);const char *response = "你好,客户端!";write(client_fd, response, strlen(response) + 1);// 6. 关闭套接字close(client_fd);close(server_fd);unlink(SOCKET_PATH);return 0;
}

客户端流程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>#define SOCKET_PATH "/tmp/unix_socket_example"int main() {int client_fd;struct sockaddr_un addr;char buffer[100];// 1. 创建套接字client_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (client_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 连接到服务器memset(&addr, 0, sizeof(struct sockaddr_un));addr.sun_family = AF_UNIX;strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);if (connect(client_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {perror("connect");close(client_fd);exit(EXIT_FAILURE);}// 3. 与服务器通信const char *message = "你好,服务器!";write(client_fd, message, strlen(message) + 1);memset(buffer, 0, sizeof(buffer));read(client_fd, buffer, sizeof(buffer));printf("收到服务器响应: %s\n", buffer);// 4. 关闭套接字close(client_fd);return 0;
}

数据报套接字(SOCK_DGRAM)

服务器端流程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>#define SOCKET_PATH "/tmp/unix_dgram_socket_example"int main() {int sockfd;struct sockaddr_un addr;char buffer[100];ssize_t num_bytes;struct sockaddr_un client_addr;socklen_t client_addr_len = sizeof(client_addr);// 1. 创建套接字sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);if (sockfd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 绑定套接字memset(&addr, 0, sizeof(addr));addr.sun_family = AF_UNIX;strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);unlink(SOCKET_PATH);if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {perror("bind");close(sockfd);exit(EXIT_FAILURE);}printf("服务器正在等待消息...\n");// 3. 接收消息memset(buffer, 0, sizeof(buffer));num_bytes = recvfrom(sockfd, buffer, sizeof(buffer), 0,(struct sockaddr *)&client_addr, &client_addr_len);if (num_bytes == -1) {perror("recvfrom");close(sockfd);exit(EXIT_FAILURE);}printf("收到客户端消息: %s\n", buffer);// 4. 发送响应const char *response = "你好,客户端!";if (sendto(sockfd, response, strlen(response) + 1, 0,(struct sockaddr *)&client_addr, client_addr_len) == -1) {perror("sendto");close(sockfd);exit(EXIT_FAILURE);}// 5. 关闭套接字close(sockfd);unlink(SOCKET_PATH);return 0;
}

客户端流程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>#define SOCKET_PATH "/tmp/unix_dgram_socket_example"int main() {int sockfd;struct sockaddr_un server_addr;char buffer[100];ssize_t num_bytes;// 1. 创建套接字sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);if (sockfd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 设置服务器地址memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);// 3. 发送消息const char *message = "你好,服务器!";if (sendto(sockfd, message, strlen(message) + 1, 0,(struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("sendto");close(sockfd);exit(EXIT_FAILURE);}// 4. 接收响应memset(buffer, 0, sizeof(buffer));num_bytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);if (num_bytes == -1) {perror("recvfrom");close(sockfd);exit(EXIT_FAILURE);}printf("收到服务器响应: %s\n", buffer);// 5. 关闭套接字close(sockfd);return 0;
}

sockaddr_un结构体

#include <sys/un.h>struct sockaddr_un {sa_family_t sun_family;  // 地址族,设置为AF_UNIXchar sun_path[108];      // 文件系统路径名
};

• sun_family:必须设置为AF_UNIX

• sun_path:最大长度108字节(包括终止符),通常以/tmp/开头

注意事项

  1. 使用UNIX域套接字前应调用unlink()确保路径可用
  2. 通信完成后应删除套接字文件
  3. 数据报套接字是无连接的,需在每次通信时指定地址
  4. 流式套接字提供可靠的双向字节流通信
http://www.xdnf.cn/news/5738.html

相关文章:

  • 在 Java 8 中 常用时间日期类
  • 【Linux系统】自动化构建-make/Makefile的使用
  • AI Agent开发第64课-DIFY和企业现有系统结合实现高可配置的智能零售AI Agent(上)
  • #S4U2SELF#S4U2Proxy#CVE-2021-42278/42287
  • 按指定位置或关键字批量删除工作表-Excel易用宝
  • 关系实验课--笛卡尔积
  • cURL:通过URL传输数据的命令行工具库介绍
  • 请求参数:Header 参数,Body 参数,Path 参数,Query 参数分别是什么意思,什么样的,分别通过哪个注解获取其中的信息
  • 每日算法刷题Day4 5.12:leetcode数组4道题,用时1h
  • zabbix6.4监控主机并触发邮件告警
  • Egg.js知识框架
  • Linux驱动:驱动编译流程了解
  • 向量组的维度是单个向量中元素的个数
  • Vue3的命名规范
  • 从ES5到ES6+:JavaScript语法演进与实现解析
  • 《汽车软件升级通用技术要求》 GB 44496-2024——解读
  • 仿函数和函数对象
  • Java中堆栈
  • vue实现进度条带指针
  • Elasticsearch 字段映射与数据类型
  • 面试专栏-03-Git的常用命令
  • 异构计算时代:混合编程的崛起与未来
  • 大型视频学习平台项目问题解决笔记
  • Megatron系列——流水线并行
  • KUKA机器人安装包选项KUKA.PLC mxAutomation软件
  • 产品功能更新迭代后需要重做算法备案吗?
  • Linux系统管理与编程20:Apache
  • 关于mac配置hdc(鸿蒙)
  • Nginx部署前端项目深度解析
  • 使用 Syncthing 在两台电脑之间同步文件:简单教程