在项目中需要访问 https 加密的网页,为了保证并发性,需要用到非阻塞的 socket,搜索发现,这种使用场景的相关介绍不是很多,所以这里记录一下使用的过程。

在项目中,所使用的 ssl 库是老牌 sll 库 —— openssl。所使用的 io多路复用 技术是 epoll。

核心流程

整体流程与访问非加密网站类似,不同之处在于有一下几点:

  1. 在 socket 建立 tcp 连接之后,需要绑定 socket 句柄在 SSL 中
  2. 读取,发送数据,使用 SSL 库的方法,替代 linux 系统调用
  3. 关闭连接前,需要先执行 SSL 关闭流程

建立连接

首先,打开 socket 句柄,然后设置必要的属性

  1. int sock_fd = -;
  2. int flags = -;
  3. sock_fd = socket(AF_INET, SOCK_STREAM, );
  4. flags = fcntl(sockfd, F_GETFL, );
  5. fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

然后,将句柄加入 epoll 的管理

  1. epoll_event ev;
  2. ev.events = EPOLLIN | EPOLLOUT | EPOLLET
  3. ev.data.ptr = your_ev_info;
  4. epoll_ctl(epfd, EPOLL_CTL_ADD, url_item->sockfd, &ev);

现在,可以开始真正的连接过程了,与普通的 tcp 连接一样,调用 connect 系统调用。在非阻塞 io 中,需要通过 connect 的返回值和 errno 来判断连接状态,采取不同的策略

  1. struct sockaddr_in serv_addr;
  2.  
  3. if (connect(sock_fd, (sockaddr *) & serv_addr, sizeof (sockaddr)) < ) {
  4. // 没有立刻连接成功,需要判断 errno
  5. if (errno != EINPROGRESS && errno != EINTR) {
  6. // 失败了, 从epoll里面干掉
  7. epoll_ctl(epfd, EPOLL_CTL_DEL, sock_fd, NULL);
  8. }
  9. } else {
  10. // 立刻成功了
  11. prepare_connect_ssl(your_ev_info);
  12. }

如果没有立刻连接成功,在成功后,会触发 epoll,我们需要在 your_ev_info 中,需要保存现在的状态,以便在  epoll_wait 之后,通过状态来决定需要调用的函数。这些属于 epoll 的细节了,在此不展开说。

假设,现在已经连接成功,则开始做 SSL 握手之前的准备工作。

  1. SSL_CTX *ssl_ctx;
  2. SSL *ssl;
  3.  
  4. ssl_ctx = SSL_CTX_new(TLSv1_method());
  5. ssl = SSL_new(url_item->ssl_ctx);
  6. SSL_set_mode(url_item->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
  7.  
  8. // 绑定 SSL 和 socket 句柄
  9. SSL_set_fd(ssl, sock_fd);

这一步之所以和后面的 SSL 握手过程分开,是因为 SSL 握手在非阻塞io 的情况下,有可能会被调用多次,而这部分只需要一次调用即可。

现在开始 SSL 握手

  1. int ssl_conn_ret = SSL_connect(ssl);
  2. if ( == ssl_conn_ret) {
  3. // 开始和对端交互
  4. } else if (- == ssl_conn_ret) {
  5. // 没有立刻握手成功,需要通过错误码来判断现在的状态
  6. int ssl_conn_err = SSL_get_error(ssl, ssl_conn_ret);
  7. if (SSL_ERROR_WANT_READ == ssl_conn_err ||
  8. SSL_ERROR_WANT_WRITE == ssl_conn_err) {
  9. //需要更多时间来进行握手
  10. }
  11. } else {
  12. // 连接失败了,做必要处理
  13. if ( != ssl_conn_ret) {
  14. SSL_shutdown(ssl);
  15. }
  16. SSL_free(ssl);
  17. SSL_CTX_free(ssl_ctx);
  18. }

在没有立刻握手成功的时候,需要在 epoll 触发后,在次调用此段代码,来继续握手的过程。

至此,建立连接的过程就完成了。

发送与读取数据

由于发送与读取数据都有可能没有完全完成我们所指定的长度,所以需要判断对应返回值,来决定是否继续发送或读取

  1. // 发送数据
  2. int ret = SSL_write(ssl, buf + last_write_pos, buf_len - last_write_pos);
  3.  
  4. // 读取数据
  5. int ret = SSL_read(ssl, buf + last_read_pos, buf_len - last_read_pos);

关闭连接

  1. // 关闭 ssl 连接
  2. SSL_shutdown(ssl);
  3. SSL_free(ssl);
  4. SSL_CTX_free(ssl_ctx);
  5.  
  6. // 然后关闭 socket
  7. close(sock_fd);

要点记录

在使用过程中,整体流程是十分顺利的。一个最重要的点是关于 openssl 与 epoll 的边缘触发配合的问题。

当需要使用 epoll 的边缘触发时,一定要注意,SSL_read 最多只会读取一个完整的加密段,所以,当一次可以读取的数据量大于此值时,需要循环调用 SSL_read 直到读取失败为止。否则,就会导致在缓冲区中的数据没有完全读取的情况。

openssl 使用非阻塞 bio的更多相关文章

  1. 同步与异步,阻塞与非阻塞 bio,nio,aio

    BIO.NIO和AIO的区别(简明版) 同步异步,阻塞非阻塞: https://www.zhihu.com/question/19732473   转载请注明原文地址:http://www.cnblo ...

  2. 【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

    常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...

  3. 【转载】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

    原文链接:https://www.cnblogs.com/lixinjie/p/10811219.html 常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使 ...

  4. 同步/异步/阻塞/非阻塞/BIO/NIO/AIO

    转摘自:https://www.cnblogs.com/lixinjie/p/a-post-about-io-clearly.html 常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HT ...

  5. 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

    常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...

  6. 同步/异步/阻塞/非阻塞/BIO/NIO/AIO各种情况介绍

    常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据. 如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTT ...

  7. 非阻塞/异步(epoll) openssl

    前段时间在自己的异步网络框架handy中添加openssl的支持,当时在网络上搜索了半天也没有找到很好的例子,后来自己慢慢的摸索,耗费不少时间,终于搞定.因此把相关的资料整理一下,并给出简单的例子,让 ...

  8. JAVA 中BIO,NIO,AIO的理解以及 同步 异步 阻塞 非阻塞

    在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步 ...

  9. IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别参考

    参考https://www.cnblogs.com/aspirant/p/6877350.html?utm_source=itdadao&utm_medium=referral IO复用,AI ...

随机推荐

  1. zabbix 参数说明

    <pre name="code" class="html">数据采集的工作模式可以分为被动模式(服务器端到客户端采集数据) 主动模式(客户端主动上报 ...

  2. C++学习之容器的摸索

    初学容器,容易犯错的地方 1.vector,list和deque都是顺序容器.其中vector和deque都可以通过下标访问,而list不能 2. 容器的begin和end操作 c.begin()返回 ...

  3. 【模拟】Codeforces 671B Robin Hood

    题目链接: http://codeforces.com/problemset/problem/671/B 题目大意: N个人,每个人有Ci钱,现在有一个人劫富济贫,从最富的人之一拿走1元,再给最穷的人 ...

  4. cf703A Mishka and Game

    A. Mishka and Game time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  5. 用phpQuery像jquery一样解析html代码

    简介 如何在php中方便地解析html代码,估计是每个phper都会遇到的问题.用phpQuery就可以让php处理html代码像jQuery一样方便. 项目地址:https://code.googl ...

  6. getting start with storm 翻译 第八章 part-1

    转载请注明出处:http://blog.csdn.net/lonelytrooper/article/details/12434915 第八章 事务性Topologies 在Storm中,正如本书前边 ...

  7. crossfire 346# B

    Vasya has the square chessboard of size n × n and m rooks. Initially the chessboard is empty. Vasya ...

  8. springMVC整合jedis+redis

    http://www.cnblogs.com/zhengbn/p/4140549.html 前两天写过 springMVC+memcached 的整合,我从这个基础上改造一下,把redis和sprin ...

  9. WebService-通俗讲解

    一.序言 大家或多或少都听过 WebService(Web服务),有一段时间很多计算机期刊.书籍和网站都大肆的提及和宣传WebService技术,其中不乏很多吹嘘和做广告的成 分.但是不得不承认的是W ...

  10. 使用easy_install安装numpy、pandas、matplotlib及各种第三方模块

    倒腾了一晚上最终把题目中的环境配好了.以下简要说明.留作资料.并共享. 1.安装python. 在cmd中能进入python环境,通过把python路径加入到系统路径中就可以实现. 2.安装easy- ...