网络编程超时检测,unix域套接字,粘包
刷题:
# 超时检测核心要点 | |
## 1. 基本类型 | |
### 阻塞模式 | |
- 永久等待数据,无超时机制 | |
- 典型函数:`recv()`阻塞调用 | |
### 非阻塞模式 | |
- 立即返回结果(成功/错误) | |
- 设置方式:`fcntl(fd, F_SETFL, O_NONBLOCK)` | |
### 超时检测 | |
- 设置等待阈值,超时返回错误 | |
- 应用场景:网络请求、心跳包 | |
--- | |
## 2. 超时检测函数 | |
### select函数 | |
- 参数:`struct timeval`设置秒和微秒 | |
- 示例: | |
```c | |
struct timeval tm = {3, 0}; | |
if (select(..., &tm) == 0) { /* 超时处理 */ } |
poll函数
- 参数:超时时间(毫秒)
- 示例:
if (poll(fds, 10, 3000) == 0) { /* 超时处理 */ }
epoll_wait
- 参数:超时时间(毫秒)
- 示例:
if (epoll_wait(epfd, events, 10, 3000) == 0) { /* 超时处理 */ }
setsockopt
- 设置收发超时:
struct timeval rcv_timeo = {3, 0};
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &rcv_timeo, sizeof(rcv_timeo));
3. 心跳包机制
核心作用
- 维持长连接活跃状态
- 检测客户端存活(TCP/UDP均适用)
实现步骤
- 客户端:周期发送空包(如每5秒)
- 服务端:
- 维护客户端列表(IP+Port+最后通信时间)
- 定时任务检测超时(如60秒无响应判定离线)
数据结构
- 消息类型:
enum type_t { CHAR, HEART }; // 区分业务包和心跳包
- 时间记录:
typedef struct timebuf {
unsigned int ip;
unsigned short port;
time_t tm; // 最后通信时间戳
} timebuf_t;
4. 信号处理
alarm函数
- 周期性触发信号(如每5秒)
- 示例:
signal(SIGALRM, handler);
alarm(5);
sigaction配置
- 关闭自重启属性:
struct sigaction act;
sigaction(SIGALRM, NULL, &act); // 获取原属性
act.sa_flags &= ~SA_RESTART; // 关闭自重启
sigaction(SIGALRM, &act, NULL); // 设置新属性
- 超时中断处理:
void handler(int sig) {
printf("超时中断!");
// 中断阻塞操作(如recv返回-1,errno=EINTR)
}
5. 代码实现示例
TCP服务端超时检测
- 关键步骤:
- 创建socket并绑定监听
- 使用
alarm
设置超时信号 - 在
recv
中捕获超时错误(errno == EINTR
)
UDP心跳包服务端
- 核心逻辑:
while (1) {
ret = recvfrom(sockfd, &buf, ..., &cliaddr);
if (ret > 0) {
update_time(cliaddr); // 更新时间戳
if (buf.type == HEART) continue; // 心跳包不处理业务
}
}
# UNIX域套接字核心要点 | |
## 1. 基本概念 | |
### 本地通信机制 | |
- 通过文件路径而非网络地址通信 | |
- 适用场景:高效本地进程间通信 | |
- 文件类型:`s`类型套接字文件 | |
### 与网络套接字对比 | |
- **网络通信**:自动填充客户端IP+Port | |
- **域套接字**:需手动绑定路径实现双向通信 | |
--- | |
## 2. TCP域套接字 | |
### 服务端流程 | |
1. **创建套接字**: | |
```c | |
socket(AF_UNIX, SOCK_STREAM, 0) |
- 绑定路径:
struct sockaddr_un addr = {.sun_family=AF_UNIX};
strcpy(addr.sun_path, "./unix_socket");
bind(sfd, (struct sockaddr*)&addr, sizeof(addr));
- 监听连接:
listen(sfd, 5);
- 接受连接:
accept(sfd, NULL, NULL); // 忽略客户端路径
客户端流程
- 创建套接字:同服务端
- 连接服务端:
connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
关键函数
access()
:检查文件存在性unlink()
:删除旧套接字文件避免绑定失败
3. UDP域套接字
服务端实现
- 绑定路径:必须显式绑定
bind(sfd, "./server_socket");
- 接收消息:
recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len);
- 发送响应:需客户端路径
sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cliaddr, len);
客户端实现
- 必须绑定路径:否则服务端无法回复
bind(cfd, "./client_socket");
- 发送消息:
sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&server_addr, len);
4. 并发与优化
epoll模型应用
- 非阻塞设置:
fcntl(fd, F_SETFL, O_NONBLOCK);
- 事件监听:
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
- 边缘触发:
event.events = EPOLLIN | EPOLLET;
文件管理
- 路径冲突处理:
if (access("./socket_file", F_OK) == 0) unlink("./socket_file");
5. 数据结构
地址结构体
struct sockaddr_un { | |
sa_family_t sun_family; // AF_UNIX | |
char sun_path[108]; // 套接字文件路径 | |
}; |
消息格式
typedef struct { | |
enum { CHAR, HEART } type; // 数据类型标识 | |
char text[128]; // 实际数据 | |
} msgbuf_t; |
# TCP粘包问题核心要点 | |
## 基本概念 | |
### 粘包现象定义 | |
- 数据包在传输过程中发生粘连 | |
- 表现为接收端无法区分原始数据边界 | |
### 发生场景 | |
- TCP流式传输固有特性 | |
- 发送/接收缓冲区大小不一致 | |
- 网络传输延迟导致数据堆积 | |
## 核心成因 | |
### 协议特性 | |
- TCP面向字节流的传输方式 | |
- UDP数据报传输天然无粘包 | |
### 应用层因素 | |
- 发送端数据写入过快 | |
- 接收端读取不及时 | |
- 数据包大小与缓冲区不匹配 | |
## 解决方案设计 | |
### 数据封包协议 | |
- 固定包头结构: | |
```c | |
typedef struct { | |
uint32_t length; // 数据长度(网络字节序) | |
char data[]; // 实际数据 | |
} tcp_packet_t; |
- 包头校验机制(CRC32/MD5)
发送端实现
- 数据分片预处理
- 添加4字节长度头
- 使用htonl转换字节序
- 分次发送保证完整性
接收端实现
- 先读取4字节包头
- ntohl转换获得数据长度
- 循环读取直到数据完整
- 缓冲区动态扩容机制
代码实现关键点
服务器端核心逻辑
- 双重接收循环结构
- 非阻塞读取超时处理
- 数据完整性校验
- 异常断开检测机制
客户端核心逻辑
- 文件分块随机读取
- 内存预分配策略
- 数据分片发送保障
- 错误重传机制
性能优化方向
传输层优化
- 设置TCP_NODELAY选项
- 调整SO_SNDBUF/SO_RCVBUF
应用层优化
- 滑动窗口协议实现
- 双缓冲区分包处理
- 异步IO事件驱动模型
错误处理机制
常见异常场景
- 数据包长度校验失败
- 接收缓冲区溢出
- 网络传输中断
- 字节序转换错误
健壮性设计
- 心跳包维持连接
- 数据包重传请求
- 校验和验证机制
- 连接异常中断恢复