epoll搭配非阻塞IO可以更为高效。
但openssl在搭配非阻塞IO时会使得SSL_read, SSL_accept等函数直接返回,导致无法连接上,而循环SSL_accept又会阻塞程序,因为你也不知道下一个连接是什么时候,故非阻塞IO在搭配openssl时较为艰难
但是在采用epoll后,下一次连接到达时epoll会返回,由此可以确定有客户端想要建立连接,在此为什么不说是建立ssl连接呢?因为ssl连接和tcp不是一个概念,epoll只能确定的是由客户端想要建立tcp连接,如果程序在处理连接时死循环,而恰巧客户端只建立tcp连接而不进行ssl握手,那你的程序会卡死。
所以可以在循环中加入循环次数,如果想要建立连接则会在很多时间内连接上,否则则会由于循环次数耗尽跳出循环。同样的,当有读事件到达时可以循环调用SSL_read。

/*************************************************************************> File Name: epoll_ssl.cc> Author: hsz> Brief:> Created Time: Tue 15 Mar 2022 04:58:28 PM CST************************************************************************/
// utils和log可以去掉,自己编写的库,方便调试
#include <utils/utils.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <map>
#include <log/log.h>#define LOG_TAG "epoll ssl"#define LOCAL_IP "172.25.12.215"
#define LOCAL_PORT 8000#define CA_CERT_FILE        "ca.crt"
#define SERVER_KEY_FILE     "server.key"
#define SERVER_CERT_FILE    "server.crt"int CreateSocket()
{int fd = ::socket(AF_INET, SOCK_STREAM, 0);assert(fd > 0 && "socket error");sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(LOCAL_IP);addr.sin_port = htons(LOCAL_PORT);int flag = 1;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));int code = ::bind(fd, (sockaddr *)&addr, sizeof(addr));assert(code == 0 && "bind error");code = ::listen(fd, 1024);assert(code == 0 && "listen error");timeval tv = {0, 500 * 1000};setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));return fd;
}void catch_signal(int sig)
{LOG_ASSERT(false, "");
}int main(int argc, char **argv)
{signal(SIGSEGV, catch_signal);int srvFd = CreateSocket();SSL_library_init();SSL_load_error_strings();OpenSSL_add_all_algorithms();int efd = epoll_create(1024);assert(efd > 0);printf("epoll fd %d\n", efd);epoll_event events[1024];epoll_event ev;ev.data.fd = srvFd;ev.events = EPOLLET | EPOLLIN;epoll_ctl(efd, EPOLL_CTL_ADD, srvFd, &ev);std::map<int, SSL *> sslMap;SSL_CTX *ctx = nullptr;ctx = SSL_CTX_new(SSLv23_server_method());assert(ctx);// 不校验客户端证书SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);// 加载CA的证书  if(!SSL_CTX_load_verify_locations(ctx, CA_CERT_FILE, nullptr)) {printf("SSL_CTX_load_verify_locations error!\n");return -1;}// 加载自己的证书  if(SSL_CTX_use_certificate_file(ctx, SERVER_CERT_FILE, SSL_FILETYPE_PEM) <= 0) {printf("SSL_CTX_use_certificate_file error!\n");return -1;}// 加载私钥if(SSL_CTX_use_PrivateKey_file(ctx, SERVER_KEY_FILE, SSL_FILETYPE_PEM) <= 0) {printf("SSL_CTX_use_PrivateKey_file error!\n");return -1;}// 判定私钥是否正确  if(!SSL_CTX_check_private_key(ctx)) {printf("SSL_CTX_check_private_key error!\n");return -1;}printf("CA证书、本地证书、私钥均已加载完毕\n");printf("server is listening...\n");static const char *https_response = "HTTP/1.1 200 OK\r\nServer: httpd\r\nContent-Length: %d\r\nConnection: keep-alive\r\n\r\n";while (true) {printf("epoll wait...\n");int nev = epoll_wait(efd, events, sizeof(events) / sizeof(epoll_event), -1);if (nev < 0) {printf("epoll_wait error. [%d,%s]", errno, strerror(errno));break;}for (size_t i = 0; i < nev; ++i) {auto &event = events[i];if (event.data.fd == srvFd) {  // acceptsockaddr_in addr;socklen_t len = sizeof(addr);int cfd = ::accept(srvFd, (sockaddr *)&addr, &len);if (cfd > 0) {printf("accept client %d [%s:%d]\n", cfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));SSL *ssl = SSL_new(ctx);bool isSSLAccept = true;if (ssl == nullptr) {printf("SSL_new error.\n");continue;}// static struct timeval tv = {0, 500 * 1000};// setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));// setsockopt(cfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));int flags = ::fcntl(cfd, F_GETFL, 0);::fcntl(cfd, F_SETFL, flags | O_NONBLOCK);SSL_set_fd(ssl, cfd);int code;int retryTimes = 0;uint64_t begin = Time::SystemTime();// 防止客户端连接了但不进行ssl握手, 单纯的增大循环次数无法解决问题,本地大概循环4000次,chrome连接会循环20000多次while ((code = SSL_accept(ssl)) <= 0 && retryTimes++ < 100) {if (SSL_get_error(ssl, code) != SSL_ERROR_WANT_READ) {printf("ssl accept error. %d\n", SSL_get_error(ssl, code));break;}msleep(1);}uint64_t end = Time::SystemTime();printf("code %d, retry times %d\n", code, retryTimes);if (code != 1) {isSSLAccept = false;::close(cfd);SSL_free(ssl);continue;}printf("ssl accept success. cost %lu ms\n", end - begin);ev.data.fd = cfd;ev.events = EPOLLET | EPOLLIN;epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &ev);sslMap.insert(std::make_pair(cfd, ssl));} else {perror("accept error");}continue;}auto it = sslMap.find(event.data.fd);assert(it != sslMap.end());if (event.events & (EPOLLRDHUP | EPOLLHUP)) {printf("client %d quit!\n", event.data.fd);close(event.data.fd);SSL_shutdown(it->second);SSL_free(it->second);sslMap.erase(it);epoll_ctl(efd, EPOLL_CTL_DEL, event.data.fd, nullptr);continue;}if (event.events & EPOLLIN) {char buf[1024] = {0};int readSize = SSL_read(it->second, buf, sizeof(buf));if (readSize <= 0) {printf("SSL_read error. %d\n", SSL_get_error(it->second, readSize));continue;}printf("read: %d\n%s\n", readSize, buf);char sendBuf[1024] = {0};int fmtSize = sprintf(sendBuf, https_response, readSize);printf("*********************\n%s*********************\n", sendBuf);int writeSize = SSL_write(it->second, sendBuf, strlen(sendBuf));    // 发送响应头printf("format size %d, write size %d\n", fmtSize, writeSize);if (writeSize <= 0) {printf("SSL_write error. %d\n", SSL_get_error(it->second, writeSize));}writeSize = SSL_write(it->second, buf, readSize);   // 发送响应主体if (writeSize <= 0) {printf("SSL_write error. %d\n", SSL_get_error(it->second, writeSize));}printf("format size %d, write size %d\n", fmtSize, writeSize);}}}for (auto it : sslMap) {close(it.first);SSL_free(it.second);}SSL_CTX_free(ctx);close(srvFd);close(efd);return 0;
}

使用chrome进行https连接结果
返回的内容就是chrome的http请求

epoll + 非阻塞IO + openssl相关推荐

  1. epoll非阻塞IO

    设置connfd套接字为非阻塞 flag = fcntl(connfd, F_GETFL); flag |= O_NONBLOCK; fcntl(connfd, F_SETFL, flag); 转载于 ...

  2. unix网络编程 str_cli epoll 非阻塞版本

    unix网络编程 str_cli epoll 非阻塞版本 unix网络编程str_cli使用epoll实现讲了使用epoll配合阻塞io来实现str_cli,这个版本是配合非阻塞io. 可以看到采用非 ...

  3. python3 异步 非阻塞 IO多路复用 select poll epoll 使用

    有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的. 下面记录下分别基于Select/Poll/Epoll的echo ser ...

  4. 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    一.基本概念 我们通俗一点讲: Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写.如果这次没有把数据一次性全部读写完( ...

  5. java epoll select_Java 非阻塞 IO 和异步 IO

    点击上方 Java后端,选择 设为星标 优质文章,及时送达 作者 | HongJie 链接 | javadoop.com/post/nio-and-aio 本文将介绍非阻塞 IO 和异步 IO,也就是 ...

  6. Python异步非阻塞IO多路复用Select/Poll/Epoll使用

    来源:http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理 ...

  7. 为何 epoll 的 ET 模式一定要设置为非阻塞IO

    ET模式下每次write或read需要循环write或read直到返回EAGAIN错误.以读操作为例,这是因为ET模式只在socket描述符状态发生变化时才触发事件,如果不一次把socket内核缓冲区 ...

  8. Redis 笔记(12)— 单线程架构(非阻塞 IO、多路复用)和多个异步线程

    Redis 使用了单线程架构.非阻塞 I/O .多路复用模型来实现高性能的内存数据库服务.Redis 是单线程的.那么为什么说是单线程呢? Redis 在 Reactor 模型内开发了事件处理器,这个 ...

  9. 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...

  10. Linux网络编程 | IO模型 :阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO

    目录 IO模型 阻塞与非阻塞 同步与异步 阻塞IO 非阻塞IO 信号驱动IO 多路复用IO 异步IO IO模型 根据各自的特性不同,IO模型被分为阻塞IO.非阻塞IO.信号驱动IO.异步IO.多路复用 ...

最新文章

  1. Eclipse 使用maven管理发布web项目在tomcat里面只有java文件没有class文件
  2. 【spider】多线程爬虫
  3. 综述 | 万字长文带你入门近几年深度学习的重要研究成果
  4. opengl绘制刻度坐标系_OpenGL中的坐标系-2D
  5. transact-sql_如何使用Transact-SQL创建,配置和删除SQL Server链接服务器
  6. React Native踩坑新建的RN0.64项目无法在xcode 12.5上打开
  7. 串口发送字符控制灯亮灭
  8. MSP430F149;二、TIMEA
  9. 不管发生什么事,最重要的是拥有乐观积极的心态,困难总会过去的。
  10. Springboot 使用restTemplate 进行跨域请求 response reqeust中首字母大写的问题
  11. 适合编程初学者的开源项目:小游戏2048(微信小程序版)
  12. Android如何自定义服务器DynamicMockServer的使用
  13. Unity Shader 简单地挖一个洞
  14. IntelliJ IDEA 下集成SVN
  15. Dell PowerEdge T140服务器安装系统笔记
  16. QGIS 1. qgis的下载和安装(Windows和macOS)
  17. 阿斯汤加瑜伽(Ashtanga Yoga)第一序列学习与实践笔记(八)
  18. 用西班牙语写一篇文章,讨论如何科学地在线教中国的小朋友学习西班牙语
  19. 微星z370安装linux系统,华硕z370主板装win10系统及bios设置(uefi+gpt方式安装)
  20. wifi 荣耀手机usb_【已解决】电脑端Win7通过WIFI无线网络共享操作安卓手机华为荣耀6的文件...

热门文章

  1. socket学习之电脑手机通信
  2. Hierarchical Prosody Modeling for Non-Autoregressive Speech Synthesis
  3. 刷网课被告非法控制计算机信息系统罪,您好,请问一下网上代刷网课叫非法控制计算...
  4. 《黑马》——C++核心编程
  5. Java poi 表格居中
  6. 数仓OLAP基础知识
  7. 2015年最新中国知网CNKI免费账号直接入口
  8. xmind怎样画流程图_【工作流程图】如何用xmind做流程图
  9. nb信号和4g信号_NB-IoT DTU与4G DTU有什么不同之处
  10. Matplotlab可视化学习笔记(二):如何绘制柱状图