本文档总结《第五章:网络编程》中所有提及的通信方式,按 "核心特点→实现步骤→C++ 简要模板" 结构整理,突出核心流程,适配 C++11 + 语法(含 auto、范围 for 等现代特性)。
一、TCP 面向连接通信(可靠传输)
核心特点
- 面向连接、可靠传输(无丢失 / 重复 / 失序)、字节流、三次握手 / 四次挥手
- 适用场景:登录、文件传输、关键数据交互(如订单、支付)
实现步骤(服务器端)
- 创建 TCP 套接字(
SOCK_STREAM) - 设置地址重用(避免端口占用)
- 绑定 IP 和端口(
bind) - 启动监听(
listen) - 循环阻塞等待客户端连接(
accept) - 与客户端收发数据(
recv/send) - 关闭套接字(
close)
实现步骤(客户端)
- 创建 TCP 套接字(
SOCK_STREAM) - 连接服务器(
connect) - 与服务器收发数据(
send/recv) - 关闭套接字(
close)
C++ 模板代码
服务器端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建TCP套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 设置地址重用
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 3. 绑定IP和端口
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }
// 4. 启动监听
if (listen(sfd, 128) == -1) { perror("listen"); return -1; }
std::cout << "TCP服务器监听中..." << std::endl;
// 5. 等待客户端连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
if (newfd == -1) { perror("accept"); return -1; }
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << ":" << ntohs(cli_addr.sin_port) << "]连接成功" << std::endl;
// 6. 收发数据
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; } // 客户端下线或出错
std::cout << "收到客户端消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0); // 回显
}
// 7. 关闭套接字
close(newfd);
close(sfd);
return 0;
}
客户端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建TCP套接字
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1) { perror("socket"); return -1; }
// 2. 连接服务器
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (connect(cfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("connect"); return -1; }
// 3. 收发数据
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "请输入消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0'; // 去除换行符
send(cfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; }
std::cout << "服务器回复: " << buf << std::endl;
}
// 4. 关闭套接字
close(cfd);
return 0;
}
二、UDP 面向无连接通信(快速传输)
核心特点
- 无连接、不可靠(可能丢失 / 乱序)、数据报、低延迟
- 适用场景:通知、广播、组播、实时音视频
实现步骤(服务器端 + 客户端)
- 创建 UDP 套接字(
SOCK_DGRAM) - 绑定 IP 和端口(服务器必选,客户端可选)
- 收发数据(
recvfrom/sendto,需指定对端地址) - 关闭套接字
C++ 模板代码
服务器端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建UDP套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 绑定IP和端口
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }
// 3. 收发数据
char buf[1024] = {0};
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
if (n <= 0) { continue; }
std::cout << "收到[" << inet_ntoa(cli_addr.sin_addr) << ":" << ntohs(cli_addr.sin_port) << "]消息: " << buf << std::endl;
strcat(buf, "*_*");
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len); // 回显
}
// 4. 关闭套接字
close(sfd);
return 0;
}
客户端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建UDP套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1) { perror("socket"); return -1; }
// 2. 初始化服务器地址
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
// 3. 收发数据
char buf[1024] = {0};
socklen_t ser_len = sizeof(ser_addr);
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "请输入消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0';
sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&ser_addr, ser_len);
memset(buf, 0, sizeof(buf));
recvfrom(cfd, buf, sizeof(buf)-1, 0, NULL, NULL);
std::cout << "服务器回复: " << buf << std::endl;
}
// 4. 关闭套接字
close(cfd);
return 0;
}
三、多进程 TCP 并发服务器
核心特点
- 主进程监听连接,子进程独立处理单个客户端通信
- 解决单进程只能处理一个客户端的问题,需回收僵尸进程
实现步骤
- 创建 TCP 套接字、绑定、监听(同基础 TCP 服务器)
- 注册信号处理函数(回收僵尸进程)
- 循环
accept接收客户端连接 fork创建子进程,子进程处理收发数据- 父进程关闭
newfd,循环等待新连接
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
// 回收僵尸进程
void sig_handler(int sig) {
while (waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收所有子进程
}
int main() {
// 注册信号处理函数
signal(SIGCHLD, sig_handler);
// 1. 创建TCP套接字、绑定、监听(同基础TCP服务器)
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
std::cout << "多进程TCP服务器启动,监听端口" << SER_PORT << std::endl;
while (true) {
// 2. 接收客户端连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
if (newfd == -1) { continue; }
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
// 3. 创建子进程
pid_t pid = fork();
if (pid == 0) { // 子进程
close(sfd); // 子进程不需要监听套接字
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; }
std::cout << "子进程[" << getpid() << "]收到消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0);
}
close(newfd);
exit(EXIT_SUCCESS); // 子进程退出
} else if (pid > 0) { // 父进程
close(newfd); // 父进程不需要通信套接字
}
}
close(sfd);
return 0;
}
四、多线程 TCP 并发服务器
核心特点
- 主线程监听连接,分支线程处理客户端通信
- 资源开销比多进程小,需注意线程安全(本模板简化,无共享资源)
实现步骤
- 创建 TCP 套接字、绑定、监听
- 循环
accept接收连接 - 创建分支线程,传入
newfd和客户端地址 - 线程分离(自动回收资源),线程内处理收发数据
- 线程退出(
pthread_exit)
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
// 线程参数结构体
struct ThreadArgs {
int newfd;
struct sockaddr_in cli_addr;
};
// 线程处理函数
void* cli_handler(void* arg) {
ThreadArgs* args = static_cast<ThreadArgs*>(arg);
int newfd = args->newfd;
auto& cli_addr = args->cli_addr;
delete args; // 释放参数内存
pthread_detach(pthread_self()); // 线程分离,自动回收
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; }
std::cout << "线程[" << pthread_self() << "]收到[" << inet_ntoa(cli_addr.sin_addr) << "]消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0);
}
close(newfd);
pthread_exit(NULL);
}
int main() {
// 1. 创建TCP套接字、绑定、监听
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
std::cout << "多线程TCP服务器启动,监听端口" << SER_PORT << std::endl;
while (true) {
// 2. 接收客户端连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
if (newfd == -1) { continue; }
// 3. 创建线程参数
ThreadArgs* args = new ThreadArgs{newfd, cli_addr};
pthread_t tid;
if (pthread_create(&tid, NULL, cli_handler, args) != 0) {
perror("pthread_create");
close(newfd);
delete args;
}
}
close(sfd);
return 0;
}
五、select IO 多路复用(TCP 并发)
核心特点
- 单进程管理多个文件描述符,监听可读 / 可写 / 异常事件
- 缺点:文件描述符上限(1024)、每次需重置 fd_set
实现步骤
- 创建 TCP 套接字、绑定、监听
- 初始化
fd_set(读集合),添加监听 fd 和标准输入 - 循环
select阻塞等待就绪事件 - 判断哪个 fd 就绪,分别处理(监听 fd→accept;客户端 fd→收发;标准输入→终端命令)
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_FD = 1024;
int main() {
// 1. 创建TCP套接字、绑定、监听
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
// 2. 初始化fd_set
fd_set readfds, tempfds;
FD_ZERO(&readfds);
FD_SET(sfd, &readfds); // 添加监听fd
FD_SET(0, &readfds); // 添加标准输入fd
int max_fd = sfd;
std::cout << "select服务器启动,监听端口" << SER_PORT << std::endl;
while (true) {
tempfds = readfds; // 备份(select会修改集合)
// 3. 阻塞等待就绪事件
int nfds = select(max_fd + 1, &tempfds, NULL, NULL, NULL);
if (nfds == -1) { perror("select"); break; }
// 4. 遍历所有fd,判断是否就绪
for (int fd = 0; fd <= max_fd; ++fd) {
if (FD_ISSET(fd, &tempfds)) {
if (fd == sfd) { // 监听fd就绪→新连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
FD_SET(newfd, &readfds);
max_fd = std::max(max_fd, newfd);
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
} else if (fd == 0) { // 标准输入就绪→终端命令
char buf[1024] = {0};
fgets(buf, sizeof(buf), stdin);
if (strcmp(buf, "quit\n") == 0) { goto END; } // 退出命令
} else { // 客户端fd就绪→收发数据
char buf[1024] = {0};
ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
if (n <= 0) { // 客户端下线
FD_CLR(fd, &readfds);
close(fd);
std::cout << "客户端fd=" << fd << "下线" << std::endl;
continue;
}
std::cout << "收到fd=" << fd << "消息: " << buf << std::endl;
strcat(buf, "*_*");
send(fd, buf, strlen(buf), 0);
}
}
}
}
END:
for (int fd = 0; fd <= max_fd; ++fd) {
if (FD_ISSET(fd, &readfds)) { close(fd); }
}
return 0;
}
六、poll IO 多路复用(TCP 并发)
核心特点
- 用
pollfd结构体管理 fd,无 fd 上限,无需备份集合 - 缺点:仍需遍历所有 fd 判断就绪状态
实现步骤
- 创建 TCP 套接字、绑定、监听
- 初始化
pollfd数组(包含监听 fd 和标准输入) - 循环
poll阻塞等待就绪事件 - 遍历
pollfd数组,判断revents,处理对应事件
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_POLLFD = 1024;
int main() {
// 1. 创建TCP套接字、绑定、监听
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
// 2. 初始化pollfd数组
struct pollfd fds[MAX_POLLFD];
fds[0].fd = sfd;
fds[0].events = POLLIN; // 监听读事件
fds[1].fd = 0;
fds[1].events = POLLIN;
int pollfd_count = 2;
std::cout << "poll服务器启动,监听端口" << SER_PORT << std::endl;
while (true) {
// 3. 阻塞等待就绪事件
int nfds = poll(fds, pollfd_count, -1);
if (nfds == -1) { perror("poll"); break; }
// 4. 遍历处理就绪事件
for (int i = 0; i < pollfd_count; ++i) {
if (fds[i].revents & POLLIN) {
if (fds[i].fd == sfd) { // 新连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
fds[pollfd_count].fd = newfd;
fds[pollfd_count].events = POLLIN;
pollfd_count++;
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
} else if (fds[i].fd == 0) { // 终端命令
char buf[1024] = {0};
fgets(buf, sizeof(buf), stdin);
if (strcmp(buf, "quit\n") == 0) { goto END; }
} else { // 客户端数据
char buf[1024] = {0};
ssize_t n = recv(fds[i].fd, buf, sizeof(buf)-1, 0);
if (n <= 0) { // 下线
close(fds[i].fd);
fds[i] = fds[--pollfd_count]; // 覆盖下线fd
std::cout << "客户端fd=" << fds[i].fd << "下线" << std::endl;
i--; // 重新检查当前位置
continue;
}
std::cout << "收到fd=" << fds[i].fd << "消息: " << buf << std::endl;
strcat(buf, "*_*");
send(fds[i].fd, buf, strlen(buf), 0);
}
}
}
}
END:
for (int i = 0; i < pollfd_count; ++i) {
close(fds[i].fd);
}
return 0;
}
七、epoll IO 多路复用(TCP 并发,LT/ET 模式)
核心特点
- 高效 IO 多路复用,基于红黑树和回调机制,无 fd 上限
- LT 模式(默认):水平触发,多次通知未处理数据;ET 模式:边沿触发,仅通知一次(需非阻塞 IO)
实现步骤(LT 模式)
- 创建 epoll 实例(
epoll_create) - 添加监听 fd 到 epoll(
epoll_ctl(EPOLL_CTL_ADD)) - 循环
epoll_wait阻塞等待就绪事件 - 遍历就绪事件数组,处理新连接或客户端数据
C++ 模板代码(LT 模式)
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_EVENTS = 1024;
int main() {
// 1. 创建TCP套接字、绑定、监听
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
// 2. 创建epoll实例
int epfd = epoll_create(1);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN; // LT模式(默认)
ev.data.fd = sfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);
std::cout << "epoll(LT)服务器启动,监听端口" << SER_PORT << std::endl;
while (true) {
// 3. 阻塞等待就绪事件
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nfds == -1) { perror("epoll_wait"); break; }
// 4. 处理就绪事件
for (int i = 0; i < nfds; ++i) {
int fd = events[i].data.fd;
if (fd == sfd) { // 新连接
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
ev.events = EPOLLIN;
ev.data.fd = newfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev);
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
} else { // 客户端数据
char buf[1024] = {0};
ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
if (n <= 0) { // 下线
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
std::cout << "客户端fd=" << fd << "下线" << std::endl;
continue;
}
std::cout << "收到fd=" << fd << "消息: " << buf << std::endl;
strcat(buf, "*_*");
send(fd, buf, strlen(buf), 0);
}
}
}
// 5. 清理资源
close(sfd);
close(epfd);
return 0;
}
ET 模式修改点(核心差异)
- 添加 fd 到 epoll 时,事件设为
EPOLLIN | EPOLLET - 客户端 fd 设为非阻塞(避免单次读取不完整)
cpp
运行
// 客户端fd设为非阻塞
int flags = fcntl(newfd, F_GETFL, 0);
fcntl(newfd, F_SETFL, flags | O_NONBLOCK);
// 添加到epoll时指定ET模式
ev.events = EPOLLIN | EPOLLET;
八、广播(UDP 基础)
核心特点
- 一对多通信,同一网络下所有主机接收,基于 UDP
- 发送端需设置
SO_BROADCAST属性,接收端必须绑定端口
实现步骤(发送端 + 接收端)
发送端
- 创建 UDP 套接字
- 设置
SO_BROADCAST允许广播 - 向广播地址(如 192.168.1.255)发送数据
接收端
- 创建 UDP 套接字
- 绑定广播地址和端口
- 循环接收广播数据
C++ 模板代码
发送端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255"; // 广播地址
int main() {
// 1. 创建UDP套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 设置允许广播
int broad = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1) {
perror("setsockopt"); return -1;
}
// 3. 初始化广播地址
struct sockaddr_in broad_addr;
memset(&broad_addr, 0, sizeof(broad_addr));
broad_addr.sin_family = AF_INET;
broad_addr.sin_port = htons(BROADCAST_PORT);
broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());
// 4. 发送广播
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "请输入广播消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0';
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&broad_addr, sizeof(broad_addr));
}
close(sfd);
return 0;
}
接收端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255";
int main() {
// 1. 创建UDP套接字
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if (rfd == -1) { perror("socket"); return -1; }
// 2. 绑定广播地址和端口
struct sockaddr_in broad_addr;
memset(&broad_addr, 0, sizeof(broad_addr));
broad_addr.sin_family = AF_INET;
broad_addr.sin_port = htons(BROADCAST_PORT);
broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());
if (bind(rfd, (struct sockaddr*)&broad_addr, sizeof(broad_addr)) == -1) {
perror("bind"); return -1;
}
// 3. 接收广播
char buf[1024] = {0};
struct sockaddr_in sender_addr;
socklen_t sender_len = sizeof(sender_addr);
std::cout << "广播接收端启动,监听端口" << BROADCAST_PORT << std::endl;
while (true) {
memset(buf, 0, sizeof(buf));
recvfrom(rfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&sender_addr, &sender_len);
std::cout << "收到[" << inet_ntoa(sender_addr.sin_addr) << "]广播: " << buf << std::endl;
}
close(rfd);
return 0;
}
九、组播(UDP 基础)
核心特点
- 一对多通信,仅加入组播组的主机接收,基于 UDP
- 组播地址为 D 类地址(224.0.0.0-239.255.255.255)
实现步骤(发送端 + 接收端)
发送端
- 创建 UDP 套接字
- 向组播地址发送数据(无需加入组播组)
接收端
- 创建 UDP 套接字
- 设置
IP_ADD_MEMBERSHIP加入组播组 - 绑定组播地址和端口
- 循环接收组播数据
C++ 模板代码
发送端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int MULTICAST_PORT = 8888;
const std::string MULTICAST_IP = "224.1.2.3"; // D类组播地址
int main() {
// 1. 创建UDP套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 初始化组播地址
struct sockaddr_in multi_addr;
memset(&multi_addr, 0, sizeof(multi_addr));
multi_addr.sin_family = AF_INET;
multi_addr.sin_port = htons(MULTICAST_PORT);
multi_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str());
// 3. 发送组播
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "请输入组播消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0';
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&multi_addr, sizeof(multi_addr));
}
close(sfd);
return 0;
}
接收端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int MULTICAST_PORT = 8888;
const std::string MULTICAST_IP = "224.1.2.3";
const std::string LOCAL_IP = "192.168.1.100"; // 本地网卡IP
int main() {
// 1. 创建UDP套接字
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if (rfd == -1) { perror("socket"); return -1; }
// 2. 加入组播组
struct ip_mreqn imr;
imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP.c_str());
imr.imr_address.s_addr = inet_addr(LOCAL_IP.c_str());
imr.imr_ifindex = 2; // 网卡编号(ip ad查看)
if (setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1) {
perror("setsockopt"); return -1;
}
// 3. 绑定组播地址和端口
struct sockaddr_in multi_addr;
memset(&multi_addr, 0, sizeof(multi_addr));
multi_addr.sin_family = AF_INET;
multi_addr.sin_port = htons(MULTICAST_PORT);
multi_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str());
if (bind(rfd, (struct sockaddr*)&multi_addr, sizeof(multi_addr)) == -1) {
perror("bind"); return -1;
}
// 4. 接收组播
char buf[1024] = {0};
std::cout << "组播接收端启动,已加入组播组" << MULTICAST_IP << std::endl;
while (true) {
memset(buf, 0, sizeof(buf));
recvfrom(rfd, buf, sizeof(buf)-1, 0, NULL, NULL);
std::cout << "收到组播消息: " << buf << std::endl;
}
close(rfd);
return 0;
}
十、域套接字(本地进程通信)
1. 流式域套接字(SOCK_STREAM,可靠)
核心特点
- 同一主机进程间通信,基于套接字文件,可靠连接
- 类似本地 TCP,无需 IP 和端口,用文件路径标识
实现步骤(服务器 + 客户端)
服务器端
- 创建域套接字(
AF_UNIX + SOCK_STREAM) - 检查并删除旧套接字文件(避免绑定失败)
- 绑定套接字文件路径
- 监听、accept、收发数据、关闭
客户端
- 创建域套接字
- 连接服务器的套接字文件
- 收发数据、关闭
C++ 模板代码
服务器端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
const std::string SOCK_PATH = "./unix_sock"; // 套接字文件路径
int main() {
// 1. 创建流式域套接字
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 删除旧套接字文件
if (access(SOCK_PATH.c_str(), F_OK) == 0) {
if (unlink(SOCK_PATH.c_str()) == -1) { perror("unlink"); return -1; }
}
// 3. 绑定套接字文件
struct sockaddr_un sun;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, SOCK_PATH.c_str());
if (bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("bind"); return -1; }
// 4. 监听、accept、收发
listen(sfd, 128);
std::cout << "流式域套接字服务器启动,路径: " << SOCK_PATH << std::endl;
struct sockaddr_un cun;
socklen_t cun_len = sizeof(cun);
int newfd = accept(sfd, (struct sockaddr*)&cun, &cun_len);
if (newfd == -1) { perror("accept"); return -1; }
std::cout << "客户端[" << cun.sun_path << "]连接成功" << std::endl;
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; }
std::cout << "收到消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0);
}
close(newfd);
close(sfd);
unlink(SOCK_PATH.c_str()); // 清理套接字文件
return 0;
}
客户端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
const std::string SOCK_PATH = "./unix_sock";
const std::string CLI_SOCK_PATH = "./client_sock";
int main() {
// 1. 创建流式域套接字
int cfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (cfd == -1) { perror("socket"); return -1; }
// 2. 绑定客户端套接字文件(可选)
if (access(CLI_SOCK_PATH.c_str(), F_OK) == 0) {
unlink(CLI_SOCK_PATH.c_str());
}
struct sockaddr_un cun;
memset(&cun, 0, sizeof(cun));
cun.sun_family = AF_UNIX;
strcpy(cun.sun_path, CLI_SOCK_PATH.c_str());
if (bind(cfd, (struct sockaddr*)&cun, sizeof(cun)) == -1) { perror("bind"); return -1; }
// 3. 连接服务器
struct sockaddr_un sun;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, SOCK_PATH.c_str());
if (connect(cfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("connect"); return -1; }
// 4. 收发数据
char buf[1024] = {0};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "请输入消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0';
send(cfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
if (n <= 0) { break; }
std::cout << "服务器回复: " << buf << std::endl;
}
close(cfd);
unlink(CLI_SOCK_PATH.c_str());
return 0;
}
2. 报式域套接字(SOCK_DGRAM,无连接)
核心特点
- 本地无连接通信,基于套接字文件,数据报形式
- 类似本地 UDP,无需连接,直接收发
实现步骤(服务器 + 客户端)
服务器端
- 创建报式域套接字(
AF_UNIX + SOCK_DGRAM) - 删除旧套接字文件、绑定
- 循环
recvfrom接收数据、sendto回复
客户端
- 创建报式域套接字、绑定(可选)
- 向服务器套接字文件发送数据、接收回复
C++ 模板代码(服务器端)
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
const std::string SOCK_PATH = "./unix_dgram_sock";
int main() {
// 1. 创建报式域套接字
int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 删除旧文件、绑定
if (access(SOCK_PATH.c_str(), F_OK) == 0) { unlink(SOCK_PATH.c_str()); }
struct sockaddr_un sun;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, SOCK_PATH.c_str());
if (bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("bind"); return -1; }
// 3. 收发数据
char buf[1024] = {0};
struct sockaddr_un cli_addr;
socklen_t cli_len = sizeof(cli_addr);
std::cout << "报式域套接字服务器启动" << std::endl;
while (true) {
memset(buf, 0, sizeof(buf));
recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
std::cout << "收到[" << cli_addr.sun_path << "]消息: " << buf << std::endl;
strcat(buf, "*_*");
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len);
}
close(sfd);
unlink(SOCK_PATH.c_str());
return 0;
}
总结
所有通信方式的核心差异在于 "连接可靠性""并发能力""适用场景":
- 可靠连接:TCP、流式域套接字
- 快速无连接:UDP、广播、组播、报式域套接字
- 高并发:epoll(首选)、poll、select、多线程 / 多进程
- 本地通信:域套接字(效率高于 TCP/UDP)
模板均采用 C++11 + 语法,突出核心流程,可根据实际需求扩展错误处理、线程安全、数据序列化等功能。</think># 网络编程所有通信方式 C++ 实现模板(C++11+)基于《第五章:网络编程》文档,整理所有提及的通信方式,按 "核心特点→实现步骤→C++ 简要模板" 结构总结,突出核心流程,适配 C++ 现代特性(auto、范围 for 等)。
一、TCP 面向连接通信(可靠传输)
核心特点
- 面向连接、可靠传输(无丢失 / 重复 / 失序)、三次握手 / 四次挥手
- 适用:登录、文件传输、关键数据交互
实现步骤(服务器 + 客户端)
服务器端
- 创建 TCP 套接字(
SOCK_STREAM) - 设置地址重用(避免端口占用)
- 绑定 IP 和端口(
bind) - 启动监听(
listen) - 循环接收客户端连接(
accept) - 收发数据(
recv/send) - 关闭套接字(
close)
客户端
- 创建 TCP 套接字
- 连接服务器(
connect) - 收发数据
- 关闭套接字
C++ 模板代码
服务器端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建TCP套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 地址重用
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 3. 绑定IP+端口
struct sockaddr_in ser_addr{};
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }
// 4. 监听
listen(sfd, 128);
std::cout << "TCP服务器监听端口" << SER_PORT << std::endl;
// 5. 接收客户端连接
struct sockaddr_in cli_addr{};
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
if (newfd == -1) { perror("accept"); return -1; }
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接成功" << std::endl;
// 6. 收发数据
char buf[1024]{};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) break; // 客户端下线
std::cout << "收到消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0); // 回显
}
// 7. 关闭
close(newfd);
close(sfd);
return 0;
}
客户端
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建套接字
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if (cfd == -1) { perror("socket"); return -1; }
// 2. 连接服务器
struct sockaddr_in ser_addr{};
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (connect(cfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("connect"); return -1; }
// 3. 收发数据
char buf[1024]{};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "输入消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0'; // 去除换行
send(cfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
if (n <= 0) break;
std::cout << "服务器回复: " << buf << std::endl;
}
close(cfd);
return 0;
}
二、UDP 面向无连接通信(快速传输)
核心特点
- 无连接、不可靠(可能丢失 / 乱序)、低延迟、数据报
- 适用:通知、广播、组播、实时音视频
实现步骤
- 创建 UDP 套接字(
SOCK_DGRAM) - 绑定 IP + 端口(服务器必选,客户端可选)
- 收发数据(
recvfrom/sendto,需指定对端地址) - 关闭套接字
C++ 模板代码(服务器端)
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";
int main() {
// 1. 创建UDP套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 绑定
struct sockaddr_in ser_addr{};
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }
// 3. 收发数据
char buf[1024]{};
struct sockaddr_in cli_addr{};
socklen_t cli_len = sizeof(cli_addr);
while (true) {
memset(buf, 0, sizeof(buf));
recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
std::cout << "收到[" << inet_ntoa(cli_addr.sin_addr) << "]消息: " << buf << std::endl;
strcat(buf, "*_*");
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len);
}
close(sfd);
return 0;
}
三、多线程 TCP 并发服务器
核心特点
- 主线程监听连接,分支线程处理客户端通信
- 资源开销小,适配高并发场景
实现步骤
- 创建 TCP 套接字、绑定、监听
- 循环
accept接收连接 - 创建分支线程(
pthread_create),传入通信 fd - 线程分离(自动回收资源),处理收发数据
- 线程退出(
pthread_exit)
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
// 线程参数结构体
struct ThreadArgs { int newfd; struct sockaddr_in cli_addr; };
// 线程处理函数
void* cli_handler(void* arg) {
auto args = static_cast<ThreadArgs*>(arg);
int newfd = args->newfd;
auto& cli_addr = args->cli_addr;
delete args;
pthread_detach(pthread_self()); // 线程分离
char buf[1024]{};
while (true) {
memset(buf, 0, sizeof(buf));
ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
if (n <= 0) break;
std::cout << "线程[" << pthread_self() << "]收到消息: " << buf << std::endl;
strcat(buf, "*_*");
send(newfd, buf, strlen(buf), 0);
}
close(newfd);
pthread_exit(NULL);
}
int main() {
// 1. 创建套接字、绑定、监听(同基础TCP服务器)
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr{};
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
std::cout << "多线程TCP服务器启动" << std::endl;
while (true) {
// 2. 接收连接
struct sockaddr_in cli_addr{};
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
if (newfd == -1) continue;
// 3. 创建线程
auto args = new ThreadArgs{newfd, cli_addr};
pthread_t tid;
if (pthread_create(&tid, NULL, cli_handler, args) != 0) {
perror("pthread_create");
close(newfd);
delete args;
}
}
close(sfd);
return 0;
}
四、epoll IO 多路复用(高并发首选)
核心特点
- 基于红黑树 + 回调,无 fd 上限,效率高
- 支持 LT(水平触发,默认)和 ET(边沿触发)模式
- 适用:高并发场景(10k + 连接)
实现步骤(LT 模式)
- 创建 epoll 实例(
epoll_create) - 添加监听 fd 到 epoll(
epoll_ctl(EPOLL_CTL_ADD)) - 循环
epoll_wait阻塞等待就绪事件 - 遍历就绪事件,处理新连接或客户端数据
C++ 模板代码
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>
constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_EVENTS = 1024;
int main() {
// 1. 创建TCP套接字、绑定、监听
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in ser_addr{};
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(SER_PORT);
ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(sfd, 128);
// 2. 创建epoll实例
int epfd = epoll_create(1);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN; // LT模式
ev.data.fd = sfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);
std::cout << "epoll服务器启动" << std::endl;
while (true) {
// 3. 等待就绪事件
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nfds == -1) { perror("epoll_wait"); break; }
// 4. 处理事件
for (int i = 0; i < nfds; ++i) {
int fd = events[i].data.fd;
if (fd == sfd) { // 新连接
struct sockaddr_in cli_addr{};
socklen_t cli_len = sizeof(cli_addr);
int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
ev.events = EPOLLIN;
ev.data.fd = newfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev);
std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接" << std::endl;
} else { // 客户端数据
char buf[1024]{};
ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
if (n <= 0) { // 下线
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
continue;
}
std::cout << "收到消息: " << buf << std::endl;
strcat(buf, "*_*");
send(fd, buf, strlen(buf), 0);
}
}
}
close(sfd);
close(epfd);
return 0;
}
五、广播(UDP 基础)
核心特点
- 一对多通信,同一网络下所有主机接收
- 发送端需设置
SO_BROADCAST属性
实现步骤(发送端)
- 创建 UDP 套接字
- 设置
SO_BROADCAST允许广播 - 向广播地址(如 192.168.1.255)发送数据
C++ 模板代码(发送端)
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255";
int main() {
// 1. 创建UDP套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1) { perror("socket"); return -1; }
// 2. 允许广播
int broad = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1) {
perror("setsockopt"); return -1;
}
// 3. 初始化广播地址
struct sockaddr_in broad_addr{};
broad_addr.sin_family = AF_INET;
broad_addr.sin_port = htons(BROADCAST_PORT);
broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());
// 4. 发送广播
char buf[1024]{};
while (true) {
memset(buf, 0, sizeof(buf));
std::cout << "输入广播消息: ";
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = '\0';
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&broad_addr, sizeof(broad_addr));
}
close(sfd);
return 0;
}
六、组播(UDP 基础)
核心特点
- 一对多通信,仅加入组播组的主机接收
- 组播地址为 D 类地址(224.0.0.0-239.255.255.255)
实现步骤(接收端)
- 创建 UDP 套接字
- 设置
IP_ADD_MEMBERSHIP加入组播组 - 绑定组播地址和端口
- 接收组播数据
C++ 模板代码(接收端)
cpp
运行
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
// 常量定义(可根据实际需求修改)
constexpr int MULTICAST_PORT = 8888; // 组播端口(需与发送端一致)
const std::string MULTICAST_IP = "224.1.2.3"; // D类组播地址(224.0.0.0-239.255.255.255)
const std::string LOCAL_IP = "192.168.174.128";// 本地网卡IP(通过 ip ad 命令查看)
constexpr int NET_CARD_INDEX = 2; // 网卡设备编号(通过 ip ad 命令查看)
int main() {
// 1. 创建UDP套接字
int recv_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (recv_fd == -1) {
perror("socket 创建失败");
return -1;
}
std::cout << "UDP套接字创建成功,文件描述符: " << recv_fd << std::endl;
// 2. 设置套接字加入组播组
struct ip_mreqn multicast_req;
// 填充组播地址(网络字节序)
multicast_req.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP.c_str());
// 填充本地网卡IP(网络字节序)
multicast_req.imr_address.s_addr = inet_addr(LOCAL_IP.c_str());
// 填充网卡编号
multicast_req.imr_ifindex = NET_CARD_INDEX;
// 调用setsockopt加入组播组(层级:IPPROTO_IP,属性:IP_ADD_MEMBERSHIP)
if (setsockopt(recv_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&multicast_req, sizeof(multicast_req)) == -1) {
perror("加入组播组失败");
close(recv_fd);
return -1;
}
std::cout << "成功加入组播组 " << MULTICAST_IP << ":" << MULTICAST_PORT << std::endl;
// 3. 绑定组播地址和端口(必须绑定,否则无法接收组播数据)
struct sockaddr_in multicast_addr;
memset(&multicast_addr, 0, sizeof(multicast_addr));
multicast_addr.sin_family = AF_INET; // IPv4协议族
multicast_addr.sin_port = htons(MULTICAST_PORT); // 端口号(网络字节序)
multicast_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str()); // 组播IP(网络字节序)
if (bind(recv_fd, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) == -1) {
perror("绑定组播地址失败");
close(recv_fd);
return -1;
}
std::cout << "已绑定组播地址,等待接收数据..." << std::endl;
// 4. 循环接收组播数据
char recv_buf[1024] = {0};
while (true) {
memset(recv_buf, 0, sizeof(recv_buf)); // 清空接收缓冲区
// 接收组播数据(无需关心发送端地址,填NULL即可)
ssize_t recv_len = recvfrom(recv_fd, recv_buf, sizeof(recv_buf) - 1,
0, NULL, NULL);
if (recv_len == -1) {
perror("接收组播数据失败");
continue; // 忽略错误,继续接收
}
// 输出接收结果
std::cout << "收到组播消息: " << recv_buf << "(字节数: " << recv_len << ")" << std::endl;
}
// 5. 关闭套接字(实际运行中需手动终止,此处为逻辑闭环)
close(recv_fd);
return 0;
}