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结构的指针,包含地址信息。 -  
addrlen:addr的长度。 
 -  
 -  
返回值:成功返回 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结构的指针,用于存储客户端地址。 -  
addrlen:addr的长度。 
 -  
 -  
返回值:成功返回新套接字描述符,失败返回 -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结构的指针,包含服务器地址信息。 -  
addrlen:addr的长度。 
 -  
 -  
返回值:成功返回 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);
 -  
参数:
-  
how:SHUT_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:
-  
套接字默认为阻塞模式。
 -  
读写操作(如
read、write、accept、connect)会阻塞进程,直到操作完成。 
 -  
 -  
非阻塞 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_fds、write_fds、except_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 三次握手
-  
客户端发送 SYN 包到服务器。
 -  
服务器回复 SYN-ACK 包。
 -  
客户端发送 ACK 包,建立连接。
 
10.2 四次挥手
-  
客户端发送 FIN 包,请求关闭连接。
 -  
服务器回复 ACK 包,确认收到 FIN。
 -  
服务器发送 FIN 包,请求关闭连接。
 -  
客户端回复 ACK 包,确认收到 FIN。
 
11、网络信息检索和设置
11.1 网络信息检索函数
-  
gethostname():获取主机名。 -  
getpeername():获取远程协议地址。 -  
getsockname():获取本地套接口地址。 -  
gethostbyname():根据主机名获取主机信息。 -  
getservbyname():根据服务名获取服务信息。 
11.2 网络属性设置
-  
使用
setsockopt()和getsockopt()设置和获取套接字选项。 -  
常见选项:
-  
SO_REUSEADDR:允许重用本地地址和端口。 -  
SO_KEEPALIVE:保持连接。 -  
SO_RCVTIMEO和SO_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系列文章完毕,会不断的优化完整。文章仅用于分享学习以及自己复习使用,若有什么问题,麻烦指出,谢谢】
