ZBLOG

上面代码加上基于事件驱动实现

要将上述HTTPS服务器代码修改为基于事件驱动的实现,我们可以使用select()epoll()等I/O多路复用技术。以下是一个使用select()的简单示例:

1. 修改后的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PORT 443
#define BACKLOG 10
#define MAX_CLIENTS 100

void handle_client(SSL *ssl) {
    const char *response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello from HTTPS Server!";
    if (SSL_write(ssl, response, strlen(response)) <= 0) {
        ERR_print_errors_fp(stderr);
    }
}

int main() {
    SSL_CTX *ctx;
    
    // 初始化OpenSSL库
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    
    // 创建并初始化上下文
    ctx = SSL_CTX_new(TLS_server_method());
    
    // 加载证书和私钥
    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);
    }
    
    // 创建socket并绑定到指定端口
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    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(PORT);

   bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
   
   listen(server_fd, BACKLOG);
   
   printf("Server started on port %d...\n", PORT);

   fd_set read_fds;
   int max_sd;
   int client_sockets[MAX_CLIENTS] = {0}; // 存储客户端套接字

   while (1) {
       FD_ZERO(&read_fds);
       FD_SET(server_fd, &read_fds); // 将服务器套接字添加到集合
       max_sd = server_fd;

       for (int i = 0; i < MAX_CLIENTS; i++) {
           int sd = client_sockets[i];
           if (sd > 0)
               FD_SET(sd, &read_fds); // 添加活动的客户端套接字
           if (sd > max_sd)
               max_sd = sd; // 更新最大套接字值
       }

       // 等待活动连接
       if (select(max_sd + 1, &read_fds, NULL, NULL, NULL) < 0) {
           perror("select error");
           break;
       }

       // 检查是否有新的客户端连接请求
       if (FD_ISSET(server_fd, &read_fds)) {
           struct sockaddr_in client_addr;
           socklen_t addr_len = sizeof(client_addr);
           
           int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len);
           
           if (client_fd >= 0) {
               printf("New connection: socket fd is %d\n", client_fd);

               for (int i = 0; i < MAX_CLIENTS; i++) {
                   if (client_sockets[i] == 0) { 
                       client_sockets[i] = client_fd; 
                       break; 
                   }
               }

               // 为每个客户端创建SSL连接
               SSL *ssl = SSL_new(ctx);
               SSL_set_fd(ssl, client_fd);

               if (SSL_accept(ssl) <= 0) {
                   ERR_print_errors_fp(stderr); 
                   close(client_fd);
                   continue;
               } else {
                   handle_client(ssl); // Handle the client request
               }

               // 清理和关闭连接
               SSL_shutdown(ssl);
               SSL_free(ssl);
               
               close(client_fd);
               
            }
         }

         for(int i=0; i<MAX_CLIENTS; i++){
             int sd=client_sockets[i];

             if(FD_ISSET(sd,&read_fds)){
                 char buffer[1024];
                 memset(buffer,'\0',sizeof(buffer));

                 int val_read=recv(sd , buffer , sizeof(buffer)-1 , MSG_DONTWAIT );

                 if(val_read==0){
                     getpeername(sd , (struct sockaddr*)&client_addr , \
                         &addr_len );
                     printf("Host disconnected , ip %s , port %d \n" ,
                         inet_ntoa(client_addr.sin_addr) , ntohs(client_addr.sin_port));
                     
                     close(sd);
                     client_sockets[i]=0;
                 }
             }
         }
     }

     close(server_fd);
     SSL_CTX_free(ctx);

     return EXIT_SUCCESS;
}

修改说明

  • 事件驱动:我们使用了 select() 函数来处理多个客户端连接。主循环中会监控所有活跃的客户端套接字,并在有新的数据可读时进行处理。

  • FD_SET 和 FD_ISSET:这些宏用于将文件描述符添加到集合以及检查集合中的文件描述符。

  • 动态管理客户端:通过 client_sockets 数组动态管理多个客户端的套接字,以便能够同时处理多个连接。

编译与运行

确保在同一目录下存在自签名证书和私钥,然后编译并运行服务器:

gcc https_server.c -o https_server -lssl -lcrypto
sudo ./https_server 

测试服务器

使用浏览器或命令行工具(如curl)访问你的服务器,记得加上 -k 参数以忽略自签名证书警告:

curl -k https://localhost/

这样,你就完成了一个基于C语言实现的事件驱动HTTPS服务器。这种设计允许高效地处理多个并发连接。根据需要,可以进一步扩展以增加更多功能,如更复杂的请求处理、日志记录等。


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=5979

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?