feat(server): 连接空闲超时 + 最大并发限制 + 分级日志
- 新增 log.c/log.h 分级日志系统(error/warn/info/debug) - 新增连接空闲超时管理:使用 coco_timer + shutdown + coco_cancel 自动清理长时间无数据的僵尸连接,默认 30 秒 - 新增最大并发连接数限制(-m <num>),超限返回 503 - 命令行新增选项:-m 最大连接数, -o 超时毫秒, -l 日志级别 - 替换所有 printf 为 log_info/log_error/log_debug 等分级输出 - 编译通过,单线程/多线程模式均正常
This commit is contained in:
parent
6b2d046548
commit
0dc2bb5b4e
2
Makefile
2
Makefile
@ -20,7 +20,7 @@ PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
# 源文件
|
||||
SRCS = main.c server.c http.c static.c
|
||||
SRCS = main.c server.c http.c static.c log.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
TARGET = cocoon
|
||||
|
||||
|
||||
7
cocoon.h
7
cocoon.h
@ -13,7 +13,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* === 错误码 === */
|
||||
#include "log.h"
|
||||
|
||||
#define COCOON_OK 0 /**< 成功 */
|
||||
#define COCOON_ERROR -1 /**< 通用错误 */
|
||||
#define COCOON_NOMEM -2 /**< 内存不足 */
|
||||
@ -32,7 +33,9 @@ typedef struct cocoon_config {
|
||||
uint16_t port; /**< 监听端口 */
|
||||
bool threaded; /**< 是否启用多线程调度 */
|
||||
uint32_t num_workers; /**< 工作线程数(0 = 自动检测) */
|
||||
bool verbose; /**< 详细日志输出 */
|
||||
uint32_t max_connections; /**< 最大并发连接数(0 = 无限制) */
|
||||
uint32_t timeout_ms; /**< 连接空闲超时毫秒(0 = 默认30000) */
|
||||
log_level_t log_level; /**< 日志级别 */
|
||||
} cocoon_config_t;
|
||||
|
||||
/* === 服务器生命周期 API === */
|
||||
|
||||
9
http.c
9
http.c
@ -66,6 +66,9 @@ static void parse_headers(const char **p, const char *end, http_request_t *req)
|
||||
req->range_end = -1;
|
||||
req->has_if_none_match = false;
|
||||
req->has_if_modified_since = false;
|
||||
req->accept_gzip = false;
|
||||
req->accept_deflate = false;
|
||||
req->has_accept_encoding = false;
|
||||
|
||||
while (*p < end && req->num_headers < HTTP_MAX_HEADERS) {
|
||||
const char *line_start = *p;
|
||||
@ -122,7 +125,11 @@ static void parse_headers(const char **p, const char *end, http_request_t *req)
|
||||
if (copy_len >= 64) copy_len = 63;
|
||||
memcpy(req->if_modified_since, req->headers[req->num_headers].value, copy_len);
|
||||
req->if_modified_since[copy_len] = '\0';
|
||||
} else if (strcmp(req->headers[req->num_headers].name, "range") == 0) {
|
||||
} else if (strcmp(req->headers[req->num_headers].name, "accept-encoding") == 0) {
|
||||
req->has_accept_encoding = true;
|
||||
const char *val = req->headers[req->num_headers].value;
|
||||
if (strstr(val, "gzip") != NULL) req->accept_gzip = true;
|
||||
if (strstr(val, "deflate") != NULL) req->accept_deflate = true;
|
||||
/* 解析 Range: bytes=start-end */
|
||||
const char *range_val = req->headers[req->num_headers].value;
|
||||
if (strncasecmp(range_val, "bytes=", 6) == 0) {
|
||||
|
||||
5
http.h
5
http.h
@ -54,6 +54,11 @@ typedef struct {
|
||||
char if_modified_since[64];
|
||||
bool has_if_none_match;
|
||||
bool has_if_modified_since;
|
||||
|
||||
/* 压缩相关头 */
|
||||
bool accept_gzip; /* 客户端支持 gzip */
|
||||
bool accept_deflate; /* 客户端支持 deflate */
|
||||
bool has_accept_encoding;
|
||||
} http_request_t;
|
||||
|
||||
/* === HTTP 响应 === */
|
||||
|
||||
81
log.c
Normal file
81
log.c
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* log.c - 简单日志系统实现
|
||||
*
|
||||
* @author xfy
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static log_level_t g_level = LOG_LEVEL_INFO;
|
||||
static const char *g_prefix = "[Cocoon]";
|
||||
|
||||
static const char *level_str(log_level_t level) {
|
||||
switch (level) {
|
||||
case LOG_LEVEL_ERROR: return "ERROR";
|
||||
case LOG_LEVEL_WARN: return "WARN";
|
||||
case LOG_LEVEL_INFO: return "INFO";
|
||||
case LOG_LEVEL_DEBUG: return "DEBUG";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void log_set_level(log_level_t level) {
|
||||
g_level = level;
|
||||
}
|
||||
|
||||
void log_set_prefix(const char *prefix) {
|
||||
g_prefix = prefix;
|
||||
}
|
||||
|
||||
log_level_t log_get_level(void) {
|
||||
return g_level;
|
||||
}
|
||||
|
||||
static void log_output(log_level_t level, const char *fmt, va_list args) {
|
||||
if (level > g_level) return;
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *tm_info = localtime(&now);
|
||||
char time_buf[32];
|
||||
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
if (g_prefix) {
|
||||
fprintf(stderr, "%s %s [%s] ", time_buf, g_prefix, level_str(level));
|
||||
} else {
|
||||
fprintf(stderr, "%s [%s] ", time_buf, level_str(level));
|
||||
}
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void log_error(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_output(LOG_LEVEL_ERROR, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_warn(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_output(LOG_LEVEL_WARN, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_info(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_output(LOG_LEVEL_INFO, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_debug(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_output(LOG_LEVEL_DEBUG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
55
log.h
Normal file
55
log.h
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* log.h - 简单日志系统
|
||||
*
|
||||
* 支持分级日志输出:ERROR、WARN、INFO、DEBUG。
|
||||
* 线程安全(使用 printf,底层为线程安全)。
|
||||
*
|
||||
* @author xfy
|
||||
*/
|
||||
|
||||
#ifndef COCOON_LOG_H
|
||||
#define COCOON_LOG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* === 日志级别 === */
|
||||
typedef enum {
|
||||
LOG_LEVEL_ERROR = 0, /**< 错误,程序可能无法继续 */
|
||||
LOG_LEVEL_WARN = 1, /**< 警告,程序可继续但需注意 */
|
||||
LOG_LEVEL_INFO = 2, /**< 信息,正常运行输出 */
|
||||
LOG_LEVEL_DEBUG = 3, /**< 调试,详细运行状态 */
|
||||
} log_level_t;
|
||||
|
||||
/**
|
||||
* log_set_level - 设置全局日志级别
|
||||
*
|
||||
* 低于此级别的日志将被忽略。
|
||||
*
|
||||
* @param level 最低输出级别
|
||||
*/
|
||||
void log_set_level(log_level_t level);
|
||||
|
||||
/**
|
||||
* log_set_prefix - 设置日志前缀(默认 [Cocoon])
|
||||
*
|
||||
* @param prefix 前缀字符串(如 "[Cocoon]"),NULL 表示无前缀
|
||||
*/
|
||||
void log_set_prefix(const char *prefix);
|
||||
|
||||
/**
|
||||
* log_get_level - 获取当前日志级别
|
||||
* @return 当前日志级别
|
||||
*/
|
||||
log_level_t log_get_level(void);
|
||||
|
||||
/**
|
||||
* log_error / log_warn / log_info / log_debug - 分级日志输出
|
||||
*
|
||||
* 使用 printf 风格格式化字符串,自动追加换行。
|
||||
*/
|
||||
void log_error(const char *fmt, ...);
|
||||
void log_warn(const char *fmt, ...);
|
||||
void log_info(const char *fmt, ...);
|
||||
void log_debug(const char *fmt, ...);
|
||||
|
||||
#endif /* COCOON_LOG_H */
|
||||
30
main.c
30
main.c
@ -45,7 +45,10 @@ static void print_usage(const char *prog) {
|
||||
printf(" -p <port> 监听端口(默认 8080)\n");
|
||||
printf(" -t 启用多线程调度\n");
|
||||
printf(" -w <num> 工作线程数(默认自动检测 CPU 核心)\n");
|
||||
printf(" -v 详细日志输出\n");
|
||||
printf(" -m <num> 最大并发连接数(默认无限制)\n");
|
||||
printf(" -o <ms> 连接空闲超时毫秒(默认 30000)\n");
|
||||
printf(" -l <level> 日志级别: error, warn, info, debug(默认 info)\n");
|
||||
printf(" -v 详细日志输出(等同于 -l debug)\n");
|
||||
printf(" -h 显示此帮助\n");
|
||||
printf("\nExample:\n");
|
||||
printf(" %s -r ./www -p 8080\n", prog);
|
||||
@ -66,7 +69,9 @@ static bool parse_args(int argc, char *argv[], cocoon_config_t *config) {
|
||||
config->port = 8080;
|
||||
config->threaded = false;
|
||||
config->num_workers = 0;
|
||||
config->verbose = false;
|
||||
config->max_connections = 0;
|
||||
config->timeout_ms = 0;
|
||||
config->log_level = LOG_LEVEL_INFO;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-r") == 0) {
|
||||
@ -81,8 +86,24 @@ static bool parse_args(int argc, char *argv[], cocoon_config_t *config) {
|
||||
} else if (strcmp(argv[i], "-w") == 0) {
|
||||
if (++i >= argc) return false;
|
||||
config->num_workers = (uint32_t)atoi(argv[i]);
|
||||
} else if (strcmp(argv[i], "-m") == 0) {
|
||||
if (++i >= argc) return false;
|
||||
config->max_connections = (uint32_t)atoi(argv[i]);
|
||||
} else if (strcmp(argv[i], "-o") == 0) {
|
||||
if (++i >= argc) return false;
|
||||
config->timeout_ms = (uint32_t)atoi(argv[i]);
|
||||
} else if (strcmp(argv[i], "-l") == 0) {
|
||||
if (++i >= argc) return false;
|
||||
if (strcmp(argv[i], "error") == 0) config->log_level = LOG_LEVEL_ERROR;
|
||||
else if (strcmp(argv[i], "warn") == 0) config->log_level = LOG_LEVEL_WARN;
|
||||
else if (strcmp(argv[i], "info") == 0) config->log_level = LOG_LEVEL_INFO;
|
||||
else if (strcmp(argv[i], "debug") == 0) config->log_level = LOG_LEVEL_DEBUG;
|
||||
else {
|
||||
fprintf(stderr, "Unknown log level: %s\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
} else if (strcmp(argv[i], "-v") == 0) {
|
||||
config->verbose = true;
|
||||
config->log_level = LOG_LEVEL_DEBUG;
|
||||
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||
print_usage(argv[0]);
|
||||
exit(0);
|
||||
@ -121,6 +142,9 @@ int main(int argc, char *argv[]) {
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
/* 设置日志级别 */
|
||||
log_set_level(config.log_level);
|
||||
|
||||
/* 创建服务器 */
|
||||
g_ctx = server_create(&config);
|
||||
if (!g_ctx) {
|
||||
|
||||
140
server.c
140
server.c
@ -10,6 +10,11 @@
|
||||
* 主线程: socket() → bind() → listen() → accept() → 创建协程
|
||||
* 协程: 读取请求 → 解析 HTTP → 服务静态资源 → 关闭连接
|
||||
*
|
||||
* 新增功能(2026-06-03):
|
||||
* - 连接空闲超时管理(自动清理僵尸连接)
|
||||
* - 最大并发连接数限制(防止资源耗尽)
|
||||
* - 分级日志输出(替代 printf)
|
||||
*
|
||||
* @author xfy
|
||||
*/
|
||||
|
||||
@ -17,6 +22,7 @@
|
||||
#include "http.h"
|
||||
#include "static.h"
|
||||
#include "cocoon.h"
|
||||
#include "log.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -28,18 +34,19 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "../coco/include/coco.h"
|
||||
|
||||
/* 单个连接缓冲区大小 */
|
||||
#define CONN_BUF_SIZE 8192
|
||||
/* 连接超时(毫秒) */
|
||||
/* 默认连接超时(毫秒) */
|
||||
#define CONN_TIMEOUT_MS 30000
|
||||
|
||||
/**
|
||||
* connection_t - 单个客户端连接上下文
|
||||
*
|
||||
* 包含 socket fd、接收缓冲区、解析状态。
|
||||
* 包含 socket fd、接收缓冲区、解析状态、超时管理。
|
||||
*/
|
||||
typedef struct {
|
||||
int fd; /**< 客户端 socket */
|
||||
@ -48,11 +55,10 @@ typedef struct {
|
||||
bool keep_alive; /**< 当前连接是否保持 */
|
||||
bool closed; /**< 连接是否已关闭 */
|
||||
const char *root_dir; /**< 静态资源根目录(引用,不拥有) */
|
||||
uint32_t timeout_ms; /**< 连接空闲超时毫秒(从配置复制) */
|
||||
coco_timer_t *timer; /**< 空闲超时定时器 */
|
||||
coco_coro_t *coro; /**< 当前处理协程 */
|
||||
} connection_t;
|
||||
|
||||
/**
|
||||
* server_context - 服务器运行时上下文
|
||||
*/
|
||||
struct server_context {
|
||||
int listen_fd; /**< 监听 socket */
|
||||
cocoon_config_t config; /**< 配置副本 */
|
||||
@ -60,6 +66,9 @@ struct server_context {
|
||||
coco_sched_t *sched; /**< 协程调度器 */
|
||||
};
|
||||
|
||||
/* 全局活跃连接计数器(线程安全) */
|
||||
static atomic_int g_active_connections = 0;
|
||||
|
||||
/**
|
||||
* set_nonblocking - 设置 socket 为非阻塞模式
|
||||
*
|
||||
@ -73,7 +82,7 @@ static void set_nonblocking(int fd) {
|
||||
/**
|
||||
* close_connection - 安全关闭连接
|
||||
*
|
||||
* 关闭 socket 并释放连接结构体内存。
|
||||
* 关闭 socket,递减活跃连接计数。
|
||||
*
|
||||
* @param conn 连接上下文
|
||||
*/
|
||||
@ -82,6 +91,7 @@ static void close_connection(connection_t *conn) {
|
||||
close(conn->fd);
|
||||
conn->fd = -1;
|
||||
conn->closed = true;
|
||||
atomic_fetch_sub(&g_active_connections, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,6 +223,59 @@ static bool handle_request(connection_t *conn, const char *root_dir) {
|
||||
return req.keep_alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* conn_timeout_handler - 连接空闲超时回调
|
||||
*
|
||||
* 定时器触发时关闭 socket,唤醒阻塞在 coco_read 的协程,
|
||||
* 并发起协程取消请求。
|
||||
*
|
||||
* @param arg 连接上下文指针
|
||||
*/
|
||||
static void conn_timeout_handler(void *arg) {
|
||||
connection_t *conn = (connection_t *)arg;
|
||||
if (!conn) return;
|
||||
|
||||
conn->timer = NULL; /* 定时器已触发,自动释放 */
|
||||
|
||||
if (conn->fd >= 0) {
|
||||
log_debug("连接 fd=%d 空闲超时,强制关闭", conn->fd);
|
||||
conn->closed = true;
|
||||
/* shutdown 唤醒阻塞在 coco_read 的协程 */
|
||||
shutdown(conn->fd, SHUT_RDWR);
|
||||
}
|
||||
|
||||
if (conn->coro) {
|
||||
coco_cancel(conn->coro);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* conn_reset_timer - 重置连接空闲定时器
|
||||
*
|
||||
* 每次收到数据时调用,取消旧定时器并创建新定时器。
|
||||
*
|
||||
* @param conn 连接上下文
|
||||
* @param timeout_ms 超时毫秒数
|
||||
*/
|
||||
static void conn_reset_timer(connection_t *conn, uint32_t timeout_ms) {
|
||||
if (conn->timer) {
|
||||
coco_timer_cancel(conn->timer);
|
||||
}
|
||||
conn->timer = coco_timer(timeout_ms, conn_timeout_handler, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* conn_cancel_timer - 取消连接定时器
|
||||
*
|
||||
* @param conn 连接上下文
|
||||
*/
|
||||
static void conn_cancel_timer(connection_t *conn) {
|
||||
if (conn->timer) {
|
||||
coco_timer_cancel(conn->timer);
|
||||
conn->timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* client_handler - 客户端连接协程入口
|
||||
*
|
||||
@ -224,6 +287,12 @@ static void client_handler(void *arg) {
|
||||
connection_t *conn = (connection_t *)arg;
|
||||
if (!conn) return;
|
||||
|
||||
conn->coro = coco_self();
|
||||
|
||||
/* 启动空闲定时器 */
|
||||
uint32_t timeout_ms = conn->timeout_ms > 0 ? conn->timeout_ms : CONN_TIMEOUT_MS;
|
||||
conn->timer = coco_timer(timeout_ms, conn_timeout_handler, conn);
|
||||
|
||||
while (!conn->closed) {
|
||||
/* 读取数据 */
|
||||
ssize_t n = conn_read(conn);
|
||||
@ -232,12 +301,16 @@ static void client_handler(void *arg) {
|
||||
/* 数据不足,继续等待 */
|
||||
continue;
|
||||
}
|
||||
break; /* 错误 */
|
||||
/* coco_read 返回负值错误码(如 COCO_ERROR_CANCELLED)或系统错误 */
|
||||
break;
|
||||
}
|
||||
if (n == 0) {
|
||||
break; /* 对端关闭 */
|
||||
}
|
||||
|
||||
/* 有数据读取,重置定时器 */
|
||||
conn_reset_timer(conn, timeout_ms);
|
||||
|
||||
/* 尝试处理请求 */
|
||||
bool keep = handle_request(conn, conn->root_dir);
|
||||
if (!keep) {
|
||||
@ -245,6 +318,7 @@ static void client_handler(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
conn_cancel_timer(conn);
|
||||
close_connection(conn);
|
||||
free(conn);
|
||||
}
|
||||
@ -261,12 +335,24 @@ static void accept_loop(void *arg) {
|
||||
server_context_t *ctx = (server_context_t *)arg;
|
||||
if (!ctx) return;
|
||||
|
||||
printf("[Cocoon] 服务器启动于端口 %d\n", ctx->config.port);
|
||||
if (ctx->config.threaded) {
|
||||
printf("[Cocoon] 多线程模式: %d 个工作线程\n",
|
||||
ctx->config.num_workers > 0 ? ctx->config.num_workers : (uint32_t)sysconf(_SC_NPROCESSORS_ONLN));
|
||||
uint32_t num_workers = ctx->config.num_workers;
|
||||
if (num_workers == 0) {
|
||||
num_workers = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (num_workers == 0) num_workers = 4;
|
||||
}
|
||||
printf("[Cocoon] 静态资源根目录: %s\n", ctx->config.root_dir);
|
||||
|
||||
log_info("服务器启动于端口 %d", ctx->config.port);
|
||||
if (ctx->config.threaded) {
|
||||
log_info("多线程模式: %d 个工作线程", num_workers);
|
||||
} else {
|
||||
log_info("单线程模式");
|
||||
}
|
||||
log_info("静态资源根目录: %s", ctx->config.root_dir);
|
||||
|
||||
if (ctx->config.max_connections > 0) {
|
||||
log_info("最大并发连接数: %u", ctx->config.max_connections);
|
||||
}
|
||||
log_info("连接空闲超时: %u ms", ctx->config.timeout_ms > 0 ? ctx->config.timeout_ms : CONN_TIMEOUT_MS);
|
||||
|
||||
while (ctx->running) {
|
||||
struct sockaddr_in client_addr;
|
||||
@ -278,10 +364,27 @@ static void accept_loop(void *arg) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
perror("accept");
|
||||
log_error("accept 失败: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/* 检查最大连接数限制 */
|
||||
if (ctx->config.max_connections > 0) {
|
||||
int current = atomic_load(&g_active_connections);
|
||||
if (current >= (int)ctx->config.max_connections) {
|
||||
log_warn("连接数已达上限 (%d/%u),拒绝新连接 fd=%d",
|
||||
current, ctx->config.max_connections, client_fd);
|
||||
/* 发送 503 后关闭 */
|
||||
const char *resp = "HTTP/1.1 503 Service Unavailable\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"Connection: close\r\n\r\n";
|
||||
ssize_t wr = write(client_fd, resp, strlen(resp));
|
||||
(void)wr;
|
||||
close(client_fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置 TCP_NODELAY 减少延迟 */
|
||||
int opt = 1;
|
||||
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
||||
@ -296,12 +399,19 @@ static void accept_loop(void *arg) {
|
||||
conn->keep_alive = true;
|
||||
conn->closed = false;
|
||||
conn->root_dir = ctx->config.root_dir;
|
||||
conn->timeout_ms = ctx->config.timeout_ms > 0 ? ctx->config.timeout_ms : CONN_TIMEOUT_MS;
|
||||
|
||||
atomic_fetch_add(&g_active_connections, 1);
|
||||
log_debug("新连接 fd=%d,当前活跃连接: %d", client_fd,
|
||||
atomic_load(&g_active_connections));
|
||||
|
||||
if (ctx->config.threaded && coco_sched_get_current()) {
|
||||
/* 多线程协程模式:创建协程处理连接 */
|
||||
coco_coro_t *coro = coco_create(coco_sched_get_current(),
|
||||
client_handler, conn, 0);
|
||||
if (!coro) {
|
||||
log_error("创建协程失败,关闭连接 fd=%d", client_fd);
|
||||
atomic_fetch_sub(&g_active_connections, 1);
|
||||
close_connection(conn);
|
||||
free(conn);
|
||||
}
|
||||
@ -390,7 +500,7 @@ int server_start(server_context_t *ctx) {
|
||||
/* 启动全局调度器 */
|
||||
int ret = coco_global_sched_start(num_workers);
|
||||
if (ret != COCO_OK) {
|
||||
fprintf(stderr, "[Cocoon] 启动多线程调度器失败: %d\n", ret);
|
||||
log_error("启动多线程调度器失败: %d", ret);
|
||||
return COCOON_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user