first commit
This commit is contained in:
133
Makefile
Executable file
133
Makefile
Executable file
@@ -0,0 +1,133 @@
|
||||
# 支持通过参数传入CC编译器,默认使用gcc
|
||||
CC ?= gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -O2
|
||||
LDFLAGS = -lssl -lcrypto -lpthread
|
||||
|
||||
# 目标文件
|
||||
SERVER_TARGET = tls_server
|
||||
CLIENT_TARGET = tls_client
|
||||
TEST_TARGET = test_long_message
|
||||
SERVER_SOURCE = tls_server.c
|
||||
CLIENT_SOURCE = tls_client.c
|
||||
TEST_SOURCE = test_long_message.c
|
||||
|
||||
# 默认目标
|
||||
all: $(SERVER_TARGET) $(CLIENT_TARGET) $(TEST_TARGET)
|
||||
|
||||
# 编译服务器
|
||||
$(SERVER_TARGET): $(SERVER_SOURCE)
|
||||
$(CC) $(CFLAGS) -o $(SERVER_TARGET) $(SERVER_SOURCE) $(LDFLAGS)
|
||||
|
||||
# 编译客户端
|
||||
$(CLIENT_TARGET): $(CLIENT_SOURCE)
|
||||
$(CC) $(CFLAGS) -o $(CLIENT_TARGET) $(CLIENT_SOURCE) $(LDFLAGS)
|
||||
|
||||
# 编译测试程序
|
||||
$(TEST_TARGET): $(TEST_SOURCE)
|
||||
$(CC) $(CFLAGS) -o $(TEST_TARGET) $(TEST_SOURCE) -lssl -lcrypto
|
||||
|
||||
# 生成证书
|
||||
cert:
|
||||
chmod +x generate_cert.sh
|
||||
./generate_cert.sh
|
||||
|
||||
# 运行服务器(默认端口8443)
|
||||
run: $(SERVER_TARGET) cert
|
||||
./$(SERVER_TARGET)
|
||||
|
||||
# 运行服务器(指定端口)
|
||||
run-port: $(SERVER_TARGET) cert
|
||||
@echo "请输入端口号:"
|
||||
@read port; ./$(SERVER_TARGET) -p $$port
|
||||
|
||||
# 测试客户端
|
||||
test: $(CLIENT_TARGET)
|
||||
@echo "测试客户端,发送消息 'Hello World'"
|
||||
./$(CLIENT_TARGET) -m "Hello World"
|
||||
|
||||
# 测试循环客户端
|
||||
test-loop: $(CLIENT_TARGET)
|
||||
@echo "测试循环客户端,发送5次消息,间隔2秒"
|
||||
./$(CLIENT_TARGET) -m "Loop Test" -c 5 -i 2
|
||||
|
||||
# 测试TLS 1.2
|
||||
test-tls12: $(CLIENT_TARGET)
|
||||
@echo "测试TLS 1.2连接"
|
||||
./$(CLIENT_TARGET) -m "TLS 1.2 Test" -t 1.2
|
||||
|
||||
# 测试TLS 1.3
|
||||
test-tls13: $(CLIENT_TARGET)
|
||||
@echo "测试TLS 1.3连接"
|
||||
./$(CLIENT_TARGET) -m "TLS 1.3 Test" -t 1.3
|
||||
|
||||
# 测试TLS版本对比
|
||||
test-tls-compare: $(CLIENT_TARGET)
|
||||
@echo "测试TLS版本对比"
|
||||
@echo "=== TLS 1.2 测试 ==="
|
||||
./$(CLIENT_TARGET) -m "TLS 1.2" -t 1.2 -c 2 -i 1
|
||||
@echo ""
|
||||
@echo "=== TLS 1.3 测试 ==="
|
||||
./$(CLIENT_TARGET) -m "TLS 1.3" -t 1.3 -c 2 -i 1
|
||||
@echo ""
|
||||
@echo "=== 自动协商测试 ==="
|
||||
./$(CLIENT_TARGET) -m "Auto TLS" -t auto -c 2 -i 1
|
||||
|
||||
# 测试并发连接
|
||||
test-concurrent: $(CLIENT_TARGET)
|
||||
@echo "测试并发客户端连接"
|
||||
@echo "启动2个并发客户端..."
|
||||
./$(CLIENT_TARGET) -m "Concurrent 1" -c 1 &
|
||||
./$(CLIENT_TARGET) -m "Concurrent 2" -c 1 &
|
||||
wait
|
||||
@echo "并发测试完成"
|
||||
|
||||
# 测试长消息
|
||||
test-long: $(TEST_TARGET)
|
||||
@echo "测试长消息,发送1000字符消息"
|
||||
./$(TEST_TARGET) 1000
|
||||
|
||||
# 清理
|
||||
clean:
|
||||
rm -f $(SERVER_TARGET) $(CLIENT_TARGET) $(TEST_TARGET)
|
||||
rm -rf cert/
|
||||
|
||||
# 安装依赖(Ubuntu/Debian)
|
||||
install-deps:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libssl-dev
|
||||
|
||||
# 安装依赖(CentOS/RHEL)
|
||||
install-deps-centos:
|
||||
sudo yum install -y openssl-devel
|
||||
|
||||
# 显示编译器信息
|
||||
info:
|
||||
@echo "当前编译器: $(CC)"
|
||||
@echo "编译选项: $(CFLAGS)"
|
||||
@echo "链接选项: $(LDFLAGS)"
|
||||
|
||||
# 帮助信息
|
||||
help:
|
||||
@echo "可用目标:"
|
||||
@echo " all - 编译服务器、客户端和测试程序"
|
||||
@echo " cert - 生成自签名证书"
|
||||
@echo " run - 运行服务器(端口8443)"
|
||||
@echo " run-port - 运行服务器(指定端口)"
|
||||
@echo " test - 测试客户端连接"
|
||||
@echo " test-loop - 测试循环客户端连接"
|
||||
@echo " test-tls12 - 测试TLS 1.2连接"
|
||||
@echo " test-tls13 - 测试TLS 1.3连接"
|
||||
@echo " test-tls-compare - 测试TLS版本对比"
|
||||
@echo " test-concurrent - 测试并发客户端连接"
|
||||
@echo " test-long - 测试长消息发送"
|
||||
@echo " clean - 清理生成的文件"
|
||||
@echo " install-deps - 安装依赖(Ubuntu/Debian)"
|
||||
@echo " info - 显示编译器信息"
|
||||
@echo " help - 显示此帮助信息"
|
||||
@echo ""
|
||||
@echo "编译器参数:"
|
||||
@echo " CC=clang make all - 使用clang编译器"
|
||||
@echo " CC=gcc make all - 使用gcc编译器(默认)"
|
||||
@echo " CC=cc make all - 使用系统默认编译器"
|
||||
|
||||
.PHONY: all cert run run-port clean install-deps install-deps-centos info help
|
310
README.md
Executable file
310
README.md
Executable file
@@ -0,0 +1,310 @@
|
||||
# TLS多客户端服务器
|
||||
|
||||
这是一个支持多客户端连接的TLS服务器,使用C语言实现,支持TLS 1.2和1.3协议。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 支持多客户端并发连接(最多100个)
|
||||
- ✅ 支持TLS 1.2和1.3协议
|
||||
- ✅ 动态端口指定
|
||||
- ✅ 接收客户端消息并计算MD5返回
|
||||
- ✅ 线程安全的客户端处理
|
||||
- ✅ 优雅的服务器关闭
|
||||
|
||||
## 系统要求
|
||||
|
||||
- Linux/Unix系统
|
||||
- GCC编译器
|
||||
- OpenSSL开发库
|
||||
- pthread库
|
||||
|
||||
## 安装依赖
|
||||
|
||||
### Ubuntu/Debian系统
|
||||
```bash
|
||||
make install-deps
|
||||
```
|
||||
|
||||
### CentOS/RHEL系统
|
||||
```bash
|
||||
make install-deps-centos
|
||||
```
|
||||
|
||||
## 编译和运行
|
||||
|
||||
### 1. 编译项目
|
||||
```bash
|
||||
# 使用默认gcc编译器
|
||||
make all
|
||||
|
||||
# 使用指定编译器
|
||||
CC=clang make all # 使用clang编译器
|
||||
CC=gcc make all # 使用gcc编译器(默认)
|
||||
CC=cc make all # 使用系统默认编译器
|
||||
|
||||
# 查看编译器信息
|
||||
make info
|
||||
```
|
||||
|
||||
### 2. 生成自签名证书
|
||||
```bash
|
||||
make cert
|
||||
```
|
||||
|
||||
**注意**: 证书将生成在 `cert/` 目录中:
|
||||
- `cert/server.key` - 服务器私钥
|
||||
- `cert/server.crt` - 服务器证书
|
||||
|
||||
### 3. 运行服务器
|
||||
|
||||
#### 使用默认端口8443
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
|
||||
#### 指定端口运行
|
||||
```bash
|
||||
make run-port
|
||||
# 然后输入端口号,例如:9000
|
||||
```
|
||||
|
||||
#### 直接运行
|
||||
```bash
|
||||
./tls_server -p 9000 # 指定端口9000
|
||||
./tls_server # 使用默认端口8443
|
||||
./tls_server -h # 显示帮助信息
|
||||
```
|
||||
|
||||
### 4. 测试客户端连接
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
#### 手动测试客户端
|
||||
```bash
|
||||
./tls_client -m "Hello World" # 基本测试
|
||||
./tls_client -s 1500 # 发送1500字节01数据
|
||||
./tls_client -m "Hello" -c 5 -i 2 # 循环测试5次,间隔2秒
|
||||
./tls_client -s 2048 -c 3 -i 1 # 发送2048字节01数据,循环3次
|
||||
./tls_client -m "Test" -h 192.168.1.100 -p 9000 # 连接到指定服务器
|
||||
./tls_client -m "TLS1.2" -t 1.2 # 强制使用TLS 1.2
|
||||
./tls_client -s 512 -t 1.2 -c 2 -i 1 # 字节数+TLS版本测试
|
||||
./tls_client -m "TLS1.3" -t 1.3 # 强制使用TLS 1.3
|
||||
./tls_client -m "Auto" -t auto # 自动协商TLS版本
|
||||
./tls_client -m "Compare" -C -c 5 # TLS版本对比测试,循环5轮
|
||||
./tls_client -s 1024 -C -c 10 # TLS版本对比测试,1024字节,10轮
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 服务器端
|
||||
1. 启动服务器后,它会监听指定端口的TLS连接
|
||||
2. 支持最多100个并发客户端连接
|
||||
3. 每个客户端连接都会在独立线程中处理
|
||||
4. 服务器会显示连接状态和TLS版本信息
|
||||
5. 按Ctrl+C可以优雅地关闭服务器
|
||||
6. **新增功能**: 带时间戳的详细日志记录
|
||||
7. **新增功能**: 按IP地址统计连接成功率和耗时
|
||||
|
||||
### 命令行参数
|
||||
```bash
|
||||
./tls_server -p 9000 # 在端口9000运行
|
||||
./tls_server # 使用默认端口8443
|
||||
./tls_server -h # 显示帮助信息
|
||||
```
|
||||
|
||||
### 客户端
|
||||
1. 连接到TLS服务器
|
||||
2. 发送消息给服务器
|
||||
3. 接收服务器计算的MD5哈希值
|
||||
4. 自动断开连接
|
||||
5. 支持循环测试,可指定测试次数和间隔
|
||||
6. 提供详细的测试统计报告
|
||||
|
||||
### 客户端参数
|
||||
```bash
|
||||
./tls_client -m <消息> 或 -s <字节数> [-h 主机] [-p 端口] [-c 次数] [-i 间隔秒数] [-t TLS版本] [-C]
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `-m 消息`: 要发送的消息内容
|
||||
- `-s 字节数`: 要发送的字节数(发送01数据,最大4095字节)
|
||||
- `-h 主机`: 服务器主机地址(默认: localhost)
|
||||
- `-p 端口`: 服务器端口(默认: 8443)
|
||||
- `-c 次数`: 循环测试次数(默认: 1)
|
||||
- `-i 间隔`: 测试间隔秒数(默认: 1)
|
||||
- `-t 版本`: TLS版本: 1.2, 1.3, auto(默认: auto)
|
||||
- `-C`: TLS版本对比模式,每轮先测试TLS 1.2,等待2秒后测试TLS 1.3
|
||||
|
||||
### 测试统计报告
|
||||
客户端测试完成后会输出详细的统计信息:
|
||||
|
||||
```
|
||||
========================================
|
||||
测试统计报告
|
||||
========================================
|
||||
测试开始时间: Mon Jan 15 10:30:00 2024
|
||||
测试结束时间: Mon Jan 15 10:30:15 2024
|
||||
总耗时: 15.00 秒
|
||||
----------------------------------------
|
||||
总测试次数: 5
|
||||
成功次数: 5
|
||||
失败次数: 0
|
||||
成功率: 100.00%
|
||||
----------------------------------------
|
||||
平均每次测试耗时: 3.00 秒
|
||||
========================================
|
||||
✅ 所有测试通过!
|
||||
```
|
||||
|
||||
**统计信息包括:**
|
||||
- 测试开始和结束时间
|
||||
- 总耗时
|
||||
- 总测试次数
|
||||
- 成功和失败次数
|
||||
- 成功率
|
||||
- 平均每次测试耗时
|
||||
- 失败率(如果有失败)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── tls_server.c # 服务器主程序
|
||||
├── tls_client.c # 客户端测试程序
|
||||
├── generate_cert.sh # 证书生成脚本
|
||||
├── Makefile # 编译配置
|
||||
├── cert/ # 证书目录
|
||||
│ ├── server.key # 服务器私钥
|
||||
│ └── server.crt # 服务器证书
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 服务器端特性
|
||||
- 使用OpenSSL库实现TLS支持
|
||||
- 多线程处理客户端连接
|
||||
- 线程安全的客户端管理
|
||||
- 支持TLS 1.2和1.3协议
|
||||
- MD5哈希计算和返回
|
||||
|
||||
### 安全特性
|
||||
- TLS加密传输
|
||||
- 自签名证书(测试用)
|
||||
- 客户端证书验证(可配置)
|
||||
|
||||
## 示例输出
|
||||
|
||||
### 服务器端
|
||||
```
|
||||
[2024-01-15 10:30:00] 启动TLS服务器,端口: 8443
|
||||
[2024-01-15 10:30:00] 服务器已启动,等待客户端连接...
|
||||
[2024-01-15 10:30:00] 支持TLS 1.2和1.3
|
||||
[2024-01-15 10:30:00] 按Ctrl+C停止服务器
|
||||
[2024-01-15 10:30:05] 新连接来自: 127.0.0.1:12345
|
||||
[2024-01-15 10:30:05] 当前活跃连接数: 0/100
|
||||
[2024-01-15 10:30:05] SSL握手成功,IP: 127.0.0.1,TLS版本: TLSv1.3
|
||||
[2024-01-15 10:30:05] 客户端处理线程已创建,当前连接数: 1
|
||||
[2024-01-15 10:30:05] 新客户端连接,IP: 127.0.0.1,线程ID: 140123456789,套接字: 4
|
||||
[2024-01-15 10:30:05] 收到客户端消息,IP: 127.0.0.1,长度: 11,内容: Hello World
|
||||
[2024-01-15 10:30:05] MD5计算完成,IP: 127.0.0.1,结果: b10a8db164e0754105b7a99be72e3fe5
|
||||
[2024-01-15 10:30:05] 已发送MD5结果给客户端,IP: 127.0.0.1
|
||||
[2024-01-15 10:30:05] 开始清理客户端连接,IP: 127.0.0.1,套接字: 4
|
||||
[2024-01-15 10:30:05] 客户端连接已关闭,IP: 127.0.0.1,当前连接数: 0
|
||||
```
|
||||
|
||||
### IP统计报告示例
|
||||
```
|
||||
[2024-01-15 10:35:00] IP统计 [127.0.0.1]: 总次数=10, 成功次数=10, 成功率=100.00%, 耗时=300秒
|
||||
[2024-01-15 10:40:00] IP统计 [192.168.1.100]: 总次数=10, 成功次数=8, 成功率=80.00%, 耗时=600秒
|
||||
```
|
||||
|
||||
### 客户端
|
||||
```
|
||||
[2024-01-15 10:30:00] 连接到TLS服务器: localhost:8443
|
||||
[2024-01-15 10:30:00] 发送消息: Hello World
|
||||
[2024-01-15 10:30:00] 测试次数: 1, 间隔: 1秒
|
||||
[2024-01-15 10:30:00] TLS版本: 自动协商
|
||||
[2024-01-15 10:30:00] 测试开始时间: Mon Jan 15 10:30:00 2024
|
||||
|
||||
[2024-01-15 10:30:00] === 测试 1/1 ===
|
||||
[2024-01-15 10:30:00] TCP连接成功
|
||||
[2024-01-15 10:30:00] SSL握手成功,TLS版本: TLSv1.3
|
||||
[2024-01-15 10:30:00] 消息已发送 (长度: 11)
|
||||
[2024-01-15 10:30:00] 收到MD5结果: b10a8db164e0754105b7a99be72e3fe5
|
||||
[2024-01-15 10:30:00] 连接已关闭
|
||||
|
||||
[2024-01-15 10:30:01] ========================================
|
||||
[2024-01-15 10:30:01] 测试统计报告
|
||||
[2024-01-15 10:30:01] ========================================
|
||||
[2024-01-15 10:30:01] 测试开始时间: Mon Jan 15 10:30:00 2024
|
||||
[2024-01-15 10:30:01] 测试结束时间: Mon Jan 15 10:30:01 2024
|
||||
[2024-01-15 10:30:01] 总耗时: 1.00 秒
|
||||
[2024-01-15 10:30:01] ----------------------------------------
|
||||
[2024-01-15 10:30:01] 总测试次数: 1
|
||||
[2024-01-15 10:30:01] 成功次数: 1
|
||||
[2024-01-15 10:30:01] 失败次数: 0
|
||||
[2024-01-15 10:30:01] 总重试次数: 0
|
||||
[2024-01-15 10:30:01] 成功率: 100.00%
|
||||
[2024-01-15 10:30:01] ----------------------------------------
|
||||
[2024-01-15 10:30:01] 平均每次测试耗时: 1.00 秒
|
||||
[2024-01-15 10:30:01] ========================================
|
||||
[2024-01-15 10:30:01] ✅ 所有测试通过!
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 编译错误
|
||||
- 确保已安装OpenSSL开发库
|
||||
- 检查GCC版本是否支持C99标准
|
||||
- **struct timeval错误**: 已添加`#include <sys/time.h>`头文件
|
||||
- **pthread错误**: 确保链接pthread库 (`-lpthread`)
|
||||
|
||||
### 编译器支持
|
||||
- **默认编译器**: gcc
|
||||
- **支持编译器**: gcc, clang, cc
|
||||
- **编译器参数**: 使用`CC=编译器名 make all`指定编译器
|
||||
- **编译器信息**: 使用`make info`查看当前编译器设置
|
||||
|
||||
### 连接错误
|
||||
- 确保端口未被占用
|
||||
- 检查防火墙设置
|
||||
- 验证证书文件是否存在
|
||||
|
||||
### SSL错误处理
|
||||
- **SSL读取错误: 1**: 通常是客户端关闭连接导致,已改进错误处理
|
||||
- **消息截断警告**: 当消息长度接近4KB时会显示警告
|
||||
- **客户端立即关闭**: 客户端现在会等待服务器响应后再关闭连接
|
||||
- **SSL系统调用错误**: 改进了SSL_ERROR_SYSCALL错误处理,防止服务器异常退出
|
||||
- **连接清理**: 所有SSL错误都会正确清理连接资源
|
||||
|
||||
### 多客户端连接问题
|
||||
- **服务器卡住**: 已添加线程分离和超时机制
|
||||
- **连接数统计**: 服务器会显示当前活跃连接数
|
||||
- **资源清理**: 改进了客户端连接清理逻辑
|
||||
- **线程管理**: 使用pthread_detach自动回收线程资源
|
||||
|
||||
### 日志功能
|
||||
- **时间戳日志**: 所有日志都带有精确的时间戳
|
||||
- **IP统计**: 按IP地址统计连接成功率和耗时
|
||||
- **统计报告**: 每10次连接自动生成统计报告
|
||||
- **详细记录**: 记录连接、SSL握手、消息处理等全过程
|
||||
|
||||
### 客户端日志功能
|
||||
- **时间戳日志**: 客户端所有操作都带有时间戳
|
||||
- **测试统计**: 显示测试次数、成功/失败次数、成功率
|
||||
- **性能监控**: 记录每次测试的耗时和平均耗时
|
||||
- **详细记录**: 记录连接建立、SSL握手、消息发送/接收等全过程
|
||||
- **重试机制**: 连接失败时自动重试3次,使用指数退避策略(2秒、4秒、8秒)
|
||||
- **重试统计**: 统计总重试次数,重试成功不计入失败
|
||||
- **超时保护**: 套接字读写操作30秒超时,防止客户端卡住
|
||||
|
||||
### 性能优化
|
||||
- 调整MAX_CLIENTS常量以支持更多连接
|
||||
- 修改BUFFER_SIZE以适应更大的消息
|
||||
- 考虑使用连接池优化资源使用
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目仅用于学习和测试目的。
|
22
cert/ca.crt
Executable file
22
cert/ca.crt
Executable file
@@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDozCCAougAwIBAgIUPJ2ri4qd4BpnUcb/6vCZ//2uYd8wDQYJKoZIhvcNAQEL
|
||||
BQAwYTELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
|
||||
aWppbmcxDjAMBgNVBAoMBU15T3JnMQswCQYDVQQLDAJJVDERMA8GA1UEAwwITXlS
|
||||
b290Q0EwHhcNMjUxMDA3MDYwMDI3WhcNMzUxMDA1MDYwMDI3WjBhMQswCQYDVQQG
|
||||
EwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEOMAwGA1UE
|
||||
CgwFTXlPcmcxCzAJBgNVBAsMAklUMREwDwYDVQQDDAhNeVJvb3RDQTCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMf0LkX1LF71yhsnqM4laww8B6RRo3nF
|
||||
UD8pzGr4J8iFhtPyUr8T4Q3B1Et98sV+7zx4qxgYaMZ1IMX6QDti/T1qurAONhy9
|
||||
/HDGDrvMYE0AOQhvRvcqicpayqNqjLBJgdzh92Uei5s8F30tqLVm8tN715UaVDuc
|
||||
enjh6s9e1GpR6CzvbKLEmsUESC9YFeV1kdlbn+b5cFgDElBan2nEpS2sAUamc5pt
|
||||
+h8cjwojd58o3s+Cfc69DXnbSzRVo/7DSLO0itBLNvoo1YBSmJkuGDzsu0dcUD3m
|
||||
84scYvxzvOZiUqESEaqXRMPw7OcOAQSbjloUgWfiX35ntmdeK5xyk90CAwEAAaNT
|
||||
MFEwHQYDVR0OBBYEFH2YOXkKFAfeuB+IXTTB5NjXFsBqMB8GA1UdIwQYMBaAFH2Y
|
||||
OXkKFAfeuB+IXTTB5NjXFsBqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAA7+FwMG6yJi9vDHdaFRWA/RFSTBCo5bcFJ7DO7DwvsEOfHnP5ygulDY
|
||||
astft8RsGelVSlXJtWHaCdByST9V282q+P2Q2jjwfmVB5ATq2MAgobYsU3nwLg3r
|
||||
c6zd34GeTJ6wjyGF5g3rDG8NqcHlARRwL8GbWX5lbcq9ooYLl9KHgFY+gz6u5njx
|
||||
WyJdnCMLRUgTt5tsT/3k5PU2V9C5kwZh/eAocEsfhuXDplFHeqdvZTZaXD/oX+So
|
||||
jPnLL6fsAoCskIhVVM0xGAXtWmhk+1LVtrqCfTQrqBhtXi5qCl3NLmBf+d0OClwh
|
||||
HgkcqJYr+WaiEHLUyIdfZji/ZEml7aA=
|
||||
-----END CERTIFICATE-----
|
28
cert/ca.key
Executable file
28
cert/ca.key
Executable file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDH9C5F9Sxe9cob
|
||||
J6jOJWsMPAekUaN5xVA/Kcxq+CfIhYbT8lK/E+ENwdRLffLFfu88eKsYGGjGdSDF
|
||||
+kA7Yv09arqwDjYcvfxwxg67zGBNADkIb0b3KonKWsqjaoywSYHc4fdlHoubPBd9
|
||||
Lai1ZvLTe9eVGlQ7nHp44erPXtRqUegs72yixJrFBEgvWBXldZHZW5/m+XBYAxJQ
|
||||
Wp9pxKUtrAFGpnOabfofHI8KI3efKN7Pgn3OvQ1520s0VaP+w0iztIrQSzb6KNWA
|
||||
UpiZLhg87LtHXFA95vOLHGL8c7zmYlKhEhGql0TD8OznDgEEm45aFIFn4l9+Z7Zn
|
||||
XiuccpPdAgMBAAECggEAIXon7y2LxsBXHLHIqO8J26wHSYMjoCUheNnKMFSo8IEu
|
||||
oDivku9EnFWJ8jO9nERS0KiRWMDpdeSxXoQ2EdtSc+B1LjnK5IgIhmcam2Wt7+Zs
|
||||
JhXfZ013cWo/CBo0QOWluPIaRhNVo2Ftu1cUKn74g+D1qLCWTr61oJyOgDar0Lrn
|
||||
Cf6DvZl5pGJVV3lQUiYNjZR5qEywpaMvVcVjRUaUElNplld9VLm9xE8lhwjq0GER
|
||||
LQKt63p6b34kUKM8dS4cWr7+EutQPEwMLPtM/0HBUpid0qRIj5iZtROo86g8ksS1
|
||||
vzGQJ5asfgiRdZX7tkrj31RDg4latXQ8saHfZeOLzwKBgQDu0jI25NKHpxbWOveS
|
||||
OBtZK12aiO4n/nria3YsOTGCWYqtFUsZqcfj7/C30KH0vKODNoy9933+5roEdOvc
|
||||
FIo6PDUADiZNGN3Oy69EhBVuGr4OkK2LjjCSSI2jvWONTDN4lQ17nAiiJ0LMT/TX
|
||||
CBIef0i/QpAOYeuUh8FT1Ph8DwKBgQDWVkIfGLXFGeRORjUBuVv0ceQMR1K8Nz0/
|
||||
ldR0nuwoxMReSgoP2OVlSXqiLWAC6zPuXxE8KBo7GkvxEc4+AcwcgPZoAkK0KGBO
|
||||
BCyS6V45abp56nanAxgGZJ/BNd3MEma9rbraFjC/yF29heGeE0s+gR6uPMW32qJe
|
||||
U8nBJFj1UwKBgCvxm3HEWwTA9w/GW+WY01duBlQ4G/JZ/gyJj34FrBl7FmxQvbfk
|
||||
KLbFYLrB9fsNdtze/bi6wIFVvSayyO9/DAw5JdtzvxJyn+W8TuzBjRvsacpOTtCe
|
||||
Akv4c6+MWrQWMGZgrtFu3ZvQs5bao4epoYPhEea3fcBXvjxfWnBtgKd7AoGAeTT8
|
||||
XWN230heEFmpfhkZRCnnwX3P7rn6O+v54h1BBWkIdx29hOquBtI/tFiek+f4TROb
|
||||
xn4TH1smmOPt0qjniTLwpS6qFAFFPLklj8rCywrcNjd988JPIsZihTt1+wJo8Vi+
|
||||
crfbx4iCYjvEs8TLZ0RTWkrpsKfF7DvLuxpX6BsCgYBygdo3EGCuKlymw1V8xrWU
|
||||
rumFLCqtTxUfTu4hzWpPM6wDBh6BNMOeXw8Z7bE69Ha8rWxGpu0qEgRM2RegNJza
|
||||
f2j+hZUZbdJsGoClpq3ROBwBJHZ3ZUs+4po3JWQWc0DblaMOZftVIAh7EbMhmVPe
|
||||
OwMin2eLv4aEzSEtSYK8aQ==
|
||||
-----END PRIVATE KEY-----
|
20
cert/client.crt
Executable file
20
cert/client.crt
Executable file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDTDCCAjQCFDcqUklN76USvOskALvfIUttDamKMA0GCSqGSIb3DQEBCwUAMGEx
|
||||
CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n
|
||||
MQ4wDAYDVQQKDAVNeU9yZzELMAkGA1UECwwCSVQxETAPBgNVBAMMCE15Um9vdENB
|
||||
MB4XDTI1MTAwNzA2MDE1NFoXDTI2MTAwNzA2MDE1NFowZDELMAkGA1UEBhMCQ04x
|
||||
EDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxDjAMBgNVBAoMBU15
|
||||
T3JnMQ8wDQYDVQQLDAZDbGllbnQxEDAOBgNVBAMMB2NsaWVudDEwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaJLDgelGrpFYgFKqGUAvopUg709OHZjwT
|
||||
FyEiBic7NgWWLSne5MFBMVY18nZF9khaJZn6wN//miubkb6hbP7695GERB/wVDTj
|
||||
nMd9FjDaZvYoZQkxemjCBDpyYd5vfP7f83EkNEnD2VljBbRATfl7tXxcbELDHC9N
|
||||
ukkFD/3sD8IiSpZyyO1U3Ljv/vuV4UCTadJlKB+R5hNqI7QWOr59bRQD2mykXGtx
|
||||
+gf44rBXqfglhY2xevuvBHYvRncHARIjOvRUmf0ue3CHPtS6wafWiPoYz6Q92xa4
|
||||
xBDOtY9lrOZuwGzAsA0dRN80Uu2YmRUoe5bAnZiMk3iDBlt6dNafAgMBAAEwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAHV2eD8V79IF/u74mXjQxEalAJ0nA/ooMASbDIx08/wy
|
||||
S4mwZl08yMFRHwV4nMp6kbRh81cWYtTe8+LW8w1mHUKd/7MzStKT93IADPULFjCi
|
||||
4ee0wJmQePnWDxOcUUz6zEfh72W2Fy1CrZ99R0ZMF5ifUGUPNfmj0NsfqGgEReOO
|
||||
ai3yNTEg0zg35+2SnHzodFerKWDeis5gP5EOURfHcKYKE3oEatxwn9//gb/sICoR
|
||||
99rxHTO4BZB06DIArdxomZ84eanBkHGsJGuPjncR9fyLQcbP1SP0WUcR2p73xAd3
|
||||
EMBByvrT1m68g7L0SVqN05EshJoYWONb4gTHd2BCO0A=
|
||||
-----END CERTIFICATE-----
|
28
cert/client.key
Executable file
28
cert/client.key
Executable file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCaJLDgelGrpFYg
|
||||
FKqGUAvopUg709OHZjwTFyEiBic7NgWWLSne5MFBMVY18nZF9khaJZn6wN//miub
|
||||
kb6hbP7695GERB/wVDTjnMd9FjDaZvYoZQkxemjCBDpyYd5vfP7f83EkNEnD2Vlj
|
||||
BbRATfl7tXxcbELDHC9NukkFD/3sD8IiSpZyyO1U3Ljv/vuV4UCTadJlKB+R5hNq
|
||||
I7QWOr59bRQD2mykXGtx+gf44rBXqfglhY2xevuvBHYvRncHARIjOvRUmf0ue3CH
|
||||
PtS6wafWiPoYz6Q92xa4xBDOtY9lrOZuwGzAsA0dRN80Uu2YmRUoe5bAnZiMk3iD
|
||||
Blt6dNafAgMBAAECggEAHzak1qAOX7qEcjSdH1ugPbkSeoL3h6iNK7R4UiJ62UOk
|
||||
N/fnTap289OgyIXTq3Emz5JjruJVubWndPY7awbeT0XIoscEzK7QkvLRdqQCuoc0
|
||||
+5MSHIHUKs2eZEErQNpH5mOumo04Dr+5mRKzoH3pskJa74BAuK/BaHT7ilnlqmJp
|
||||
xoVDilQC5jiGys4MpCtx2Kt8l2CAlsE84vZff+PGV4O0kDsqqky6uhuJtrTaZs7j
|
||||
bFJoWcG9L9s/WZtwb32X11epHOmQv6mrzgFyminzkBoCmO7cBNB44eyQJnnNYece
|
||||
MnFdklKCPuSYc0lgWSepuUkrz8hLKC8+QzQO668UsQKBgQDYfmM9ES087q+H9ooc
|
||||
l/GZsEdV/iN+Kcm1JkyyypzRQrmVUTyAp9SFRaLiQP3ea6Jy/CUeCC1SWwPLJM9E
|
||||
EA0jlCYIPFOhsT7I1EM5Qa7DWf1z8bAVUMs1cqZrzOegIbEiKSu+Ym4MMVtvKvON
|
||||
OqaTtwwNUvyxVHmsuZBUZzzXOQKBgQC2RZLvI4p/Qzbpznsmq4beCSFEACRJTa/F
|
||||
e58/Xbmfdwo42WU17548k7sN/Jgyg7PVu4+bY5ZAYMibXFtEHW+sBz/4Gf90BZxE
|
||||
egiKD7gtEw4kxu8BfygJgWyszK4guzTYKx+DJRl2xtvGCNX10SrV1rEfHvYNHXj8
|
||||
BekIht0ElwKBgQCb/FSch2fE42Vt3WEdwQy+45hCiV4hZRKEhxf0KrBaxmzY/TNO
|
||||
r54ceFQoGRPR0lO17Z8AyHt/Pzy4fckpDTeqTvAoNu87LW5DXU0iUAUPlCNeCuII
|
||||
ObJwzC7EtVqesifiqS9veZQ5DMcIjjX1qDClddolL4oKawdQQFORvODFYQKBgB/l
|
||||
FM0b3wRd8qH/K7WclkEMP/HyRGc/XN6lvzwLXov0/KjuAbPqdjoLb9QGu2s7eKCR
|
||||
7ZM3Xfdt+CyXgLDupbfonN0BT54xzSJ+aDgggA4DI5pz5SbR5WOkbivetSmtGJYr
|
||||
FZyRRV9vdM22hho5u9EnfF8Bv/STj7QqJJkFYG+JAoGBAKx9evvrTS1tgcduIZXm
|
||||
fFQJ/Y4r2AtUqPs5TE69wdQg+WL35Njd0r3gguK4isur4NV3Yb97JUN95L9E2cY2
|
||||
asRkLNGiWwgFtSp6xTA6ltfLbdW+smZgeFkMTu8O6WBUiep6TEkRiR60UnKxDfwO
|
||||
eNr+BzEVtLNmY43pf3uRjnfm
|
||||
-----END PRIVATE KEY-----
|
20
cert/server.crt
Executable file
20
cert/server.crt
Executable file
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDUTCCAjkCFFDhJROu/swj7dMd1fokMaMYKNtfMA0GCSqGSIb3DQEBCwUAMGEx
|
||||
CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n
|
||||
MQ4wDAYDVQQKDAVNeU9yZzELMAkGA1UECwwCSVQxETAPBgNVBAMMCE15Um9vdENB
|
||||
MB4XDTI1MTAwNzA2MDExN1oXDTI2MTAwNzA2MDExN1owaTELMAkGA1UEBhMCQ04x
|
||||
EDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWppbmcxDjAMBgNVBAoMBU15
|
||||
T3JnMQ8wDQYDVQQLDAZTZXJ2ZXIxFTATBgNVBAMMDG15c2VydmVyLmNvbTCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPqMRUu5Vni7eAPEcstj2bfPOJXN
|
||||
XtEvCLHH5XeVlmdUhwXZ9AlCITdjp2teV/hn6kbJACegY1g9IetnLqkWWm2MBvqm
|
||||
OG9H2UfobSyfNiYmgI4X05KLlIzQ2vm2F0+sl5BQUv1t3ly4zXdb1q6O6xdiUaNG
|
||||
d66DTL2MyaxCR8tgmhHiohEvozRzLUu63nbufva4CLALF71G45fq+jy+moIYeWop
|
||||
SQn3Teu3HRTaV+XCMxCFw4TrJvN4aGDDHiE8f8TneSIISf8zEt4qGVzwSkOWTqZC
|
||||
jkqe1vmV3qpMnfXdpsUYRXegKQjorIddWGBM86MisY3kFO9wmQjCggV0BssCAwEA
|
||||
ATANBgkqhkiG9w0BAQsFAAOCAQEAKNIOPNNcZudVUq7ql16nnbBZrEkSF5WlMXZa
|
||||
Zaka/Ix7/fuGJ2EKcy8npcPLOMSzt7DX9EmcIBF5FGrwBvLdrbR/ikW62vTgUv9q
|
||||
l+P/VegPT6FXlU5AD5ur0hAYCsMk4EbT9ZfmU3qXDc55qXTQUY/dlzTOaCRBBQdO
|
||||
PVImAdU/Zv3hKhXgj/93wWKdPFc3tjLiY3tqbMVJLkRaiQsR5tDVb8DQOXOgwWHf
|
||||
PBdmrkLjYZHWk+wCinR50V9viS6KMXc/YJlDx1o5lfFdsMRWteDpHS4X01C7BF8R
|
||||
vdJOr4sfVP8GRuFadW9GlEhRN4BKj/PecsZr+3HPUOHyfPesTw==
|
||||
-----END CERTIFICATE-----
|
17
cert/server.csr
Executable file
17
cert/server.csr
Executable file
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICrjCCAZYCAQAwaTELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAO
|
||||
BgNVBAcMB0JlaWppbmcxDjAMBgNVBAoMBU15T3JnMQ8wDQYDVQQLDAZTZXJ2ZXIx
|
||||
FTATBgNVBAMMDG15c2VydmVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAPqMRUu5Vni7eAPEcstj2bfPOJXNXtEvCLHH5XeVlmdUhwXZ9AlCITdj
|
||||
p2teV/hn6kbJACegY1g9IetnLqkWWm2MBvqmOG9H2UfobSyfNiYmgI4X05KLlIzQ
|
||||
2vm2F0+sl5BQUv1t3ly4zXdb1q6O6xdiUaNGd66DTL2MyaxCR8tgmhHiohEvozRz
|
||||
LUu63nbufva4CLALF71G45fq+jy+moIYeWopSQn3Teu3HRTaV+XCMxCFw4TrJvN4
|
||||
aGDDHiE8f8TneSIISf8zEt4qGVzwSkOWTqZCjkqe1vmV3qpMnfXdpsUYRXegKQjo
|
||||
rIddWGBM86MisY3kFO9wmQjCggV0BssCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQCWDmyzIhCsrDKrOb7MVN6GJHIqQdZj88zxil0KSR7wxHXCZD2TOJXUnwEnQgW7
|
||||
m/q4375rpYO5bjhDr0JxnIsS0LB/QDZKMNA6PgR1LcTUZsQValvDnEVu/u1rQOgR
|
||||
IZoVrGcKjfJ7rYPnArAPqhyLEtME6QYz7hzBeRdZhN+P1HQdBT5tpXi84awZmr5P
|
||||
6MgKUS4HWui11sO/TRAL9AR2721PNN+ZwycPuPOuZ6YusI/6x3Y4u0OxxNPeOBme
|
||||
LXt/kbpU1WNj2MemEnMLDMDB6COF86tJFw/cUF+lW5kBlvWIFWwapCp22h0XtxfE
|
||||
zOaJgcmGkn2egLvmF5S3c/Tq
|
||||
-----END CERTIFICATE REQUEST-----
|
28
cert/server.key
Executable file
28
cert/server.key
Executable file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD6jEVLuVZ4u3gD
|
||||
xHLLY9m3zziVzV7RLwixx+V3lZZnVIcF2fQJQiE3Y6drXlf4Z+pGyQAnoGNYPSHr
|
||||
Zy6pFlptjAb6pjhvR9lH6G0snzYmJoCOF9OSi5SM0Nr5thdPrJeQUFL9bd5cuM13
|
||||
W9aujusXYlGjRneug0y9jMmsQkfLYJoR4qIRL6M0cy1Lut527n72uAiwCxe9RuOX
|
||||
6vo8vpqCGHlqKUkJ903rtx0U2lflwjMQhcOE6ybzeGhgwx4hPH/E53kiCEn/MxLe
|
||||
Khlc8EpDlk6mQo5Kntb5ld6qTJ313abFGEV3oCkI6KyHXVhgTPOjIrGN5BTvcJkI
|
||||
woIFdAbLAgMBAAECggEAZ97zc5tY0CDYdqdg+BVjU6LjUKed4ZdgQWQgK92mRxxy
|
||||
BekAFT6HStFTvV2VlvRt2f7Gw6boyNI/V7qlS5Bq2POuiQeUaf4dhIRFjrRK3LIl
|
||||
pcMXAbBAW8jSn6fwLiXdiATlAxAjmck71XoLnL6/lCg8tDYos/7XN9ZAXdv0q8mE
|
||||
1+nnyS6WfWXBdOqDhgfvzwyIG/dfA9KmXfHw3dFXQL9up9M18d4dQLC7XtYxlL9h
|
||||
ttUG5XvzkwnnKbrNVKKFy1bynl335NrujyAQjWTVU9GmWTZvd11DFWXm0raQ1Q/Y
|
||||
zS+JQ+rtJwldthN2vyCC5Oj5xZSCldVkivLqfpk/8QKBgQD98EDLtr2DGC5GwEEm
|
||||
g+Gne45XocCc683Xq/WpTR45kuSv5rzb9G6zbWRgF9Rn6dYnkFgwlU4WW6bF851V
|
||||
UWGOGoE4DL4F2ecvc/Bnmwp5v1aoDCokS2QEBRDg+Y5I769L1PdQwWVRs85TQv9s
|
||||
9DqAXAwjZAJJlKAqEiLABAkJLwKBgQD8lPieQytn43hWZfm+IA4zNLg4Fuhd9NPz
|
||||
IsYCYFno4F2Ibp7kspm0HTf1u4/pXGCEbDIxgY4EX2KVQfTuEovx0VMlgGVJVsYZ
|
||||
Z0LMmMkDe/xRh9Pta0uI5ApP+kxw/hv7ug+r4jrBdFxacM8913Q24xn1sGPbMxcw
|
||||
t6bF7gK9JQKBgQCAtQISfpfFVz81SwJ6hAOegOhfkDNxFa9qLdVArz6vjb1tTnTQ
|
||||
wy5MsRhcABf/ZyzNNsMMNNC0UlXQJS7MbIgWn2Z5QzQZAR3FFm12BcYkeQ9x82B0
|
||||
ruWNiAfLD5HHc/yUYefMPYmeBJWB7AZMceko3/6i/XCPXM5lJHYKfsEZrwKBgQC2
|
||||
zAGaWDZju2Zt8ONry7s7J95jCYEl7nguMG5pbd3kAezvPk/UZ8WBatwDBblDRE29
|
||||
yKgS2BY15+pq5w8h9V4+piWssF2dVCWOYQMhGznvQZ38S3b/DnXeHluwQg9sfxs1
|
||||
dRS+ar3nIBVuhtIViYZb6sPw4bWDAFi6M0+JU5W72QKBgQD02Z8VHXtdKbzWafK6
|
||||
TOJ3u5ReTL78yF4JLdtLLpnOJ0XFpKbLRzCYrBIuLwJgnTC1VqcnVezlCvcDj8fc
|
||||
KUNQbEAFDMUedA9J4tIsohjZlbW1tbyAo7h160TOUg+15CIMLpI+6Or5ADXeyisv
|
||||
T68iyFB+Qul1Nr/J/8JlPRD6pw==
|
||||
-----END PRIVATE KEY-----
|
566
tls_client.c
Executable file
566
tls_client.c
Executable file
@@ -0,0 +1,566 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
// 初始化OpenSSL
|
||||
void init_openssl() {
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
// 清理OpenSSL
|
||||
void cleanup_openssl() {
|
||||
EVP_cleanup();
|
||||
ERR_free_strings();
|
||||
}
|
||||
|
||||
// 创建SSL上下文
|
||||
SSL_CTX *create_context(int tls_version) {
|
||||
const SSL_METHOD *method;
|
||||
SSL_CTX *ctx;
|
||||
|
||||
method = TLS_client_method();
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (!ctx) {
|
||||
perror("无法创建SSL上下文");
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 设置TLS版本
|
||||
switch (tls_version) {
|
||||
case 1: // TLS 1.2
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
|
||||
break;
|
||||
case 2: // TLS 1.3
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
|
||||
break;
|
||||
case 0: // 自动协商(默认)
|
||||
default:
|
||||
// 不设置版本限制,让OpenSSL自动协商
|
||||
break;
|
||||
}
|
||||
|
||||
// 跳过证书验证(仅用于测试)
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// 时间戳日志函数
|
||||
void log_with_timestamp(const char *format, ...) {
|
||||
time_t now;
|
||||
struct tm *tm_info;
|
||||
char timestamp[64];
|
||||
va_list args;
|
||||
|
||||
time(&now);
|
||||
tm_info = localtime(&now);
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
printf("[%s] ", timestamp);
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// 完整测试函数(包含重试逻辑)
|
||||
int perform_test(struct sockaddr_in *server_addr, SSL_CTX *ssl_ctx, const char *message,
|
||||
int byte_count, int max_retries, int *retry_count_out) {
|
||||
int socket_fd;
|
||||
SSL *ssl = NULL;
|
||||
char buffer[BUFFER_SIZE];
|
||||
int bytes_received;
|
||||
int current_retry = 0;
|
||||
int test_success = 0;
|
||||
|
||||
*retry_count_out = 0;
|
||||
|
||||
log_with_timestamp("进入perform_test函数\n");
|
||||
|
||||
// 重试循环
|
||||
for (current_retry = 0; current_retry <= max_retries; current_retry++) {
|
||||
log_with_timestamp("重试循环: 第 %d/%d 次尝试\n", current_retry, max_retries);
|
||||
|
||||
if (current_retry > 0) {
|
||||
// 指数退避:2^retry_count 秒,最大60秒
|
||||
int backoff_time = (1 << current_retry); // 2的幂:2, 4, 8, 16...
|
||||
if (backoff_time > 60) {
|
||||
backoff_time = 60; // 最大60秒
|
||||
}
|
||||
log_with_timestamp("开始第 %d 次重试,等待%d秒(指数退避)...\n", current_retry, backoff_time);
|
||||
sleep(backoff_time);
|
||||
*retry_count_out = current_retry;
|
||||
}
|
||||
|
||||
// 创建套接字
|
||||
log_with_timestamp("开始创建套接字...\n");
|
||||
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0) {
|
||||
log_with_timestamp("创建套接字失败: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
log_with_timestamp("套接字创建成功,fd=%d\n", socket_fd);
|
||||
|
||||
// 设置连接超时(30秒)
|
||||
struct timeval connect_timeout;
|
||||
connect_timeout.tv_sec = 30;
|
||||
connect_timeout.tv_usec = 0;
|
||||
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) {
|
||||
log_with_timestamp("设置连接超时失败: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
// 连接到服务器
|
||||
log_with_timestamp("开始连接到服务器...\n");
|
||||
if (connect(socket_fd, (struct sockaddr *)server_addr, sizeof(*server_addr)) < 0) {
|
||||
log_with_timestamp("连接失败: %s\n", strerror(errno));
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_with_timestamp("TCP连接成功\n");
|
||||
|
||||
// 设置套接字超时(30秒)
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 30;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
|
||||
log_with_timestamp("设置接收超时失败\n");
|
||||
}
|
||||
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
|
||||
log_with_timestamp("设置发送超时失败\n");
|
||||
}
|
||||
|
||||
// 创建SSL连接
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if (!ssl) {
|
||||
log_with_timestamp("SSL对象创建失败\n");
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
SSL_set_fd(ssl, socket_fd);
|
||||
|
||||
if (SSL_connect(ssl) <= 0) {
|
||||
log_with_timestamp("SSL握手失败\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
SSL_free(ssl);
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_with_timestamp("SSL握手成功,TLS版本: %s\n", SSL_get_version(ssl));
|
||||
|
||||
// 发送消息
|
||||
int write_result = SSL_write(ssl, message, strlen(message));
|
||||
if (write_result <= 0) {
|
||||
int ssl_error = SSL_get_error(ssl, write_result);
|
||||
log_with_timestamp("发送消息失败,SSL错误: %d\n", ssl_error);
|
||||
if (ssl_error == SSL_ERROR_SYSCALL) {
|
||||
log_with_timestamp("系统调用错误: %s\n", strerror(errno));
|
||||
}
|
||||
SSL_free(ssl);
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byte_count > 0) {
|
||||
log_with_timestamp("字节数据已发送 (长度: %zu)\n", strlen(message));
|
||||
} else {
|
||||
log_with_timestamp("消息已发送 (长度: %zu)\n", strlen(message));
|
||||
}
|
||||
|
||||
// 等待一小段时间,确保服务器处理完成
|
||||
usleep(100000); // 等待100ms
|
||||
|
||||
// 接收MD5结果
|
||||
bytes_received = SSL_read(ssl, buffer, BUFFER_SIZE - 1);
|
||||
if (bytes_received <= 0) {
|
||||
int ssl_error = SSL_get_error(ssl, bytes_received);
|
||||
log_with_timestamp("接收MD5结果失败,SSL错误: %d\n", ssl_error);
|
||||
|
||||
// 详细的错误诊断
|
||||
switch (ssl_error) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
log_with_timestamp("服务器关闭连接\n");
|
||||
break;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
log_with_timestamp("SSL需要读取数据(可能超时)\n");
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
log_with_timestamp("SSL需要写入数据\n");
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
log_with_timestamp("接收超时(30秒无响应)\n");
|
||||
} else {
|
||||
log_with_timestamp("系统调用错误: %s\n", strerror(errno));
|
||||
}
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
log_with_timestamp("SSL协议错误\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
break;
|
||||
default:
|
||||
log_with_timestamp("未知SSL错误\n");
|
||||
break;
|
||||
}
|
||||
|
||||
SSL_free(ssl);
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer[bytes_received] = '\0';
|
||||
log_with_timestamp("收到MD5结果: %s\n", buffer);
|
||||
|
||||
// 清理连接
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close(socket_fd);
|
||||
|
||||
log_with_timestamp("连接已关闭\n");
|
||||
|
||||
test_success = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return test_success;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *host = "localhost";
|
||||
int port = 8443;
|
||||
int socket_fd;
|
||||
struct sockaddr_in server_addr;
|
||||
SSL_CTX *ssl_ctx;
|
||||
char message[BUFFER_SIZE];
|
||||
int test_count = 1; // 默认测试1次
|
||||
int test_interval = 1; // 默认间隔1秒
|
||||
int tls_version = 0; // 默认自动协商TLS版本
|
||||
int byte_count = 0; // 字节数参数,0表示使用消息内容
|
||||
int tls_compare_mode = 0; // TLS版本对比测试模式,0=关闭,1=开启
|
||||
int c;
|
||||
|
||||
// 统计变量
|
||||
int success_count = 0; // 成功次数
|
||||
int failure_count = 0; // 失败次数
|
||||
int total_retry_count = 0; // 总重试次数
|
||||
time_t start_time, end_time; // 开始和结束时间
|
||||
|
||||
// 解析命令行参数
|
||||
while ((c = getopt(argc, argv, "m:h:p:c:i:t:s:C")) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
strcpy(message, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
if (port <= 0 || port > 65535) {
|
||||
printf("错误: 端口号必须在1-65535之间\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
test_count = atoi(optarg);
|
||||
if (test_count <= 0) {
|
||||
printf("错误: 测试次数必须大于0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
test_interval = atoi(optarg);
|
||||
if (test_interval < 0) {
|
||||
printf("错误: 测试间隔不能为负数\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (strcmp(optarg, "1.2") == 0) {
|
||||
tls_version = 1; // TLS 1.2
|
||||
} else if (strcmp(optarg, "1.3") == 0) {
|
||||
tls_version = 2; // TLS 1.3
|
||||
} else if (strcmp(optarg, "auto") == 0) {
|
||||
tls_version = 0; // 自动协商
|
||||
} else {
|
||||
printf("错误: 无效的TLS版本 '%s',支持: 1.2, 1.3, auto\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
byte_count = atoi(optarg);
|
||||
if (byte_count <= 0) {
|
||||
printf("错误: 字节数必须大于0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
tls_compare_mode = 1;
|
||||
break;
|
||||
case '?':
|
||||
printf("使用 -h 查看帮助信息\n");
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有通过-m参数指定消息,检查位置参数
|
||||
if (strlen(message) == 0 && optind < argc) {
|
||||
strcpy(message, argv[optind]);
|
||||
}
|
||||
|
||||
// 检查必需参数:必须有消息内容或字节数
|
||||
if (strlen(message) == 0 && byte_count == 0) {
|
||||
printf("用法: %s -m <消息> 或 -s <字节数> [-h 主机] [-p 端口] [-c 次数] [-i 间隔秒数] [-t TLS版本] [-C]\n", argv[0]);
|
||||
printf("选项:\n");
|
||||
printf(" -m 消息 要发送的消息内容\n");
|
||||
printf(" -s 字节数 要发送的字节数(发送01数据)\n");
|
||||
printf(" -h 主机 服务器主机地址 (默认: localhost)\n");
|
||||
printf(" -p 端口 服务器端口 (默认: 8443)\n");
|
||||
printf(" -c 次数 循环测试次数 (默认: 1)\n");
|
||||
printf(" -i 间隔 测试间隔秒数 (默认: 1)\n");
|
||||
printf(" -t 版本 TLS版本: 1.2, 1.3, auto (默认: auto)\n");
|
||||
printf(" -C TLS版本对比模式:先测试TLS1.2,等待2秒后测试TLS1.3\n");
|
||||
printf("\n示例:\n");
|
||||
printf(" %s -m \"Hello\" -c 5 -i 2 # 发送Hello消息5次,间隔2秒\n", argv[0]);
|
||||
printf(" %s -s 1024 # 发送1024字节的01数据\n", argv[0]);
|
||||
printf(" %s -s 2048 -c 3 -i 1 # 发送2048字节01数据,循环3次\n", argv[0]);
|
||||
printf(" %s -m \"Test\" -h 192.168.1.100 -p 9000 # 连接到指定服务器\n", argv[0]);
|
||||
printf(" %s -m \"TLS1.2\" -t 1.2 # 强制使用TLS 1.2\n", argv[0]);
|
||||
printf(" %s -m \"TLS1.3\" -t 1.3 # 强制使用TLS 1.3\n", argv[0]);
|
||||
printf(" %s -m \"Compare\" -C -c 5 # TLS版本对比测试,循环5轮\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_with_timestamp("连接到TLS服务器: %s:%d\n", host, port);
|
||||
|
||||
// 根据参数类型显示不同的信息
|
||||
if (byte_count > 0) {
|
||||
// 检查字节数是否超出缓冲区大小
|
||||
if (byte_count > BUFFER_SIZE - 1) {
|
||||
printf("错误: 字节数 %d 超出最大缓冲区大小 %d\n", byte_count, BUFFER_SIZE - 1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_with_timestamp("发送字节数: %d (01数据)\n", byte_count);
|
||||
// 生成01数据
|
||||
for (int i = 0; i < byte_count; i++) {
|
||||
message[i] = '0' + (i % 2); // 交替发送0和1
|
||||
}
|
||||
message[byte_count] = '\0';
|
||||
} else {
|
||||
log_with_timestamp("发送消息: %s\n", message);
|
||||
}
|
||||
|
||||
log_with_timestamp("测试次数: %d, 间隔: %d秒\n", test_count, test_interval);
|
||||
|
||||
// 显示TLS版本信息
|
||||
const char *tls_version_str;
|
||||
switch (tls_version) {
|
||||
case 1:
|
||||
tls_version_str = "TLS 1.2";
|
||||
break;
|
||||
case 2:
|
||||
tls_version_str = "TLS 1.3";
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
tls_version_str = "自动协商";
|
||||
break;
|
||||
}
|
||||
// 显示测试模式
|
||||
if (tls_compare_mode) {
|
||||
log_with_timestamp("TLS版本对比模式: 每轮先测试TLS 1.2,等待2秒后测试TLS 1.3\n");
|
||||
} else {
|
||||
log_with_timestamp("TLS版本: %s\n", tls_version_str);
|
||||
}
|
||||
|
||||
// 记录开始时间
|
||||
time(&start_time);
|
||||
log_with_timestamp("测试开始时间: %s", ctime(&start_time));
|
||||
|
||||
// 初始化OpenSSL
|
||||
init_openssl();
|
||||
|
||||
// 执行循环测试
|
||||
for (int i = 1; i <= test_count; i++) {
|
||||
if (tls_compare_mode) {
|
||||
// TLS版本对比模式:先测试TLS 1.2,再测试TLS 1.3
|
||||
log_with_timestamp("\n=== 测试轮次 %d/%d ===\n", i, test_count);
|
||||
|
||||
// 测试TLS 1.2
|
||||
log_with_timestamp("\n--- TLS 1.2 测试 ---\n");
|
||||
ssl_ctx = create_context(1); // 1 = TLS 1.2
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) {
|
||||
log_with_timestamp("地址解析失败: %s\n", strerror(errno));
|
||||
failure_count++;
|
||||
} else {
|
||||
int retry_count = 0;
|
||||
if (perform_test(&server_addr, ssl_ctx, message, byte_count, 3, &retry_count)) {
|
||||
log_with_timestamp("TLS 1.2 测试成功\n");
|
||||
success_count++;
|
||||
total_retry_count += retry_count;
|
||||
} else {
|
||||
log_with_timestamp("TLS 1.2 测试失败(已重试 %d 次)\n", retry_count);
|
||||
failure_count++;
|
||||
total_retry_count += retry_count;
|
||||
}
|
||||
}
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
// 等待2秒
|
||||
log_with_timestamp("等待2秒后测试TLS 1.3...\n");
|
||||
sleep(1);
|
||||
|
||||
// 测试TLS 1.3
|
||||
log_with_timestamp("\n--- TLS 1.3 测试 ---\n");
|
||||
ssl_ctx = create_context(2); // 2 = TLS 1.3
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) {
|
||||
log_with_timestamp("地址解析失败: %s\n", strerror(errno));
|
||||
failure_count++;
|
||||
} else {
|
||||
int retry_count = 0;
|
||||
if (perform_test(&server_addr, ssl_ctx, message, byte_count, 3, &retry_count)) {
|
||||
log_with_timestamp("TLS 1.3 测试成功\n");
|
||||
success_count++;
|
||||
total_retry_count += retry_count;
|
||||
} else {
|
||||
log_with_timestamp("TLS 1.3 测试失败(已重试 %d 次)\n", retry_count);
|
||||
failure_count++;
|
||||
total_retry_count += retry_count;
|
||||
}
|
||||
}
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
log_with_timestamp("--- 本轮测试完成 ---\n");
|
||||
} else {
|
||||
// 普通测试模式
|
||||
log_with_timestamp("\n=== 测试 %d/%d ===\n", i, test_count);
|
||||
|
||||
// 创建SSL上下文(只创建一次)
|
||||
if (i == 1) {
|
||||
ssl_ctx = create_context(tls_version);
|
||||
}
|
||||
|
||||
// 设置服务器地址
|
||||
log_with_timestamp("开始设置服务器地址: %s:%d\n", host, port);
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
log_with_timestamp("开始解析IP地址...\n");
|
||||
if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) {
|
||||
log_with_timestamp("地址解析失败: %s\n", strerror(errno));
|
||||
failure_count++;
|
||||
continue;
|
||||
}
|
||||
log_with_timestamp("地址解析成功\n");
|
||||
|
||||
// 执行测试(包含重试逻辑)
|
||||
log_with_timestamp("开始执行测试...\n");
|
||||
int retry_count = 0;
|
||||
if (perform_test(&server_addr, ssl_ctx, message, byte_count, 3, &retry_count)) {
|
||||
log_with_timestamp("测试成功\n");
|
||||
success_count++;
|
||||
total_retry_count += retry_count;
|
||||
} else {
|
||||
log_with_timestamp("测试失败(已重试 %d 次)\n", retry_count);
|
||||
failure_count++;
|
||||
total_retry_count += retry_count;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果不是最后一次测试,等待指定间隔
|
||||
if (i < test_count) {
|
||||
log_with_timestamp("等待 %d 秒后进行下一次测试...\n", test_interval);
|
||||
sleep(test_interval);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理SSL上下文(非对比模式)
|
||||
if (!tls_compare_mode && ssl_ctx) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
// 记录结束时间
|
||||
time(&end_time);
|
||||
|
||||
// 最终清理
|
||||
cleanup_openssl();
|
||||
|
||||
// 计算统计信息
|
||||
int total_tests = success_count + failure_count;
|
||||
double success_rate = (total_tests > 0) ? ((double)success_count / total_tests) * 100.0 : 0.0;
|
||||
double elapsed_time = difftime(end_time, start_time);
|
||||
|
||||
// 输出统计信息
|
||||
log_with_timestamp("\n");
|
||||
log_with_timestamp("========================================\n");
|
||||
log_with_timestamp(" 测试统计报告\n");
|
||||
log_with_timestamp("========================================\n");
|
||||
log_with_timestamp("测试开始时间: %s", ctime(&start_time));
|
||||
log_with_timestamp("测试结束时间: %s", ctime(&end_time));
|
||||
log_with_timestamp("总耗时: %.2f 秒\n", elapsed_time);
|
||||
log_with_timestamp("----------------------------------------\n");
|
||||
log_with_timestamp("总测试次数: %d\n", total_tests);
|
||||
log_with_timestamp("成功次数: %d\n", success_count);
|
||||
log_with_timestamp("失败次数: %d\n", failure_count);
|
||||
log_with_timestamp("总重试次数: %d\n", total_retry_count);
|
||||
log_with_timestamp("成功率: %.2f%%\n", success_rate);
|
||||
log_with_timestamp("----------------------------------------\n");
|
||||
|
||||
if (success_count > 0) {
|
||||
log_with_timestamp("平均每次测试耗时: %.2f 秒\n", elapsed_time / success_count);
|
||||
}
|
||||
|
||||
if (failure_count > 0) {
|
||||
log_with_timestamp("失败率: %.2f%%\n", 100.0 - success_rate);
|
||||
}
|
||||
|
||||
log_with_timestamp("========================================\n");
|
||||
|
||||
// 根据成功率返回适当的退出码
|
||||
if (success_rate >= 100.0) {
|
||||
log_with_timestamp("✅ 所有测试通过!\n");
|
||||
return 0;
|
||||
} else if (success_rate >= 80.0) {
|
||||
log_with_timestamp("⚠️ 大部分测试通过,有少量失败\n");
|
||||
return 1;
|
||||
} else {
|
||||
log_with_timestamp("❌ 测试失败率较高,请检查服务器状态\n");
|
||||
return 2;
|
||||
}
|
||||
}
|
497
tls_server.c
Executable file
497
tls_server.c
Executable file
@@ -0,0 +1,497 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define MAX_CLIENTS 100
|
||||
#define BUFFER_SIZE 4096
|
||||
#define MAX_MESSAGE_SIZE 4096
|
||||
#define CLIENT_TIMEOUT 30 // 客户端超时时间(秒)
|
||||
|
||||
// 全局变量
|
||||
SSL_CTX *ssl_ctx;
|
||||
int server_socket;
|
||||
int client_count = 0;
|
||||
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// IP统计结构体
|
||||
typedef struct {
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
int total_connections;
|
||||
int successful_connections;
|
||||
time_t first_connection_time;
|
||||
time_t last_connection_time;
|
||||
} ip_stats_t;
|
||||
|
||||
ip_stats_t ip_statistics[MAX_CLIENTS];
|
||||
pthread_mutex_t ip_stats_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// 时间戳日志函数
|
||||
void log_with_timestamp(const char *format, ...) {
|
||||
time_t now;
|
||||
struct tm *tm_info;
|
||||
char timestamp[64];
|
||||
va_list args;
|
||||
|
||||
time(&now);
|
||||
tm_info = localtime(&now);
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
printf("[%s] ", timestamp);
|
||||
va_start(args, format);
|
||||
vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// 更新IP统计信息
|
||||
void update_ip_stats(const char *ip, int success) {
|
||||
pthread_mutex_lock(&ip_stats_mutex);
|
||||
|
||||
// 查找或创建IP统计记录
|
||||
int index = -1;
|
||||
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (strlen(ip_statistics[i].ip) == 0) {
|
||||
index = i;
|
||||
strcpy(ip_statistics[i].ip, ip);
|
||||
ip_statistics[i].total_connections = 0;
|
||||
ip_statistics[i].successful_connections = 0;
|
||||
ip_statistics[i].first_connection_time = time(NULL);
|
||||
break;
|
||||
} else if (strcmp(ip_statistics[i].ip, ip) == 0) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
ip_statistics[index].total_connections++;
|
||||
if (success) {
|
||||
ip_statistics[index].successful_connections++;
|
||||
}
|
||||
ip_statistics[index].last_connection_time = time(NULL);
|
||||
|
||||
// 每10次连接统计一次
|
||||
if (ip_statistics[index].total_connections % 10 == 0) {
|
||||
double success_rate = (double)ip_statistics[index].successful_connections /
|
||||
ip_statistics[index].total_connections * 100.0;
|
||||
time_t duration = ip_statistics[index].last_connection_time -
|
||||
ip_statistics[index].first_connection_time;
|
||||
|
||||
log_with_timestamp("IP统计 [%s]: 总次数=%d, 成功次数=%d, 成功率=%.2f%%, 耗时=%ld秒\n",
|
||||
ip, ip_statistics[index].total_connections,
|
||||
ip_statistics[index].successful_connections,
|
||||
success_rate, duration);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&ip_stats_mutex);
|
||||
}
|
||||
|
||||
// 客户端结构体
|
||||
typedef struct {
|
||||
int socket;
|
||||
SSL *ssl;
|
||||
pthread_t thread;
|
||||
int active;
|
||||
} client_t;
|
||||
|
||||
client_t clients[MAX_CLIENTS];
|
||||
|
||||
// 初始化OpenSSL
|
||||
void init_openssl() {
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
// 清理OpenSSL
|
||||
void cleanup_openssl() {
|
||||
EVP_cleanup();
|
||||
ERR_free_strings();
|
||||
}
|
||||
|
||||
// 创建SSL上下文
|
||||
SSL_CTX *create_context() {
|
||||
const SSL_METHOD *method;
|
||||
SSL_CTX *ctx;
|
||||
|
||||
// 使用TLS方法,支持1.2和1.3
|
||||
method = TLS_server_method();
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (!ctx) {
|
||||
perror("无法创建SSL上下文");
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 设置最小TLS版本为1.2
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
||||
|
||||
// 加载证书和私钥(从cert目录加载)
|
||||
if (SSL_CTX_use_certificate_file(ctx, "cert/server.crt", SSL_FILETYPE_PEM) <= 0) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, "cert/server.key", SSL_FILETYPE_PEM) <= 0) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// 计算MD5哈希
|
||||
void calculate_md5(const char *input, char *output) {
|
||||
unsigned char digest[MD5_DIGEST_LENGTH];
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, input, strlen(input));
|
||||
MD5_Final(digest, &ctx);
|
||||
|
||||
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
|
||||
sprintf(output + (i * 2), "%02x", digest[i]);
|
||||
}
|
||||
output[32] = '\0';
|
||||
}
|
||||
|
||||
// 处理客户端连接
|
||||
void *handle_client(void *arg) {
|
||||
client_t *client = (client_t *)arg;
|
||||
char buffer[BUFFER_SIZE];
|
||||
char md5_result[33];
|
||||
int bytes_received;
|
||||
char client_ip[INET_ADDRSTRLEN];
|
||||
int connection_success = 0;
|
||||
|
||||
// 获取客户端IP地址
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t addr_len = sizeof(client_addr);
|
||||
if (getpeername(client->socket, (struct sockaddr*)&client_addr, &addr_len) == 0) {
|
||||
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
|
||||
} else {
|
||||
strcpy(client_ip, "unknown");
|
||||
}
|
||||
|
||||
log_with_timestamp("新客户端连接,IP: %s,线程ID: %lu,套接字: %d\n",
|
||||
client_ip, client->thread, client->socket);
|
||||
|
||||
while (client->active) {
|
||||
// 检查SSL连接状态
|
||||
if (SSL_get_shutdown(client->ssl) & SSL_RECEIVED_SHUTDOWN) {
|
||||
printf("检测到SSL关闭信号\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// 设置套接字超时
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 30; // 30秒超时
|
||||
timeout.tv_usec = 0;
|
||||
setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
|
||||
// 检查连接是否仍然有效
|
||||
if (!client->active || client->socket < 0) {
|
||||
log_with_timestamp("客户端连接已失效,退出处理,IP: %s\n", client_ip);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// 接收客户端消息
|
||||
bytes_received = SSL_read(client->ssl, buffer, MAX_MESSAGE_SIZE);
|
||||
|
||||
if (bytes_received <= 0) {
|
||||
int ssl_error = SSL_get_error(client->ssl, bytes_received);
|
||||
switch (ssl_error) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
log_with_timestamp("客户端正常断开连接,IP: %s\n", client_ip);
|
||||
goto cleanup;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
printf("SSL需要更多数据,继续等待...\n");
|
||||
continue;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
printf("SSL需要写入数据,继续等待...\n");
|
||||
continue;
|
||||
case SSL_ERROR_SSL:
|
||||
// 检查是否是客户端关闭连接导致的错误
|
||||
if (bytes_received == 0) {
|
||||
log_with_timestamp("客户端关闭连接,IP: %s\n", client_ip);
|
||||
} else {
|
||||
log_with_timestamp("SSL协议错误,可能是数据格式问题或连接异常,IP: %s\n", client_ip);
|
||||
ERR_print_errors_fp(stderr);
|
||||
}
|
||||
// 尝试清理SSL错误队列
|
||||
ERR_clear_error();
|
||||
goto cleanup;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
if (bytes_received == 0) {
|
||||
log_with_timestamp("客户端关闭连接(系统调用),IP: %s\n", client_ip);
|
||||
} else {
|
||||
log_with_timestamp("SSL系统调用错误,可能是网络问题,IP: %s\n", client_ip);
|
||||
}
|
||||
// 不要直接break,让连接正常清理
|
||||
goto cleanup;
|
||||
default:
|
||||
log_with_timestamp("SSL读取错误: %d,IP: %s\n", ssl_error, client_ip);
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[bytes_received] = '\0';
|
||||
|
||||
// 检查消息长度
|
||||
if (bytes_received >= MAX_MESSAGE_SIZE - 1) {
|
||||
printf("警告: 消息可能被截断,长度: %d\n", bytes_received);
|
||||
}
|
||||
|
||||
log_with_timestamp("收到客户端消息,IP: %s,长度: %d,内容: %s\n",
|
||||
client_ip, bytes_received, buffer);
|
||||
|
||||
// 计算MD5
|
||||
calculate_md5(buffer, md5_result);
|
||||
log_with_timestamp("MD5计算完成,IP: %s,结果: %s\n", client_ip, md5_result);
|
||||
|
||||
// 发送MD5结果给客户端
|
||||
if (SSL_write(client->ssl, md5_result, strlen(md5_result)) <= 0) {
|
||||
int write_error = SSL_get_error(client->ssl, -1);
|
||||
log_with_timestamp("发送MD5结果失败,SSL错误: %d,IP: %s\n", write_error, client_ip);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
log_with_timestamp("已发送MD5结果给客户端,IP: %s\n", client_ip);
|
||||
connection_success = 1; // 标记连接成功
|
||||
}
|
||||
|
||||
cleanup:
|
||||
// 更新IP统计
|
||||
update_ip_stats(client_ip, connection_success);
|
||||
|
||||
// 清理客户端连接
|
||||
log_with_timestamp("开始清理客户端连接,IP: %s,套接字: %d\n", client_ip, client->socket);
|
||||
|
||||
if (client->ssl) {
|
||||
SSL_shutdown(client->ssl);
|
||||
SSL_free(client->ssl);
|
||||
client->ssl = NULL;
|
||||
}
|
||||
|
||||
if (client->socket >= 0) {
|
||||
close(client->socket);
|
||||
client->socket = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&client_mutex);
|
||||
client->active = 0;
|
||||
client_count--;
|
||||
pthread_mutex_unlock(&client_mutex);
|
||||
|
||||
log_with_timestamp("客户端连接已关闭,IP: %s,当前连接数: %d\n", client_ip, client_count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 信号处理函数
|
||||
void signal_handler(int sig) {
|
||||
printf("\n收到信号 %d,正在关闭服务器...\n", sig);
|
||||
|
||||
// 关闭所有客户端连接
|
||||
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (clients[i].active) {
|
||||
clients[i].active = 0;
|
||||
if (clients[i].ssl) {
|
||||
SSL_shutdown(clients[i].ssl);
|
||||
SSL_free(clients[i].ssl);
|
||||
}
|
||||
if (clients[i].socket > 0) {
|
||||
close(clients[i].socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (server_socket > 0) {
|
||||
close(server_socket);
|
||||
}
|
||||
|
||||
if (ssl_ctx) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
cleanup_openssl();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int port = 8443; // 默认端口
|
||||
struct sockaddr_in server_addr, client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
int opt = 1;
|
||||
int c;
|
||||
|
||||
// 解析命令行参数
|
||||
while ((c = getopt(argc, argv, "p:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
if (port <= 0 || port > 65535) {
|
||||
printf("错误: 端口号必须在1-65535之间\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
printf("用法: %s [-p 端口] [-h]\n", argv[0]);
|
||||
printf("选项:\n");
|
||||
printf(" -p 端口 指定服务器端口 (默认: 8443)\n");
|
||||
printf(" -h 显示此帮助信息\n");
|
||||
printf("\n示例:\n");
|
||||
printf(" %s -p 9000 # 在端口9000运行服务器\n", argv[0]);
|
||||
printf(" %s # 在默认端口8443运行服务器\n", argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
case '?':
|
||||
printf("使用 -h 查看帮助信息\n");
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
log_with_timestamp("启动TLS服务器,端口: %d\n", port);
|
||||
|
||||
// 设置信号处理
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// 初始化OpenSSL
|
||||
init_openssl();
|
||||
|
||||
// 创建SSL上下文
|
||||
ssl_ctx = create_context();
|
||||
|
||||
// 创建服务器套接字
|
||||
server_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_socket < 0) {
|
||||
perror("创建套接字失败");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 设置套接字选项
|
||||
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||
perror("设置套接字选项失败");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 绑定地址
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
perror("绑定地址失败");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 开始监听
|
||||
if (listen(server_socket, 10) < 0) {
|
||||
perror("监听失败");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_with_timestamp("服务器已启动,等待客户端连接...\n");
|
||||
log_with_timestamp("支持TLS 1.2和1.3\n");
|
||||
log_with_timestamp("按Ctrl+C停止服务器\n");
|
||||
|
||||
// 初始化客户端数组
|
||||
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||
clients[i].socket = -1;
|
||||
clients[i].ssl = NULL;
|
||||
clients[i].active = 0;
|
||||
}
|
||||
|
||||
// 主循环:接受客户端连接
|
||||
while (1) {
|
||||
int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
|
||||
if (client_socket < 0) {
|
||||
perror("接受连接失败");
|
||||
continue;
|
||||
}
|
||||
|
||||
log_with_timestamp("新连接来自: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
||||
log_with_timestamp("当前活跃连接数: %d/%d\n", client_count, MAX_CLIENTS);
|
||||
|
||||
// 检查是否达到最大客户端数
|
||||
if (client_count >= MAX_CLIENTS) {
|
||||
log_with_timestamp("已达到最大客户端连接数,拒绝新连接\n");
|
||||
close(client_socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建SSL连接
|
||||
SSL *ssl = SSL_new(ssl_ctx);
|
||||
SSL_set_fd(ssl, client_socket);
|
||||
|
||||
if (SSL_accept(ssl) <= 0) {
|
||||
log_with_timestamp("SSL握手失败,IP: %s\n", inet_ntoa(client_addr.sin_addr));
|
||||
ERR_print_errors_fp(stderr);
|
||||
SSL_free(ssl);
|
||||
close(client_socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_with_timestamp("SSL握手成功,IP: %s,TLS版本: %s\n",
|
||||
inet_ntoa(client_addr.sin_addr), SSL_get_version(ssl));
|
||||
|
||||
// 查找空闲的客户端槽位
|
||||
int client_index = -1;
|
||||
pthread_mutex_lock(&client_mutex);
|
||||
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (!clients[i].active) {
|
||||
client_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (client_index == -1) {
|
||||
log_with_timestamp("没有可用的客户端槽位\n");
|
||||
SSL_free(ssl);
|
||||
close(client_socket);
|
||||
pthread_mutex_unlock(&client_mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 设置客户端信息
|
||||
clients[client_index].socket = client_socket;
|
||||
clients[client_index].ssl = ssl;
|
||||
clients[client_index].active = 1;
|
||||
client_count++;
|
||||
|
||||
// 创建处理线程
|
||||
if (pthread_create(&clients[client_index].thread, NULL, handle_client, &clients[client_index]) != 0) {
|
||||
log_with_timestamp("创建线程失败\n");
|
||||
clients[client_index].active = 0;
|
||||
client_count--;
|
||||
SSL_free(ssl);
|
||||
close(client_socket);
|
||||
pthread_mutex_unlock(&client_mutex);
|
||||
continue;
|
||||
} else {
|
||||
// 分离线程,让系统自动回收线程资源
|
||||
pthread_detach(clients[client_index].thread);
|
||||
log_with_timestamp("客户端处理线程已创建,当前连接数: %d\n", client_count);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&client_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user