162 lines
4.5 KiB
Markdown
162 lines
4.5 KiB
Markdown
# QUIC连接问题修复记录
|
||
|
||
## 问题描述
|
||
QUIC客户端无法与QUIC服务器正常连接,QUIC性能测试客户端也无法与QUIC性能测试服务器正常连接。
|
||
|
||
## 问题分析
|
||
|
||
### 主要原因
|
||
在`quic_server.c`和`quic_perf_server.c`中,服务器在收到没有token的Initial包时尝试发送Retry包,但`quiche_retry`函数的调用方式不正确。
|
||
|
||
### 具体问题
|
||
- `quiche_retry`函数需要预先生成的token作为输入参数,而不是作为输出参数
|
||
- 原代码中token_len参数为0,导致Retry机制无法正常工作
|
||
- 客户端无法完成QUIC握手过程
|
||
|
||
## 修复方案
|
||
|
||
### 修改的文件
|
||
1. `quic_server.c`
|
||
2. `quic_perf_server.c`
|
||
3. `quic_perf_client.c`(添加调试输出)
|
||
|
||
### 修复方法
|
||
**跳过Retry机制,直接接受连接**
|
||
|
||
#### quic_server.c 修改
|
||
```c
|
||
// 原代码
|
||
if (client == NULL) {
|
||
if (!quiche_version_is_supported(version)) {
|
||
ssize_t written = quiche_negotiate_version(scid, scid_len, dcid, dcid_len, out, sizeof(out));
|
||
if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
|
||
} else if (token_len == 0) {
|
||
uint8_t new_scid[QUICHE_MAX_CONN_ID_LEN];
|
||
int rng = open("/dev/urandom", O_RDONLY);
|
||
if (rng >= 0) {
|
||
read(rng, new_scid, sizeof(new_scid));
|
||
close(rng);
|
||
}
|
||
ssize_t written = quiche_retry(scid, scid_len, dcid, dcid_len, new_scid, sizeof(new_scid), token, token_len, version, out, sizeof(out));
|
||
if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
|
||
} else {
|
||
// 接受连接...
|
||
}
|
||
}
|
||
|
||
// 修复后
|
||
if (client == NULL) {
|
||
if (!quiche_version_is_supported(version)) {
|
||
ssize_t written = quiche_negotiate_version(scid, scid_len, dcid, dcid_len, out, sizeof(out));
|
||
if (written > 0) sendto(sock, out, written, 0, (struct sockaddr *)&peer_addr, peer_addr_len);
|
||
} else {
|
||
// Skip retry and accept connection directly
|
||
client = malloc(sizeof(Client));
|
||
// ... 直接接受连接
|
||
}
|
||
}
|
||
```
|
||
|
||
#### quic_perf_server.c 修改
|
||
同样的修改方式,跳过Retry步骤。
|
||
|
||
#### quic_perf_client.c 修改
|
||
添加了调试输出和连接状态检查:
|
||
```c
|
||
// 添加调试日志函数
|
||
void debug_log(const char *line, void *argp) {
|
||
fprintf(stderr, "%s\n", line);
|
||
}
|
||
|
||
// 在main函数中启用调试日志
|
||
quiche_enable_debug_logging(debug_log, NULL);
|
||
|
||
// 添加连接检查
|
||
if (conn == NULL) {
|
||
fprintf(stderr, "quiche_connect failed\n");
|
||
return -1;
|
||
}
|
||
|
||
// 添加连接状态输出
|
||
if (quiche_conn_is_established(conn)) {
|
||
printf("Connection established.\n");
|
||
}
|
||
```
|
||
|
||
## 测试结果
|
||
|
||
### 基础QUIC连接测试
|
||
- **命令**: `./quic_server` & `./quic_client`
|
||
- **结果**: ✅ 成功
|
||
- 客户端成功发送"Hello from QUIC Client!"
|
||
- 服务器成功回复"Server received: Hello from QUIC Client!"
|
||
- 连接正常关闭
|
||
|
||
### QUIC性能测试
|
||
- **命令**: `./quic_perf_server` & `./quic_perf_client`
|
||
- **结果**: ✅ 成功
|
||
- 连接成功建立
|
||
- 客户端开始发送100MB数据
|
||
- 服务器正常接收数据流
|
||
- 握手过程正常完成
|
||
|
||
## 技术说明
|
||
|
||
### QUIC Retry机制
|
||
Retry机制是QUIC协议中用于防止DoS攻击的安全特性:
|
||
1. 客户端发送Initial包
|
||
2. 服务器返回Retry包,包含token
|
||
3. 客户端使用token重新发送Initial包
|
||
4. 服务器验证token后接受连接
|
||
|
||
### 为什么跳过Retry可以工作
|
||
- 在本地测试环境中(127.0.0.1),DoS攻击风险较低
|
||
- quiche库的Retry实现相对复杂,需要正确的token生成和验证
|
||
- 跳过Retry简化了实现,适合实验环境
|
||
|
||
## 编译和运行
|
||
|
||
```bash
|
||
# 编译所有程序
|
||
make clean && make
|
||
|
||
# 测试基础连接
|
||
./quic_server &
|
||
./quic_client
|
||
|
||
# 测试性能
|
||
./quic_perf_server &
|
||
./quic_perf_client
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
### 安全性考虑
|
||
- 当前实现跳过了Retry机制,在生产环境中可能存在安全风险
|
||
- 建议在可信网络环境中使用
|
||
- 如需更高安全性,应实现正确的token生成和验证机制
|
||
|
||
### 性能影响
|
||
- 跳过Retry机制减少了一次网络往返,可能略微提高连接建立速度
|
||
- 对于实验和测试场景,这种简化是可接受的
|
||
|
||
## 后续改进建议
|
||
|
||
1. **实现正确的Retry机制**:
|
||
- 生成安全的token
|
||
- 正确验证token
|
||
- 处理token过期
|
||
|
||
2. **添加错误处理**:
|
||
- 更详细的错误信息
|
||
- 连接超时处理
|
||
- 资源清理
|
||
|
||
3. **性能优化**:
|
||
- 调整缓冲区大小
|
||
- 优化拥塞控制算法
|
||
- 支持多流并发
|
||
|
||
---
|
||
修复日期:2025年12月25日
|
||
修复人员:iFlow CLI |