刷题:

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