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系列文章完毕,会不断的优化完整。文章仅用于分享学习以及自己复习使用,若有什么问题,麻烦指出,谢谢】