10、Linux C 网络编程(完整版)

1、网络发展历史和分层

1.1 Internet 的历史
  • 起源

    • 1957 年:苏联发射第一颗人造卫星 "Sputnik"。

    • 1958 年:美国总统艾森豪威尔成立 DARPA(国防部高级研究计划署)。

    • 1968 年:DARPA 提出 "资源共享计算机网络"(ARPAnet),成为 Internet 的雏形。

  • TCP/IP 协议的诞生

    • 1973 年:Robert Kahn 和 Vinton Cerf 开发了新的互联协议。

    • 1974 年:TCP 协议首次发布,但存在数据包丢失时无法有效纠正的问题。

    • 1974 年后:TCP 协议拆分为 TCP 和 IP 两个独立协议。

    • 1983 年:ARPAnet 停止使用 NCP,全面采用 TCP/IP 协议。

1.2 网络的体系结构
  • 分层设计:将网络功能划分为不同模块,每层实现特定功能,对外部透明。

  • 两种体系结构

    • OSI 模型:七层模型(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)。

    • TCP/IP 模型:四层模型(网络接口层、网络层、传输层、应用层)。

1.3 TCP/IP 协议通信模型
  • 数据封装与传递过程

    • 应用层数据 → 传输层(TCP/UDP)→ 网络层(IP)→ 数据链路层(以太网帧)→ 物理层。

  • 数据包结构

    • 包含以太网头部、IP 头部、TCP/UDP 头部、应用层头部和用户数据。

2、TCP 和 UDP 协议特点

2.1 TCP 协议特点
  • 面向连接:提供高可靠性通信(无丢失、无失序、无重复)。

  • 适用场景

    • 传输大量数据或对质量要求较高的场景。

    • 即时通讯软件的用户登录管理。

2.2 UDP 协议特点
  • 无连接:高效传输,但不可靠。

  • 适用场景

    • 小数据量传输(如 DNS 查询)。

    • 广播/组播通信。

    • 实时数据传输(如流媒体、VoIP)。

3、网络编程预备知识

3.1 Socket 简介
  • 定义:Socket 是一种编程接口,用于网络通信。

  • 类型

    • 流式套接字(SOCK_STREAM):面向连接,可靠。

    • 数据报套接字(SOCK_DGRAM):无连接,不可靠。

    • 原始套接字(SOCK_RAW):直接访问底层协议。

3.2 IP 地址
  • IPv4:32 位,点分十进制表示(如 192.168.1.1)。

  • IPv6:128 位。

  • 子网掩码:用于区分网络部分和主机部分。

3.3 端口号
  • 范围

    • 众所周知端口:1~1023。

    • 注册端口:1024~49150。

    • 动态端口:49151~65535。

  • TCP 和 UDP 端口独立

3.4 字节序
  • 主机字节序(HBO):因 CPU 架构不同而不同(如 Intel 为小端序,ARM 为大端序)。

  • 网络字节序(NBO):统一为大端序。

  • 转换函数

    • htonl()htons():主机字节序转网络字节序。

    • ntohl()ntohs():网络字节序转主机字节序。

3.5 IP 地址转换
  • inet_aton():字符串转 32 位网络字节序。

  • inet_ntoa():32 位网络字节序转点分十进制字符串。

4、TCP/IP 网络编程

4.1 网络编程 API
4.1.1 socket() 函数
  • 作用:创建一个套接字。

  • 原型

    int socket(int domain, int type, int protocol);
  • 参数

    • domain:协议族(如 AF_INET 表示 IPv4,AF_INET6 表示 IPv6)。

    • type:套接字类型(如 SOCK_STREAM 表示 TCP,SOCK_DGRAM 表示 UDP)。

    • protocol:协议类型(通常为 0,表示默认协议)。

  • 返回值:成功返回套接字描述符,失败返回 -1。

  • 示例

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {perror("创建套接字失败");return -1;
    }
    printf("创建套接字成功\n");

4.1.2 bind() 函数
  • 作用:将套接字绑定到指定的地址和端口。

  • 原型

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数

    • sockfd:套接字描述符。

    • addr:指向 sockaddr 结构的指针,包含地址信息。

    • addrlenaddr 的长度。

  • 返回值:成功返回 0,失败返回 -1。

  • 示例

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    ​
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;
    }
    printf("绑定成功\n");

4.1.3 listen() 函数
  • 作用:将套接字设置为监听状态。

  • 原型

    int listen(int sockfd, int backlog);
  • 参数

    • sockfd:套接字描述符。

    • backlog:最大连接请求数。

  • 返回值:成功返回 0,失败返回 -1。

  • 示例

    if (listen(sockfd, 5) == -1) {perror("监听失败");close(sockfd);return -1;
    }
    printf("开始监听\n");

4.1.4 accept() 函数
  • 作用:接受客户端连接请求。

  • 原型

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 参数

    • sockfd:监听套接字描述符。

    • addr:指向 sockaddr 结构的指针,用于存储客户端地址。

    • addrlenaddr 的长度。

  • 返回值:成功返回新套接字描述符,失败返回 -1。

  • 示例

    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
    if (clientfd == -1) {perror("接受连接失败");close(sockfd);return -1;
    }
    printf("客户端连接成功\n");

4.1.5 connect() 函数
  • 作用:主动连接服务器。

  • 原型

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 参数

    • sockfd:套接字描述符。

    • addr:指向 sockaddr 结构的指针,包含服务器地址信息。

    • addrlenaddr 的长度。

  • 返回值:成功返回 0,失败返回 -1。

  • 示例

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("连接失败");close(sockfd);return -1;
    }
    printf("连接服务器成功\n");

4.1.6 send()recv() 函数
  • send() 作用:发送数据。

  • send() 原型

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
  • recv() 作用:接收数据。

  • recv() 原型

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
  • 参数

    • sockfd:套接字描述符。

    • buf:数据缓冲区。

    • len:缓冲区长度。

    • flags:控制标志。

  • 返回值:成功返回发送/接收的字节数,失败返回 -1。

  • 示例

    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));
    strcpy(buffer, "你好,服务器");
    if (send(sockfd, buffer, strlen(buffer), 0) == -1) {perror("发送失败");close(sockfd);return -1;
    }
    printf("发送数据成功\n");int bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);
    if (bytes_read <= 0) {perror("接收失败");close(sockfd);return -1;
    }
    printf("收到服务器回复:%s\n", buffer);

4.1.7 close()shutdown() 函数
  • close() 作用:关闭套接字。

  • close() 原型

    int close(int sockfd);
    
  • shutdown() 作用:关闭套接字的读或写操作。

  • shutdown() 原型

    int shutdown(int sockfd, int how);
    
  • 参数

    • howSHUT_RD 表示关闭读,SHUT_WR 表示关闭写,SHUT_RDWR 表示关闭读和写。

  • 返回值:成功返回 0,失败返回 -1。

  • 示例

    close(sockfd);
    printf("关闭套接字成功\n");
    

5、TCP 编程模型

5.1 循环服务器
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("创建套接字失败");return -1;}printf("创建套接字成功\n");struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;}printf("绑定成功\n");if (listen(sockfd, 5) == -1) {perror("监听失败");close(sockfd);return -1;}printf("开始监听\n");while (1) {struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);if (clientfd == -1) {perror("接受连接失败");close(sockfd);return -1;}printf("客户端连接成功\n");char buffer[1024];while (1) {int bytes_read = recv(clientfd, buffer, sizeof(buffer), 0);if (bytes_read <= 0) {if (bytes_read == 0) {printf("客户端断开连接\n");} else {perror("接收失败");}break;}buffer[bytes_read] = '\0';printf("收到客户端消息:%s\n", buffer);send(clientfd, buffer, bytes_read, 0);}close(clientfd);printf("关闭客户端连接\n");}close(sockfd);return 0;
}
5.2 多进程/多线程并发服务器
  • 多进程示例

    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>void handle_client(int clientfd) {char buffer[1024];while (1) {int bytes_read = recv(clientfd, buffer, sizeof(buffer), 0);if (bytes_read <= 0) {if (bytes_read == 0) {printf("客户端断开连接\n");} else {perror("接收失败");}break;}buffer[bytes_read] = '\0';printf("收到客户端消息:%s\n", buffer);send(clientfd, buffer, bytes_read, 0);}close(clientfd);printf("关闭客户端连接\n");exit(0);
    }int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("创建套接字失败");return -1;}printf("创建套接字成功\n");struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;}printf("绑定成功\n");if (listen(sockfd, 5) == -1) {perror("监听失败");close(sockfd);return -1;}printf("开始监听\n");while (1) {struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);if (clientfd == -1) {perror("接受连接失败");close(sockfd);return -1;}printf("客户端连接成功\n");if (fork() == 0) {close(sockfd);handle_client(clientfd);} else {close(clientfd);}}close(sockfd);return 0;
    }

6、UDP 编程模型

6.1 循环服务器
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd == -1) {perror("创建套接字失败");return -1;}printf("创建套接字成功\n");struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;}printf("绑定成功\n");char buffer[1024];struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);while (1) {int bytes_read = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &client_len);if (bytes_read <= 0) {perror("接收失败");continue;}buffer[bytes_read] = '\0';printf("收到客户端消息:%s\n", buffer);sendto(sockfd, buffer, bytes_read, 0, (struct sockaddr *)&client_addr, client_len);}close(sockfd);return 0;
}

7、网络调试和协议分析

7.1 工具
  • Wireshark:图形化网络抓包工具,用于分析网络封包。

  • tcpdump:命令行网络抓包工具。

8、I/O 模型和多路复用模型

8.1 I/O 模型
  • 阻塞 I/O

    • 套接字默认为阻塞模式。

    • 读写操作(如 readwriteacceptconnect)会阻塞进程,直到操作完成。

  • 非阻塞 I/O

    • 套接字设置为非阻塞模式后,I/O 操作无法立即完成时会立即返回错误。

    • 需要轮询(polling)检查 I/O 是否就绪,浪费 CPU 资源。

  • I/O 多路复用

    • 同时监控多个文件描述符的 I/O 状态。

    • 使用 select()poll() 函数实现。

  • 信号驱动 I/O

    • 异步通信模型,通过信号通知 I/O 事件。

8.2 阻塞 I/O 的问题
  • 读操作:如果套接字接收缓冲区没有数据,read 会阻塞,直到数据到达。

  • 写操作:如果发送缓冲区空间不足,write 会阻塞,直到缓冲区有足够空间。

  • UDP 协议中不存在发送缓冲区满的情况,写操作不会阻塞。

8.3 非阻塞模式的实现
  • 使用 fcntl()ioctl() 函数将套接字设置为非阻塞模式。

  • 示例代码

    int flag = fcntl(sockfd, F_GETFL, 0);
    flag |= O_NONBLOCK;
    fcntl(sockfd, F_SETFL, flag);
    
8.4 I/O 多路复用
  • select() 函数

    • 监控多个文件描述符的读、写和异常事件。

    • 参数:

      • maxfd:监控的文件描述符中最大的值加 1。

      • read_fdswrite_fdsexcept_fds:文件描述符集合。

      • timeout:超时时间。

  • poll() 函数

    • 类似 select(),但更节省空间,适合监控大量文件描述符。

9、网络分析测试工具

9.1 常用工具
  • Wireshark:图形化网络抓包工具,用于分析网络封包。

  • tcpdump:命令行网络抓包工具。

  • telnet:测试 TCP 服务器端。

  • netstat:显示网络连接、路由表等信息。

  • lsof:列出当前系统打开的文件和网络连接。

  • sniffer:网络协议分析工具。

9.2 网络封包格式
  • 以太网头:包含源和目的 MAC 地址。

  • IP 头:包含源和目的 IP 地址、协议类型等。

  • TCP/UDP 头:包含源和目的端口号、序列号等。

  • 应用层数据:用户数据。

10、TCP 握手过程

10.1 三次握手
  1. 客户端发送 SYN 包到服务器。

  2. 服务器回复 SYN-ACK 包。

  3. 客户端发送 ACK 包,建立连接。

10.2 四次挥手
  1. 客户端发送 FIN 包,请求关闭连接。

  2. 服务器回复 ACK 包,确认收到 FIN。

  3. 服务器发送 FIN 包,请求关闭连接。

  4. 客户端回复 ACK 包,确认收到 FIN。

11、网络信息检索和设置

11.1 网络信息检索函数
  • gethostname():获取主机名。

  • getpeername():获取远程协议地址。

  • getsockname():获取本地套接口地址。

  • gethostbyname():根据主机名获取主机信息。

  • getservbyname():根据服务名获取服务信息。

11.2 网络属性设置
  • 使用 setsockopt()getsockopt() 设置和获取套接字选项。

  • 常见选项:

    • SO_REUSEADDR:允许重用本地地址和端口。

    • SO_KEEPALIVE:保持连接。

    • SO_RCVTIMEOSO_SNDTIMEO:设置接收和发送超时。

11.3 网络超时检测
  • 方法一:设置 socket 属性 SO_RCVTIMEO

  • 方法二:使用 select() 检测 socket 是否就绪。

  • 方法三:设置定时器捕获 SIGALRM 信号。

12、思考与应用

12.1 如何动态检查网络连接状态?
  • 应用层:使用心跳检测机制。

  • 内核层:启用周期性检查定时器(如 2.6 内核)。

  • 硬件层:通过网卡硬件或 GPIO 检测网线插拔。

13、广播和组播

13.1 广播
  • 定义:将数据包发送给局域网中的所有主机。

  • 实现方式:使用 UDP 协议套接字。

  • 广播地址

    • 以 192.168.1.0 网段为例,广播地址为 192.168.1.255。

    • 255.255.255.255 代表所有网段的广播地址。

  • 发送广播

    • 创建 UDP 套接字。

    • 使用 setsockopt() 设置套接字为广播模式。

    • 指定广播地址和端口。

    • 使用 sendto() 发送数据包。

  • 接收广播

    • 创建 UDP 套接字。

    • 绑定本机 IP 地址和端口。

    • 使用 recvfrom() 接收数据。

13.2 组播
  • 定义:将数据包发送给特定多播组中的主机。

  • 优势:避免广播风暴,减少网络负载。

  • 组播地址范围:224.0.0.1 – 239.255.255.255。

  • 发送组播

    • 创建 UDP 套接字。

    • 指定组播地址和端口。

    • 使用 sendto() 发送数据包。

  • 接收组播

    • 创建 UDP 套接字。

    • 加入多播组(使用 setsockopt())。

    • 绑定本机 IP 地址和端口。

    • 使用 recvfrom() 接收数据。

  • 加入多播组示例

    struct ip_mreq mreq;
    bzero(&mreq, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr("235.10.10.3");
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

14、UNIX 域套接字

14.1 定义
  • 用于本地进程间通信的套接字。

14.2 类型
  • 流式套接字:提供可靠、有序的通信。

  • 数据报套接字:提供无连接、不可靠的通信。

14.3 地址结构
  • struct sockaddr_un

    struct sockaddr_un {sa_family_t sun_family;char sun_path[108]; // 套接字文件的路径
    };
    
  • 填充地址结构示例

    struct sockaddr_un myaddr;
    bzero(&myaddr, sizeof(myaddr));
    myaddr.sun_family = AF_UNIX;
    strcpy(myaddr.sun_path, "/tmp/mysocket");

14.4 流式套接字使用示例
  • 服务器端

    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, "/tmp/mysocket");if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;
    }
    printf("绑定成功\n");if (listen(sockfd, 5) == -1) {perror("监听失败");close(sockfd);return -1;
    }
    printf("开始监听\n");struct sockaddr_un client_addr;
    socklen_t client_len = sizeof(client_addr);
    int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
    if (clientfd == -1) {perror("接受连接失败");close(sockfd);return -1;
    }
    printf("客户端连接成功\n");char buffer[1024];
    while (1) {int bytes_read = recv(clientfd, buffer, sizeof(buffer), 0);if (bytes_read <= 0) {if (bytes_read == 0) {printf("客户端断开连接\n");} else {perror("接收失败");}break;}buffer[bytes_read] = '\0';printf("收到客户端消息:%s\n", buffer);send(clientfd, buffer, bytes_read, 0);
    }close(clientfd);
    close(sockfd);
    unlink("/tmp/mysocket");
    printf("关闭连接并删除套接字文件\n");

  • 客户端

    int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, "/tmp/mysocket");if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("连接失败");close(sockfd);return -1;
    }
    printf("连接服务器成功\n");char buffer[1024];
    while (1) {printf("请输入消息:");fgets(buffer, sizeof(buffer), stdin);int len = strlen(buffer);if (buffer[len - 1] == '\n') {buffer[len - 1] = '\0';}if (send(sockfd, buffer, strlen(buffer), 0) == -1) {perror("发送失败");close(sockfd);return -1;}printf("发送消息:%s\n", buffer);int bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);if (bytes_read <= 0) {if (bytes_read == 0) {printf("服务器断开连接\n");} else {perror("接收失败");}break;}buffer[bytes_read] = '\0';printf("收到服务器回复:%s\n", buffer);
    }close(sockfd);
    printf("关闭连接\n");

14.5 数据报套接字使用示例
  • 服务器端

    int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    struct sockaddr_un server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, "/tmp/mysocket");if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;
    }
    printf("绑定成功\n");char buffer[1024];
    struct sockaddr_un client_addr;
    socklen_t client_len = sizeof(client_addr);while (1) {int bytes_read = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &client_len);if (bytes_read <= 0) {perror("接收失败");continue;}buffer[bytes_read] = '\0';printf("收到客户端消息:%s\n", buffer);sendto(sockfd, buffer, bytes_read, 0, (struct sockaddr *)&client_addr, client_len);
    }close(sockfd);
    unlink("/tmp/mysocket");
    printf("关闭连接并删除套接字文件\n");

  • 客户端

    int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    struct sockaddr_un server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strcpy(server_addr.sun_path, "/tmp/mysocket");struct sockaddr_un client_addr;
    bzero(&client_addr, sizeof(client_addr));
    client_addr.sun_family = AF_UNIX;
    strcpy(client_addr.sun_path, "/tmp/myclientsocket");if (bind(sockfd, (struct sockaddr *)&client_addr, sizeof(client_addr)) == -1) {perror("绑定失败");close(sockfd);return -1;
    }
    printf("绑定成功\n");if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("连接失败");close(sockfd);return -1;
    }
    printf("连接服务器成功\n");char buffer[1024];
    while (1) {printf("请输入消息:");fgets(buffer, sizeof(buffer), stdin);int len = strlen(buffer);if (buffer[len - 1] == '\n') {buffer[len - 1] = '\0';}if (sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("发送失败");close(sockfd);return -1;}printf("发送消息:%s\n", buffer);struct sockaddr_un from_addr;socklen_t from_len = sizeof(from_addr);int bytes_read = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&from_addr, &from_len);if (bytes_read <= 0) {perror("接收失败");continue;}buffer[bytes_read] = '\0';printf("收到服务器回复:%s\n", buffer);
    }close(sockfd);
    unlink("/tmp/myclientsocket");
    printf("关闭连接并删除套接字文件\n");

15、网络总结

15.1 网络编程的关键点
  • Socket 编程:掌握不同类型的套接字及其使用方法。

  • I/O 模型:理解阻塞、非阻塞、多路复用等模型。

  • 网络工具:熟练使用 Wireshark、tcpdump 等工具。

  • 网络协议:熟悉 TCP/IP 协议族及其应用。

15.2 应用场景
  • 单播:点对点通信。

  • 广播:局域网内所有主机通信。

  • 组播:特定多播组内的主机通信。

  • UNIX 域套接字:本地进程间通信。

【至此Linux C系列文章完毕,会不断的优化完整。文章仅用于分享学习以及自己复习使用,若有什么问题,麻烦指出,谢谢】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74230.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端:开源软件镜像站 / 清华大学开源软件镜像站 / 阿里云 / 网易 / 搜狐

一、理解开源软件镜像站 开源软件镜像是指开源软件在远程服务器上的备份副本&#xff0c;允许用户通过互联网快速下载和安装所需的软件。在国内&#xff0c;有多个知名的开源软件镜像站&#xff0c;为开发者提供稳定、快速的开源软件下载服务。 二、常见开源软件镜像站 序号…

Oracle 数据库中优化 INSERT INTO 操作的性能

在 Oracle 数据库中优化 INSERT INTO 操作的性能&#xff0c;尤其是在处理大批量数据时&#xff0c;可以通过以下方法显著提升效率。 使用直接路径插入&#xff08;Direct-Path Insert&#xff09; 通过 APPEND 提示绕过缓冲区缓存&#xff0c;直接写入数据文件&#xff0c;减…

嵌入式硬件篇---嘉立创PCB绘制

文章目录 前言一、PCB绘制简介1.1绘制步骤1.1.1前期准备1.1.2原理图设计1.1.3原理图转PCB1.1.4PCB布局1.1.5布线1.1.6布线优化和丝印1.1.7制版 1.2原理1.2.1电气连接原理1.2.2信号传输原理1.2.3电源和接地原理 1.3注意事项1.3.1元件封装1.3.2布局规则1.3.3过孔设计1.3.4DRC检查…

ideal自动生成类图的方法

在 IntelliJ IDEA 中&#xff0c;“**在项目资源管理器中选择以下类**” 是指通过 **项目资源管理器&#xff08;Project Tool Window&#xff09;** 找到并选中你需要生成类图的类文件&#xff08;如 .java 文件&#xff09;&#xff0c;然后通过右键菜单或快捷键操作生成类图…

【零基础入门unity游戏开发——2D篇】2D物理关节 —— Joint2D相关组件

考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…

在Vue 3 + TypeScript + Vite 项目中安装和使用 SCSS

在Vue 3 TypeScript Vite 项目中安装和使用 SCSS 1、安装 SCSS 的相关依赖 npm install sass --save-dev2、配置 Vite 对于 Vue 3&#xff0c;Vite 已经内置了对 SCSS 的支持&#xff0c;通常不需要额外的配置。但是&#xff0c;如果需要自定义配置&#xff0c;可以在路径…

【渗透测试】Vulnhub靶机-FSoft Challenges VM: 1-详细通关教程

下载地址&#xff1a;https://www.vulnhub.com/entry/fsoft-challenges-vm-1,402/ 目录 前言 信息收集 目录扫描 wpscan扫描 修改密码 反弹shell 提权 思路总结 前言 开始前注意靶机简介&#xff0c;当第一次开机时会报apache错误&#xff0c;所以要等一分钟后重启才…

Redis 6.2.6 生产环境单机配置详解redis.conf

文章目录 Redis 生产环境配置详解配置文件示例基础网络与进程管理RDB 持久化配置复制&#xff08;主从同步&#xff09;设置内存管理AOF 持久化性能优化设置限制与监控其他参数完整配置总结 Redis 生产环境配置详解 在实际生产环境中&#xff0c;为了保障 Redis 的稳定性和高性…

SpringBoot实战:Excel文件上传、数据验证与存储全流程解析

一、需求场景与技术选型 在企业管理、数据中台等系统中&#xff0c;Excel文件处理是常见需求。本文将基于SpringBoot实现以下核心功能&#xff1a; 支持.xls/.xlsx文件上传数据完整性验证&#xff08;非空、格式等&#xff09;业务数据验证&#xff08;关联数据库校验&#x…

使用Java爬虫按关键字搜索淘宝商品?

在电商领域&#xff0c;通过关键字搜索商品是获取商品信息的常见需求。Java爬虫技术可以帮助我们自动化地获取这些信息&#xff0c;提高工作效率。本文将详细介绍如何使用Java爬虫按关键字搜索淘宝商品&#xff0c;并提供完整的代码示例。 一、准备工作 1. 注册淘宝开放平台账…

【Git】5 个分区的切换方式及示例

目录 1. **工作区&#xff08;Working Directory&#xff09;**2. **缓存区&#xff08;Stage/Index&#xff09;**3. **本地仓库&#xff08;Local Repository&#xff09;**4. **远程仓库&#xff08;Remote Repository&#xff09;**5. **贮藏区&#xff08;Stash&#xff0…

【计算机视觉】YOLO语义分割

一、语义分割简介 1. 定义 语义分割&#xff08;Semantic Segmentation&#xff09;是计算机视觉中的一项任务&#xff0c;其目标是对图像中的每一个像素赋予一个类别标签。与目标检测只给出目标的边界框不同&#xff0c;语义分割能够在像素级别上区分不同类别&#xff0c;从…

MATLAB之数据分析图系列:从二维到三维(直接套用)

MATLAB以其强大的矩阵运算和可视化功能&#xff0c;成为科研、工程领域的标配工具。本文提供从基础二维图形到复杂三维模型的即用代码块&#xff0c;涵盖数据标注、多图排版、动态演示等核心技巧 所有代码均经过MATLAB 2023a实测&#xff0c;替换数据即可生成专业级图表。” …

HTTP响应数据包全面解析:结构、原理与最佳实践

目录 HTTP响应概述 HTTP响应数据包结构 2.1 状态行 2.2 响应头 2.3 空行 2.4 响应体 HTTP状态码详解 3.1 1xx信息响应 3.2 2xx成功响应 3.3 3xx重定向 3.4 4xx客户端错误 3.5 5xx服务器错误 常见HTTP响应头字段 响应体内容类型 缓存控制机制 实际HTTP响应示例分…

H.264编码解析与C++实现详解

一、H.264编码核心概念 1.1 分层编码结构 H.264采用分层设计&#xff0c;包含视频编码层&#xff08;VCL&#xff09;和网络抽象层&#xff08;NAL&#xff09;。VCL处理核心编码任务&#xff0c;NAL负责封装网络传输数据。 1.2 NALU单元结构 // NAL单元头部结构示例 struc…

快速入手-基于Django-rest-framework的自身组件权限认证(九)

1、在对应的视图函数里增加认证&#xff08;局部起作用&#xff0c;不全局生效&#xff09; 导入类&#xff1a; from rest_framework.authentication import ( BasicAuthentication, SessionAuthentication, ) from rest_framework.permissions import IsAuthentica…

受控组件和非受控组件的区别

在 React 中&#xff0c;​受控组件&#xff08;Controlled Components&#xff09;​ 和 ​非受控组件&#xff08;Uncontrolled Components&#xff09;​ 是处理表单元素的两种不同方式&#xff0c;它们的核心区别在于 ​数据管理的方式 和 ​与 React 的交互模式。 受控组件…

迈向云原生:理想汽车 OLAP 引擎变革之路

在如今数据驱动的时代&#xff0c;高效的分析引擎对企业至关重要。理想汽车作为智能电动汽车的领军企业&#xff0c;面临着海量数据分析的挑战。本文将展开介绍理想汽车 OLAP 引擎从存算一体向云原生架构演进的变革历程&#xff0c;以及在此过程中面临的挑战&#xff0c;以及是…

ZLMediaKit 源码分析——[3] ZLToolKit 中EventPoller之网络事件处理

系列文章目录 第一篇 基于SRS 的 WebRTC 环境搭建 第二篇 基于SRS 实现RTSP接入与WebRTC播放 第三篇 centos下基于ZLMediaKit 的WebRTC 环境搭建 第四篇 WebRTC学习一&#xff1a;获取音频和视频设备 第五篇 WebRTC学习二&#xff1a;WebRTC音视频数据采集 第六篇 WebRTC学习三…

【分布式】分布式限流方案解析

文章目录 固定窗口限流方案​实现方式​优点​缺点​ 滑动窗口限流方案​实现方式​优点​缺点​ 令牌桶限流方案​实现方式​优点​缺点​ 漏斗限流方案​实现方式​优点​缺点​ 在分布式系统蓬勃发展的当下&#xff0c;系统面临的流量挑战日益复杂。为确保系统在高并发场景下…