要将您的 HTTPS 服务器代码改为基于 epoll
的事件驱动模型,我们需要对代码进行一些重要的修改。这包括使用 epoll_create
来创建一个 epoll 实例,使用 epoll_ctl
注册文件描述符,以及在事件循环中处理可读和可写事件。
下面是经过修改后的代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/epoll.h>
#define PORT 443 // HTTPS 默认端口
#define MAX_EVENTS 10
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl() {
EVP_cleanup();
}
SSL_CTX *create_context() {
const SSL_METHOD *method;
SSL_CTX *ctx;
method = TLS_server_method(); // 使用更安全的 TLS 服务端方法
ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
void configure_context(SSL_CTX *ctx) {
// 加载证书和私钥,这里需要指定你的证书和私钥文件路径
if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0 ||
SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
int main() {
int sockfd;
struct sockaddr_in addr;
init_openssl();
SSL_CTX *ctx = create_context();
configure_context(ctx);
// 创建 Socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置服务器地址
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
// 绑定 Socket
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 开始监听连接
if (listen(sockfd, SOMAXCONN) != 0) {
perror("listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Listening on port %d...\n", PORT);
int epfd = epoll_create1(0); // 创建 epoll 实例
struct epoll_event event;
event.events = EPOLLIN; // 感兴趣的事件为可读事件
event.data.fd = sockfd; // 将服务器套接字添加到 epoll 实例中
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
while (1) {
struct epoll_event events[MAX_EVENTS];
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int n = 0; n < nfds; n++) {
if (events[n].data.fd == sockfd) {
// 新连接到达
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock = accept(sockfd, (struct sockaddr*)&client_addr, &client_len);
if (client_sock >= 0) {
event.events = EPOLLIN | EPOLLET; // 使用边缘触发模式来处理客户端套接字的可读事件
event.data.fd = client_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &event);
} else {
perror("accept failed");
}
} else {
// 客户端发送数据到达,可以进行读操作
int client_sock = events[n].data.fd;
SSL *ssl = SSL_new(ctx);
if (!ssl || !SSL_set_fd(ssl, client_sock)) {
perror("SSL setup failed");
close(client_sock);
continue;
}
// 建立安全连接并读取数据
if (SSL_accept(ssl)<=0){
ERR_print_errors_fp(stderr);
SSL_free(ssl);
close(client_sock);
} else {
char buf[1024];
memset(buf, 0, sizeof(buf));
int bytes = SSL_read(ssl, buf, sizeof(buf));
printf("Received: %s\n", buf);
const char* response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello World!";
SSL_write(ssl,response,sizeof(response));
}
// 清理工作,包括关闭套接字等操作
SSL_shutdown(ssl);
SSL_free(ssl);
close(client_sock);
}
}
}
close(sockfd);
cleanup_openssl();
return EXIT_SUCCESS;
}
更改说明:
- 引入了
<sys/epoll.h>
:用于实现基于epoll
的 I/O 多路复用。 - 创建了
epoll
实例:通过调用epoll_create1()
。 - 注册监听套接字:使用
epoll_ctl()
将监听套接字添加到epoller
中,以便它能够检测新的连接。 - 在主循环中等待事件:使用
epoll_wait()
检测可用事件。 - 处理新连接或客户端数据:根据就绪的文件描述符决定是接受新连接还是读取已连接客户的数据。
这个代码只是一个基本框架,实际应用中可能还需要添加更多错误处理、异步操作以及优化细节。
内容由零声教学AI助手提供,问题来源于学员提问