openssl在实现ssl握手的时候是用状态机实现的,实际上linux内核的tcp协议也是状态机实现的,原因就在于这些协议本身的握手规则就是状态机(简直是废话)。SSLv3握手规则要比tcp复杂的多,因此它的实现如果要既美观又高效的话就一定需要很多技巧,造成这种结果的原因就在于ssl握手过程中存在很多的可选消息,而这些可选消息是否存在并不简单依赖与前一个或者前若干个消息的消息头,而只有解析了前面的消息体内容之后才能知道是否存在可选消息,在这个意义上可以说客户端的握手状态机实现要么保存曾经的消息遗留的状态,要么不保留状态,默认按状态机转化顺序处理任何消息(还好,状态机有且只有一种顺序),如果采用前一个方案的话,那么势必要在握手规则和消息处理handler之间进行耦合,如果那一天改变了一方的框架,就会导致另一方的改变,openssl的实现采用了后一种解决方案巧妙的解决了这个问题,不管怎样状态机顺序地在所有可能的状态中转变,每一个状态中调用固定的处理函数,然后在处理函数中采用了一个技巧:处理函数首先不去下面的BIO(一般是tcp套接字)中读消息,而是先检查一个标志reuse,如果这个标志置位了,那么就清除这个标志,取上次读到的消息后返回,在返回状态机处理handler之后,首先根据得到消息的头判断其类型是否是状态机处于该状态所处理消息的对应类型,如果不是,上述的reuse标志重新置位,handler返回,状态机推进到下一个状态,从而在接下来的状态处理当中不再从套接字读取消息。大致框架就是:
state_machine()
{
    while (true) {
        switch (state)
            case s1:
                handler1(param);
                state = s2;
                break;
            case s2:
                handler2(param);
                state = s3;
                break;
            case s3:
                handler3(param);
                state = s4;
                break;
            ...
    }
}
如果s2状态所对应的消息是可选的,但是s3是必须的状态,并且此次此时并不需要处理s2这个状态,但是handler2中确实要读取消息,怎么办呢?如果真的从套接字读取消息的话,那么处理就会乱掉,handler2中读取的是s3状态所对应的消息,那么怎么处理呢?openssl的方式是在case s2中调用handler2之中判断一下当前读到消息的类型,如果不是对应于s2的消息,那就说明s2状态所对应的消息不存在,于是在case s2的handler中进行完判断之后,将一个reuse标志设置上,于是在s3状态中的s3处理中首先判断是否有reuse标志,如果有的话,那么就不再从套接字读取消息,而是将在handler2中读取的消息返回,并且清除reuse标志,之后以此类推。总结一下就是只要在可选消息的handler处理之后,都要进行类似s2处理的判断,如果失败就将reuse设置。
     但是这又引出了另外一个问题,那就是在每个handler中都要判断reuse标志,这个工作实在繁重,状态机处理是解放了,每个状态的handler的任务却更加繁重了,任何好的设计需要做到的就是尽量将繁杂的判断放到稳定的逻辑当中,状态机是不稳定的,因为有很多可选状态,每个状态的handler也不是稳定的,因为每个状态的处理太复杂了,随着版本的更新,任何细节的改变都会导致大量的更改,于是openssl就把这个逻辑抽取出来,专门放到SSL_METHOD的一个回调函数ssl3_get_message中,在调用完这个回调函数之后马上检查读到的消息类型是否是我们本状态要处理的类型,如果不是,那么马上将reuse设置为1,我们看一下ssl_get_message中在真正在套接字上读取消息之前的一个处理:
if (s->s3->tmp.reuse_message)
{
    s->s3->tmp.reuse_message=0;
    if ((mt >= 0) && (s->s3->tmp.message_type != mt)) {
        ...
    }
    *ok=1;
    s->init_msg = s->init_buf->data + 4;
    s->init_num = (int)s->s3->tmp.message_size;
    return s->init_num;
}
以上代码是s->method->ssl_get_message中首先要调用的,对应上面代码的另一部分在ssl3_connect中可选消息handler的调用之后,以ssl3_get_key_exchange为例:
int ssl3_get_key_exchange(SSL *s)
{
...
    n=s->method->ssl_get_message(s,
        SSL3_ST_CR_KEY_EXCH_A,
        SSL3_ST_CR_KEY_EXCH_B,
        -1,
        s->max_cert_list,
        &ok);
...
    if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) {
        s->s3->tmp.reuse_message=1;
        return(1);
    }
...
}
openssl的这种实现使得握手状态机处理看起来十分清晰,所有的处理消息是否可选的逻辑都隐藏到了逻辑比较单一的回调函数中,然后在逻辑复杂的处理函数中简单判断即可,这种分离比SSLv2的设计要更好,在SSLv2的状态机实现中,每一个状态处理完了之后就会判断结果,然后根据结果去设置下一个状态,其实这种方式更像是一个状态机,SSLv3的实现反而不像状态机了,一个一个顺序的处理,没有变数,这种情况其实完全不用状态机实现,一个顺序执行的函数就可以了,连循环都不用,谁知道以后的版本会怎样呢。

转载于:https://blog.51cto.com/dog250/1271994

OpenSSL状态机中可选消息的处理相关推荐

  1. 一致性协议raft详解(三):raft中的消息类型

    一致性协议raft详解(三):raft中的消息类型 前言 raft 节点 Raft中RPC的种类 RequestVote leader选举成功后 AppendEntries 请求参数 返回值 存储日志 ...

  2. 详解SpringCloud中RabbitMQ消息队列原理及配置,一篇就够!

    作者:kosamino cnblogs.com/jing99/p/11679426.html 一.MQ用途 1.同步变异步消息 场景:用户下单完成后,发送邮件和短信通知. 运用消息队列之后,用户下单完 ...

  3. java中的消息队列

    消息队列:可以看做是一个存储消息的容器,它是分布式系统中的重要组件之一. 目的是: 1.为了通过异步处理来提高系统的性能来减少系统响应的时间 一般的步骤是客户端发起请求给服务端,服务端在请求给数据库, ...

  4. RTOS中的消息队列的原理以及应用

    消息队列的原理 RTOS中的消息队列是一种用于任务间通信的机制,它可以实现任务之间的异步通信,从而实现任务间的解耦.消息队列是一个先进先出的数据结构,任务可以向队列中发送消息,也可以从队列中接收消息. ...

  5. DELPHI 中 Window 消息大全使用详解

    Window 消息大全使用详解 导读: Delphi是Borland公司的一种面向对象的可视化软件开发工具. Delphi集中了Visual C++和Visual Basic两者的优点:容易上手.功能 ...

  6. 深度解析VC中的消息(上)

    消息是指什么?      消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程 ...

  7. Delphi中的消息截获

    Windows是一个基于消息驱动的系统,因此,在很多时候,我们需要截获一些消息然后自己进行处理.而VCL系统又有一些特定的消息.下面对我所了解的delphi环境中截获消息进行一些总结.       就 ...

  8. 如何在优雅地Spring 中实现消息的发送和消费

    本文将对rocktmq-spring-boot的设计实现做一个简单的介绍,读者可以通过本文了解将RocketMQ Client端集成为spring-boot-starter框架的开发细节,然后通过一个 ...

  9. rocketmq中的消息拉取及并发消费理解

    消息拉取采用单线程形式,便于消息的顺序拉取 默认批量取32个,出现性能考虑,减少网络请求.不能保证会拉取到32个,因为消息队列中的存放的是topic-queueid对应的索引,会包含多个tag,而消息 ...

最新文章

  1. Linux LVM逻辑卷配置过程详解
  2. 自己收集的一些技术blog-javascript
  3. 从“IBM刀片服务器广告告别电视”说起
  4. CTFshow php特性 web125
  5. 每天一道LeetCode-----在有序的二维数组中查找某个元素
  6. spring-AOP前言
  7. [渝粤教育] 厦门大学 大数据技术原理与应用 参考 资料
  8. Spring整合Quartz定时任务 在集群、分布式系统中的应用(Mysql数据库环境)
  9. 一目了然的 Docker 环境配置指南
  10. 别再面向 for 循环编程了,JDK 自带的观察者模式就很香!
  11. C语言集合的排序方法,排序集合 c语言.doc
  12. java通过桥访问excel_通过jdbc-odbc桥来访问excel文件
  13. vb用鼠标实现屏幕绘图
  14. 使用 matlab 进行正太拟合
  15. BZOJ4538 HNOI2016网络(树链剖分+线段树+堆/整体二分+树上差分)
  16. oracle安装包安装教程,oracle安装教程【搞定方案】
  17. java1.8.0_java jdk官方下载|java jdk v1.8.0 官方免费版-520下载站
  18. 北京“曼联梦剧场”项目开业,迎接中国球迷与家庭
  19. ViewStub延迟加载
  20. uniapp中针对H5端做微信分享功能总结

热门文章

  1. [Java拾遗三]JavaWeb基础之Servlet
  2. 使用Ant将windows下开发的Struts2应用直接部署到Linux上
  3. PHP工程师面临的成长瓶颈
  4. SQL SERVER中架构的理解
  5. 活动 | PMcaff大讲堂预告:听CRM大神教你玩转CRM
  6. 【震惊】史上最牛的市场推广/营销
  7. 【pmcaff】玩智能硬件的小伙伴,这些你用过么!
  8. 语音购票、刷脸进站:上海联手阿里打造全球首个AI地铁之城
  9. 这里有一份面筋请查收(四)
  10. easyui validatebox验证