刷题:
# 超时检测核心要点 |
|
## 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事件驱动模型
错误处理机制
常见异常场景
- 数据包长度校验失败
- 接收缓冲区溢出
- 网络传输中断
- 字节序转换错误
健壮性设计
- 心跳包维持连接
- 数据包重传请求
- 校验和验证机制
- 连接异常中断恢复