#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#define SERVER_PORT 8000
#define MAX_EVENTS 1024
#define BUFFER_SIZE 4096
#define THREAD_COUNT 4
#define QUEUE_SIZE 10000
// 任务结构体:传递给工作线程
typedef struct {
int client_fd;
struct sockaddr_in client_addr;
} task_t;
// 任务队列(线程安全)
task_t task_queue[QUEUE_SIZE];
int queue_head = 0;
int queue_tail = 0;
int queue_count = 0;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queue_not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t queue_not_full = PTHREAD_COND_INITIALIZER;
volatile int running = 1; // 控制主循环
// 设置非阻塞
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
// 信号处理:优雅关闭
void signal_handler(int sig) {
printf("\n Received signal %d, shutting down...\n", sig);
running = 0;
}
// 工作线程:从队列取任务处理
void* worker_routine(void* arg) {
char buffer[BUFFER_SIZE];
while (running) {
task_t task;
pthread_mutex_lock(&queue_mutex);
while (queue_count == 0 && running) {
pthread_cond_wait(&queue_not_empty, &queue_mutex);
}
if (!running) {
pthread_mutex_unlock(&queue_mutex);
break;
}
task = task_queue[queue_head];
queue_head = (queue_head + 1) % QUEUE_SIZE;
queue_count--;
pthread_cond_signal(&queue_not_full);
pthread_mutex_unlock(&queue_mutex);
// 处理该客户端
handle_connection(task.client_fd, &task.client_addr);
}
return NULL;
}
// 处理单个客户端连接(在工作线程中执行)
void handle_connection(int client_fd, struct sockaddr_in* client_addr) {
ssize_t n;
char buffer[BUFFER_SIZE];
while ((n = recv(client_fd, buffer, sizeof(buffer), 0)) > 0) {
buffer[n] = '\0';
printf("[Worker %ld] Received %zd bytes from %s:%d: %s",
pthread_self(), n, inet_ntoa(client_addr->sin_addr),
ntohs(client_addr->sin_port), buffer);
// 回显
if (send(client_fd, buffer, n, 0) < 0) {
perror("send failed");
break;
}
printf("[Worker %ld] Sent %zd bytes back\n", pthread_self(), n);
}
if (n == 0) {
printf("Client %s:%d (fd=%d) disconnected.\n",
inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);
} else if (n < 0) {
if (errno != ECONNRESET) {
perror("recv error");
}
}
close(client_fd);
printf("Closed client fd %d\n", client_fd);
}
int main() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
int server_fd, epoll_fd;
struct epoll_event ev, events[MAX_EVENTS];
// 创建监听 socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置端口复用
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定
struct sockaddr_in server_addr;
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(SERVER_PORT);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
if (listen(server_fd, SOMAXCONN) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
set_nonblocking(server_fd);
// 创建 epoll 实例
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) {
perror("epoll_create1 failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 将监听 socket 加入 epoll(ET 模式)
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
perror("epoll_ctl add server_fd failed");
close(server_fd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
// 创建线程池
pthread_t workers[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_create(&workers[i], NULL, worker_routine, NULL);
}
printf("Multi-threaded epoll Server started on port %d\n", SERVER_PORT);
printf("%d worker threads running...\n", THREAD_COUNT);
// 主事件循环
while (running) {
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 1000); // 1s 超时,响应信号
if (nfds < 0 && errno == EINTR) continue;
if (nfds < 0) {
perror("epoll_wait failed");
break;
}
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == server_fd) {
// 新连接到来
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd;
// ET 模式:必须循环 accept
while ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) > 0) {
set_nonblocking(client_fd);
printf("Accepted client: %s:%d (fd=%d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);
// 创建任务
task_t task;
task.client_fd = client_fd;
memcpy(&task.client_addr, &client_addr, sizeof(client_addr));
// 加入任务队列
pthread_mutex_lock(&queue_mutex);
while (queue_count >= QUEUE_SIZE && running) {
pthread_cond_wait(&queue_not_full, &queue_mutex);
}
if (!running) {
pthread_mutex_unlock(&queue_mutex);
close(client_fd);
break;
}
task_queue[queue_tail] = task;
queue_tail = (queue_tail + 1) % QUEUE_SIZE;
queue_count++;
pthread_cond_signal(&queue_not_empty);
pthread_mutex_unlock(&queue_mutex);
}
if (errno != EAGAIN) {
perror("accept error");
}
}
}
}
printf("\nShutting down server...\n");
// 等待所有线程结束
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_join(workers[i], NULL);
}
close(server_fd);
close(epoll_fd);
printf("Server stopped.\n");
return 0;
}
// filename: epoll_client_heartbeat.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8000
#define BUFFER_SIZE 4096
#define EPOLL_EVENTS 10
#define CONNECT_TIMEOUT 5 // 连接超时(秒)
#define HEARTBEAT_INTERVAL 10 // 心跳间隔(秒)
#define HEARTBEAT_TIMEOUT 3 // 最多允许失败次数
// 心跳消息
#define PING_MSG "PING\n"
#define PONG_MSG "PONG\n"
#define PING_LEN 5
#define PONG_LEN 5
// 客户端状态
typedef enum {
CONNECTING,
CONNECTED,
DISCONNECTED
} client_state_t;
// 全局状态
client_state_t state = CONNECTING;
time_t last_heartbeat; // 最后一次成功通信时间
time_t next_heartbeat; // 下次应发送心跳时间
int heartbeat_failed_count = 0; // 心跳失败次数
// 设置非阻塞
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
// 创建非阻塞 socket 并发起连接
int connect_nonblocking(const char* ip, int port) {
int sockfd;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
return -1;
}
set_nonblocking(sockfd);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0) {
fprintf(stderr, "invalid address: %s\n", ip);
close(sockfd);
return -1;
}
int result = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (result < 0) {
if (errno != EINPROGRESS) {
perror("connect failed");
close(sockfd);
return -1;
}
}
return sockfd;
}
// 发送心跳包
void send_heartbeat(int sockfd) {
if (send(sockfd, PING_MSG, PING_LEN, 0) == PING_LEN) {
printf("Sent heartbeat: PING\n");
} else {
perror("Failed to send heartbeat");
}
}
// 处理接收的数据
void handle_read(int sockfd, char* buffer, ssize_t n) {
if (n <= 0) return;
buffer[n] = '\0';
printf("Received: %s", buffer);
// 更新最后通信时间
last_heartbeat = time(NULL);
// 检查是否是 PONG 响应
if (n == PONG_LEN && strncmp(buffer, PONG_MSG, PONG_LEN) == 0) {
printf("Heartbeat response received: PONG\n");
heartbeat_failed_count = 0; // 重置失败计数
}
// 其他业务消息...
}
// 检查心跳是否超时
int check_heartbeat_timeout(int sockfd) {
time_t now = time(NULL);
// 如果长时间无通信(收或发),发送心跳
if (state == CONNECTED && now >= next_heartbeat) {
send_heartbeat(sockfd);
next_heartbeat = now + HEARTBEAT_INTERVAL;
// 注意:我们不立即增加 failed_count,等下次检查时看是否收到 PONG
}
// 如果太久没收到任何响应(包括 PONG)
if (state == CONNECTED && now - last_heartbeat > HEARTBEAT_INTERVAL * (HEARTBEAT_TIMEOUT + 1)) {
heartbeat_failed_count = HEARTBEAT_TIMEOUT + 1;
}
if (heartbeat_failed_count >= HEARTBEAT_TIMEOUT) {
fprintf(stderr, "Heartbeat failed %d times, connection lost.\n", heartbeat_failed_count);
return -1; // 触发断开
}
return 0;
}
int main() {
int sockfd, epoll_fd;
struct epoll_event ev, events[EPOLL_EVENTS];
char buffer[BUFFER_SIZE];
ssize_t n;
int done = 0;
time_t connect_start;
printf("Connecting to %s:%d...\n", SERVER_IP, SERVER_PORT);
sockfd = connect_nonblocking(SERVER_IP, SERVER_PORT);
if (sockfd < 0) {
exit(EXIT_FAILURE);
}
epoll_fd = epoll_create1(0);
if (epoll_fd < 0) {
perror("epoll_create1 failed");
close(sockfd);
exit(EXIT_FAILURE);
}
ev.events = EPOLLOUT | EPOLLIN | EPOLLET;
ev.data.fd = sockfd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {
perror("epoll_ctl add failed");
close(sockfd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
// 初始化时间
connect_start = time(NULL);
last_heartbeat = time(NULL); // 刚连接,视为有通信
next_heartbeat = last_heartbeat + HEARTBEAT_INTERVAL;
while (!done) {
// 计算 epoll_wait 超时时间(最多等待 1 秒,以便及时检查心跳)
int timeout_ms = 1000;
int nfds = epoll_wait(epoll_fd, events, EPOLL_EVENTS, timeout_ms);
if (nfds < 0) {
if (errno == EINTR) continue;
perror("epoll_wait failed");
break;
}
time_t now = time(NULL);
// 处理事件
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == sockfd) {
uint32_t events_mask = events[i].events;
// 连接完成
if (state == CONNECTING && (events_mask & EPOLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0 ||
error != 0) {
errno = error ? error : errno;
perror("connection failed");
done = 1;
break;
}
state = CONNECTED;
printf("Connected to server\n");
// 修改监听事件
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sockfd;
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sockfd, &ev);
// 发送欢迎消息
const char* hello = "Hello from client with heartbeat!\n";
send(sockfd, hello, strlen(hello), 0);
printf("Sent: %s", hello);
}
// 接收数据
if (events_mask & EPOLLIN) {
while ((n = recv(sockfd, buffer, sizeof(buffer)-1, 0)) > 0) {
handle_read(sockfd, buffer, n);
}
if (n == 0) {
printf("Server disconnected.\n");
done = 1;
} else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("recv error");
done = 1;
}
}
if (events_mask & (EPOLLERR | EPOLLHUP)) {
fprintf(stderr, "Socket error or hangup.\n");
done = 1;
}
}
}
// 超时检查:连接阶段
if (state == CONNECTING && now - connect_start > CONNECT_TIMEOUT) {
fprintf(stderr, "Connection timeout\n");
done = 1;
}
// 检查心跳
if (state == CONNECTED) {
if (check_heartbeat_timeout(sockfd) < 0) {
done = 1;
}
}
}
printf("Closing connection...\n");
close(sockfd);
close(epoll_fd);
printf("Client exited.\n");
return 0;
}