如何让服务端同时支持WebSocket和SSL加密的WebSocket(即同时支持ws和wss)?

  自从HTML5出来之后,使用WebSocket通讯就变得火热起来,基于WebSocket开发的手机APP和手机游戏也愈来愈多。个人一些开发APP的朋友,开始使用WebSocket通讯,后来以为通讯不够安全,想要对通讯进行加密,因而天然而然地就想从ws升级到wss。在升级的过程当中,就会存在旧的ws客户端与新的wss客户端同时链接到同一个服务器的状况。因此,若是同一个服务端,能同时支持ws和wss,那就太方便了。html

一. 实现方案

  可是,要服务端同时支持ws与wss并不太容易,其难点主要在于:wss通道必须在TCP链接刚创建时(收发消息前)就要先进行SSL加密,不然,后续的通讯将没法正常进行。如此一来,当同时存在ws和wss客户端时,服务器在还没有通讯以前就没法具体分辨哪一个是ws哪一个是wss。那怎么办了?咱们的解决方案,是采用试探法,该方案已经在 ESFramework 通讯框架中实现。 api

(1)因为wss通道必须在TCP链接刚创建时(收发消息前)就要先进行SSL加密,不然,后续的通讯将没法正常进行。浏览器

(2)基于(1),在没有收发任何消息时,服务端就没法将wss客户端与其它客户端区分开来。安全

(3)为此采用的办法是:对于任何刚创建的TCP链接,先都不加密它,等收到的第一个消息来判断其消息的头标志。服务器

(4)若是头标志不是ESFramework所规定的标志,则表示这第一个消息是密文,没法被解析,从而说明这个客户端是wss。因而将该客户端的ip放到cache中,并断开该链接。框架

(5)wss客户端会从新连上来,此时服务端从cache中发现已经存在目标ip,则断定其为wss客户端,因而当即使用SSL加密该通道,以后,该wss客户端就能够正常通讯了。测试

(6)因为wss 客户端 IP在cache中的过时时间是 6秒左右,因此,若是一个客户端IP刚登陆了wss客户端,那么在同一个IP上登陆第二个客户端(任何客户端类型),就须要相隔6秒以后。加密

   基于以上方案实现服务端后,咱们接下来基于 ESFramework入门demo 来具体讲解一下如何在实际应用中同时支持ws和wss。spa

二. 服务端实现

1. 数字证书

      为测试方便,咱们可使用 CertificateCreator 制做一个用于本地测试的数字证书。code

      运行 CertificateCreator.exe, 而后输入Common Name(好比Test)、密码、保存路径(好比D:\server.pfx),咱们就能够获得包含私钥的证书server.pfx 。双击server.pfx ,便可安装证书。 

2. 服务端引擎设置

       在服务端RapidServerEngine初始化以前,添加以下代码设置其 WssOptions 属性:

        WssOptions wssOptions = new WssOptions( new X509Certificate2("D:\\server.pfx", "password") ,SslProtocols.Default ,false);
        rapidServerEngine.WssOptions = wssOptions;   

        设置完成后,启动服务端。 

三. 客户端实现

1. 信任测试用的数字证书      

        因为上述生成的数字证书仅仅是用于测试的,而是不被正式承认的,因此,须要在浏览器设置中,将目标数字证书加入到信任列表。

        好比,在360浏览器中,可以下设置:

          

        在FireFox中,设置以下:

         

          将服务器的地址(https://127.0.0.1:4530)添加到例外中。  

2. 客户端引擎设置

        打开入门demo的Web端源码中的index.js文件,找到engine的Initialize方法,将 useWss 参数由false修改成true。

        而后将Web端的 index.html 文件拖入浏览器中运行便可。 

四. 运行效果

       登陆一个wss客户端,一个ws客户端和一个.NET客户端,服务端的UI显示以下:

       

        下载 Demo源码