211 lines
6.1 KiB
C
211 lines
6.1 KiB
C
#include "../src/client.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
/**
|
|
* 并发测试客户端 - 用于测试多个客户端并发访问服务端
|
|
*
|
|
* 用法:
|
|
* ./test_concurrent_client <log_file_path> <线程数> [延迟ms]
|
|
*
|
|
* 示例:
|
|
* ./test_concurrent_client /tmp/test_error.log 10 # 10个并发线程
|
|
* ./test_concurrent_client /tmp/test_error.log 20 100 # 20个线程,每个延迟100ms
|
|
*/
|
|
|
|
// 线程参数结构
|
|
typedef struct {
|
|
int thread_id;
|
|
const char *log_path;
|
|
int delay_ms; // 启动前延迟(毫秒)
|
|
int success; // 执行结果
|
|
} thread_args_t;
|
|
|
|
// 统计信息
|
|
typedef struct {
|
|
int total;
|
|
int success;
|
|
int failed;
|
|
pthread_mutex_t mutex;
|
|
} stats_t;
|
|
|
|
static stats_t g_stats = {0, 0, 0, PTHREAD_MUTEX_INITIALIZER};
|
|
|
|
/**
|
|
* 工作线程函数
|
|
*/
|
|
void *worker_thread(void *arg) {
|
|
thread_args_t *args = (thread_args_t *)arg;
|
|
|
|
// 如果设置了延迟,先等待
|
|
if (args->delay_ms > 0) {
|
|
usleep(args->delay_ms * 1000);
|
|
}
|
|
|
|
// 模拟不同的命令和参数
|
|
char *commands[] = {
|
|
"/usr/bin/python3",
|
|
"/usr/bin/ls",
|
|
"/bin/bash",
|
|
"/usr/bin/grep"
|
|
};
|
|
|
|
const char *filename = commands[args->thread_id % 4];
|
|
|
|
// 构造测试参数
|
|
char arg1[64], arg2[64];
|
|
snprintf(arg1, sizeof(arg1), "arg1_thread_%d", args->thread_id);
|
|
snprintf(arg2, sizeof(arg2), "arg2_thread_%d", args->thread_id);
|
|
|
|
char *test_argv[] = {
|
|
"command",
|
|
arg1,
|
|
arg2,
|
|
NULL
|
|
};
|
|
|
|
char *test_envp[] = {
|
|
"PATH=/usr/bin:/bin",
|
|
"HOME=/home/test",
|
|
"THREAD_ID=test",
|
|
NULL
|
|
};
|
|
|
|
printf("[线程 %d] 开始发送请求: %s\n", args->thread_id, filename);
|
|
|
|
// 调用客户端函数
|
|
int result = seeking_solutions(filename, test_argv, test_envp, args->log_path, &STDOUT_FILENO);
|
|
|
|
args->success = (result == 0);
|
|
|
|
// 更新统计信息
|
|
pthread_mutex_lock(&g_stats.mutex);
|
|
if (result == 0) {
|
|
g_stats.success++;
|
|
printf("[线程 %d] ✓ 成功\n", args->thread_id);
|
|
} else {
|
|
g_stats.failed++;
|
|
printf("[线程 %d] ✗ 失败 (返回值: %d)\n", args->thread_id, result);
|
|
}
|
|
pthread_mutex_unlock(&g_stats.mutex);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* 打印使用说明
|
|
*/
|
|
void print_usage(const char *prog_name) {
|
|
fprintf(stderr, "用法: %s <log_file_path> <线程数> [延迟ms]\n", prog_name);
|
|
fprintf(stderr, "\n参数说明:\n");
|
|
fprintf(stderr, " log_file_path - 日志文件路径\n");
|
|
fprintf(stderr, " 线程数 - 并发线程数量 (1-1000)\n");
|
|
fprintf(stderr, " 延迟ms - 可选,每个线程启动前的延迟(毫秒)\n");
|
|
fprintf(stderr, "\n示例:\n");
|
|
fprintf(stderr, " %s /tmp/test.log 10 # 10个并发线程\n", prog_name);
|
|
fprintf(stderr, " %s /tmp/test.log 50 100 # 50个线程,每个延迟100ms\n", prog_name);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc < 3) {
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
const char *log_path = argv[1];
|
|
int num_threads = atoi(argv[2]);
|
|
int delay_ms = (argc >= 4) ? atoi(argv[3]) : 0;
|
|
|
|
// 验证参数
|
|
if (num_threads <= 0 || num_threads > 1000) {
|
|
fprintf(stderr, "错误: 线程数必须在 1-1000 之间\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("====== 并发测试开始 ======\n");
|
|
printf("socket地址: %s\n", SOCKET_PATH);
|
|
printf("日志路径: %s\n", log_path);
|
|
printf("并发线程数: %d\n", num_threads);
|
|
printf("启动延迟: %d ms\n", delay_ms);
|
|
printf("========================\n\n");
|
|
|
|
// 分配线程资源
|
|
pthread_t *threads = malloc(sizeof(pthread_t) * num_threads);
|
|
thread_args_t *args = malloc(sizeof(thread_args_t) * num_threads);
|
|
|
|
if (!threads || !args) {
|
|
fprintf(stderr, "错误: 内存分配失败\n");
|
|
free(threads);
|
|
free(args);
|
|
return 1;
|
|
}
|
|
|
|
// 初始化统计信息
|
|
g_stats.total = num_threads;
|
|
g_stats.success = 0;
|
|
g_stats.failed = 0;
|
|
|
|
// 记录开始时间
|
|
struct timespec start_time, end_time;
|
|
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
|
|
|
// 创建并启动所有线程
|
|
printf("创建 %d 个线程...\n\n", num_threads);
|
|
for (int i = 0; i < num_threads; i++) {
|
|
args[i].thread_id = i;
|
|
args[i].log_path = log_path;
|
|
args[i].delay_ms = delay_ms;
|
|
args[i].success = 0;
|
|
|
|
if (pthread_create(&threads[i], NULL, worker_thread, &args[i]) != 0) {
|
|
fprintf(stderr, "错误: 创建线程 %d 失败\n", i);
|
|
// 继续创建其他线程
|
|
}
|
|
}
|
|
|
|
// 等待所有线程完成
|
|
printf("等待所有线程完成...\n\n");
|
|
for (int i = 0; i < num_threads; i++) {
|
|
pthread_join(threads[i], NULL);
|
|
}
|
|
|
|
// 记录结束时间
|
|
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
|
|
|
// 计算耗时
|
|
double elapsed = (end_time.tv_sec - start_time.tv_sec) +
|
|
(end_time.tv_nsec - start_time.tv_nsec) / 1e9;
|
|
|
|
// 打印统计结果
|
|
printf("\n====== 测试结果统计 ======\n");
|
|
printf("总线程数: %d\n", g_stats.total);
|
|
printf("成功: %d (%.1f%%)\n", g_stats.success,
|
|
g_stats.total > 0 ? (g_stats.success * 100.0 / g_stats.total) : 0);
|
|
printf("失败: %d (%.1f%%)\n", g_stats.failed,
|
|
g_stats.total > 0 ? (g_stats.failed * 100.0 / g_stats.total) : 0);
|
|
printf("总耗时: %.3f 秒\n", elapsed);
|
|
printf("平均每个请求: %.3f 毫秒\n",
|
|
g_stats.total > 0 ? (elapsed * 1000 / g_stats.total) : 0);
|
|
printf("吞吐量: %.1f 请求/秒\n",
|
|
elapsed > 0 ? (g_stats.total / elapsed) : 0);
|
|
printf("========================\n");
|
|
|
|
// 清理资源
|
|
free(threads);
|
|
free(args);
|
|
pthread_mutex_destroy(&g_stats.mutex);
|
|
|
|
// 返回结果
|
|
if (g_stats.failed > 0) {
|
|
printf("\n✗ 测试完成,但有 %d 个请求失败\n", g_stats.failed);
|
|
return 1;
|
|
} else {
|
|
printf("\n✓ 所有测试通过!\n");
|
|
return 0;
|
|
}
|
|
}
|