openssl 使用非阻塞 bio
序
在项目中需要访问 https 加密的网页,为了保证并发性,需要用到非阻塞的 socket,搜索发现,这种使用场景的相关介绍不是很多,所以这里记录一下使用的过程。
在项目中,所使用的 ssl 库是老牌 sll 库 —— openssl。所使用的 io多路复用 技术是 epoll。
核心流程
整体流程与访问非加密网站类似,不同之处在于有一下几点:
- 在 socket 建立 tcp 连接之后,需要绑定 socket 句柄在 SSL 中
- 读取,发送数据,使用 SSL 库的方法,替代 linux 系统调用
- 关闭连接前,需要先执行 SSL 关闭流程
建立连接
首先,打开 socket 句柄,然后设置必要的属性
int sock_fd = -;
int flags = -;
sock_fd = socket(AF_INET, SOCK_STREAM, );
flags = fcntl(sockfd, F_GETFL, );
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
然后,将句柄加入 epoll 的管理
epoll_event ev;
ev.events = EPOLLIN | EPOLLOUT | EPOLLET
ev.data.ptr = your_ev_info;
epoll_ctl(epfd, EPOLL_CTL_ADD, url_item->sockfd, &ev);
现在,可以开始真正的连接过程了,与普通的 tcp 连接一样,调用 connect 系统调用。在非阻塞 io 中,需要通过 connect 的返回值和 errno 来判断连接状态,采取不同的策略
struct sockaddr_in serv_addr;
if (connect(sock_fd, (sockaddr *) & serv_addr, sizeof (sockaddr)) < ) {
// 没有立刻连接成功,需要判断 errno
if (errno != EINPROGRESS && errno != EINTR) {
// 失败了, 从epoll里面干掉
epoll_ctl(epfd, EPOLL_CTL_DEL, sock_fd, NULL);
}
} else {
// 立刻成功了
prepare_connect_ssl(your_ev_info);
}
如果没有立刻连接成功,在成功后,会触发 epoll,我们需要在 your_ev_info 中,需要保存现在的状态,以便在 epoll_wait 之后,通过状态来决定需要调用的函数。这些属于 epoll 的细节了,在此不展开说。
假设,现在已经连接成功,则开始做 SSL 握手之前的准备工作。
SSL_CTX *ssl_ctx;
SSL *ssl; ssl_ctx = SSL_CTX_new(TLSv1_method());
ssl = SSL_new(url_item->ssl_ctx);
SSL_set_mode(url_item->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); // 绑定 SSL 和 socket 句柄
SSL_set_fd(ssl, sock_fd);
这一步之所以和后面的 SSL 握手过程分开,是因为 SSL 握手在非阻塞io 的情况下,有可能会被调用多次,而这部分只需要一次调用即可。
现在开始 SSL 握手
int ssl_conn_ret = SSL_connect(ssl);
if ( == ssl_conn_ret) {
// 开始和对端交互
} else if (- == ssl_conn_ret) {
// 没有立刻握手成功,需要通过错误码来判断现在的状态
int ssl_conn_err = SSL_get_error(ssl, ssl_conn_ret);
if (SSL_ERROR_WANT_READ == ssl_conn_err ||
SSL_ERROR_WANT_WRITE == ssl_conn_err) {
//需要更多时间来进行握手
}
} else {
// 连接失败了,做必要处理
if ( != ssl_conn_ret) {
SSL_shutdown(ssl);
}
SSL_free(ssl);
SSL_CTX_free(ssl_ctx);
}
在没有立刻握手成功的时候,需要在 epoll 触发后,在次调用此段代码,来继续握手的过程。
至此,建立连接的过程就完成了。
发送与读取数据
由于发送与读取数据都有可能没有完全完成我们所指定的长度,所以需要判断对应返回值,来决定是否继续发送或读取
// 发送数据
int ret = SSL_write(ssl, buf + last_write_pos, buf_len - last_write_pos); // 读取数据
int ret = SSL_read(ssl, buf + last_read_pos, buf_len - last_read_pos);
关闭连接
// 关闭 ssl 连接
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ssl_ctx); // 然后关闭 socket
close(sock_fd);
要点记录
在使用过程中,整体流程是十分顺利的。一个最重要的点是关于 openssl 与 epoll 的边缘触发配合的问题。
当需要使用 epoll 的边缘触发时,一定要注意,SSL_read 最多只会读取一个完整的加密段,所以,当一次可以读取的数据量大于此值时,需要循环调用 SSL_read 直到读取失败为止。否则,就会导致在缓冲区中的数据没有完全读取的情况。
openssl 使用非阻塞 bio的更多相关文章
- 同步与异步,阻塞与非阻塞 bio,nio,aio
BIO.NIO和AIO的区别(简明版) 同步异步,阻塞非阻塞: https://www.zhihu.com/question/19732473 转载请注明原文地址:http://www.cnblo ...
- 【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...
- 【转载】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
原文链接:https://www.cnblogs.com/lixinjie/p/10811219.html 常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使 ...
- 同步/异步/阻塞/非阻塞/BIO/NIO/AIO
转摘自:https://www.cnblogs.com/lixinjie/p/a-post-about-io-clearly.html 常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HT ...
- 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...
- 同步/异步/阻塞/非阻塞/BIO/NIO/AIO各种情况介绍
常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...
- 非阻塞/异步(epoll) openssl
前段时间在自己的异步网络框架handy中添加openssl的支持,当时在网络上搜索了半天也没有找到很好的例子,后来自己慢慢的摸索,耗费不少时间,终于搞定.因此把相关的资料整理一下,并给出简单的例子,让 ...
- JAVA 中BIO,NIO,AIO的理解以及 同步 异步 阻塞 非阻塞
在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步 ...
- IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别参考
参考https://www.cnblogs.com/aspirant/p/6877350.html?utm_source=itdadao&utm_medium=referral IO复用,AI ...
随机推荐
- ikely()与unlikely() 都等同于if, 此处只是做编译优化
ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首先明确: if (likely(value))等价于if (value)if (likely( ...
- Linux企业级开发技术(3)——epoll企业级开发之epoll模型
EPOLL事件有两种模型: Edge Triggered (ET) 边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据. Level Triggered (LT) 水平触发 只要有数据都会触 ...
- ASP.NET网站文件上传下载功能
if (!IsPostBack) { if (Application["RaNum"] == null) { Random ra = new Random(); Applicati ...
- 数学概念——E 期望(经典问题)
E - 期望(经典问题) Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%lld & %llu Submit S ...
- 暴力求解——POJ 1321 棋盘问题
Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...
- [转]浏览器如何和Web服务器通信
http://hi.baidu.com/ywqme/item/b5297014b2e58f4e6826bb74 概述 普通网民打开网页,访问网站,并不需要了解所谓HTTP协议.作为软件工程师,了解一下 ...
- kafka Disks and Filesystem(磁盘和文件系统)
转载请注明来源地址:http://www.cnblogs.com/dongxiao-yang/p/5206631.html We recommend using multiple drives to ...
- 只对safari起作用的css hack
下面的css代码只对safari browser 起作用: .test { width: 200px; height:50px; background-color:red; padding-top: ...
- iOS中@class #import #include 简介
[转载自:http://blog.csdn.net/chengwuli125/article/details/9705315] 一.解析 很多刚开始学习iOS开发的同学可能在看别人的代码 ...
- HDU 4628 多校第三场1008 dp
这题就没什么好说的了.直接枚举2 ^ 16 的状态,用1表示拿这位,0表示不拿,每次判断是否可以这么拿. #include <iostream> #include <cstdio&g ...