WEBSOCKET协议判断、握手及反馈

本文详细介绍了消息中心通过WebSocket实现长连接的原理,包括客户端和服务端的握手过程,以及如何判断请求是否为WebSocket请求。同时,文章还讨论了不同版本的WebSocket协议及其握手方法,并提供了基于Netty的响应代码示例。

    消息中心的布点,是通过WEBSOCKET与后端服务器建立长连接的方式实现的,这种方式的优点一是节约网络带宽,二是用户可以实时的收到由后台发过来的消息,后端的实现采用的是NETTY,经过压力测试,每台服务器可以承受50万的长连接,也就是同时50万个用户(只为每个网站用户建立一个长连接),性能上还是比较好的。

    要建立长连接,首先需要由客户端发起与服务端的握手动作,以下是从wikipedia找的一个示例:

    浏览器请求:    

GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
Sec-WebSocket-Protocol: sample
Upgrade: WebSocket
Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
Origin: http://example.com
^n:ds[4U

    在请求中的“Sec-WebSocket-Key1”, “Sec-WebSocket-Key2”和最后的“^n:ds[4U”都是随机的,服务器端会用这些数据来构造出一个16字节的应答。其中:^n:ds[4U为请求的内容,其它的都是http请求头。

    注:Sec-WebSocket-Key1和Sec-WebSocket-Key2在旧的WEBSOCKET协议中是没有的,因为判断当前请求是否WEBSOCKET,主要还是通过请求头中的Connection是不是等于Upgrade以及Upgrade是否等于WebSocket,也就是说判断一个请求是否WEBSOCKET请求,只需要判断请求头中的Connection及Upgrade,判断新旧版本可以通过是否包含“Sec-WebSocket-Key1”和“Sec-WebSocket-Key2”。以下是一小段判断是否WEBSOCKET请求的代码:

//注:代码是基于Netty的
private boolean isWebSocketReq(HttpRequest req) {
        return (HttpHeaders.Values.UPGRADE.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.CONNECTION)) && HttpHeaders.Values.WEBSOCKET.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.UPGRADE)));
    }

    服务端回应:    

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Location: ws://example.com/demo
Sec-WebSocket-Protocol: sample
8jKS’y:G*Co,Wxa-
    把请求的第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此。然后把这两个结果与请求最后的8字节字符串连接起来成为一个字符串,服务器应答正文(“8jKS’y:G*Co,Wxa-”)即这个字符串的MD5 sum。以下是一段基于Netty的响应JAVA代码:    

    //基于Netty的WEBSOCKET响应代码
    private HttpResponse buildWebSocketRes(HttpRequest req) {
        HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
                                                   new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
        res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);
        res.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE);
        // Fill in the headers and contents depending on handshake method.
        if (req.containsHeader(Names.SEC_WEBSOCKET_KEY1) && req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {//草案7.5、7.6和协议标准
            // New handshake method with a challenge:
            res.addHeader(Names.SEC_WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN));
            res.addHeader(Names.SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req));
            String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
            if (protocol != null) {
                res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);
            }

            // Calculate the answer of the challenge.
            String key1 = req.getHeader(Names.SEC_WEBSOCKET_KEY1);
            String key2 = req.getHeader(Names.SEC_WEBSOCKET_KEY2);
            int a = (int) (Long.parseLong(getNumeric(key1)) / getSpace(key1).length());
            int b = (int) (Long.parseLong(getNumeric(key2)) / getSpace(key2).length());
            long c = req.getContent().readLong();
            ChannelBuffer input = ChannelBuffers.buffer(16);
            input.writeInt(a);
            input.writeInt(b);
            input.writeLong(c);
            ChannelBuffer output = null;
            try {
                output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array()));
            } catch (NoSuchAlgorithmException e) {
                logger.error("no such Algorithm : MD5. ", e);
            }

            res.setContent(output);
        } else {//最老的websocket协议
            // Old handshake method with no challenge:
            if (req.getHeader(Names.ORIGIN) != null) {
                res.addHeader(Names.WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN));
            }
            res.addHeader(Names.WEBSOCKET_LOCATION, getWebSocketLocation(req));
            String protocol = req.getHeader(Names.WEBSOCKET_PROTOCOL);
            if (protocol != null) {
                res.addHeader(Names.WEBSOCKET_PROTOCOL, protocol);
            }
        }

        return res;
    }
    // 去掉传入字符串的所有非数字
    private String getNumeric(String str) {
        return str.replaceAll("\\D", "");
    }

    // 返回传入字符串的空格
    private String getSpace(String str) {
        return str.replaceAll("\\S", "");
    }

后记:

    最近发现chrome14及FF6.5中使用最新的websocket草案10协议,也就是说上面的例出的代码还不能够支持草案10的握手协议,草案手的协议变量比较大,如传输是通过帧来进行的,并且对帧的位有权限检查等,详细可以查看我的另外一篇文章:http://blog.youkuaiyun.com/fenglibing/article/details/6852497

本文出自:冯立彬的博客




 

### 使用 JMeter 模拟 HTTP 协议升级至 WebSocket 为了使用 JMeter 测试 WebSocket 接口并模拟从 HTTP 到 WebSocket协议升级,可以按照以下方法进行配置: #### 准备工作 确保已下载最新版本的 JMeter[^1]。安装完成后,在插件管理器中添加必要的 WebSocket 插件支持。 #### 创建测试计划 启动 JMeter 后创建一个新的测试计划,并依次添加线程组和其他组件。 #### 配置 HTTP 请求以发起握手 在测试计划下添加一个 **HTTP 请求** 默认设置项用于初始化连接前的状态参数。接着在同一级别上增加具体的 **HTTP 请求采样器** 来执行实际的手动操作——即向服务器发出带有 `Upgrade` 头部字段值为 `"websocket"` 的 GET 请求,从而触发 Web Socket 连接转换过程。 ```http GET /chat HTTP/1.1 Host: example.com Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Origin: http://example.com ``` 此部分可以通过自定义 HTTP Header Manager 添加上述头部信息实现[^2]。 #### 建立 WebSocket 连接 一旦完成初始阶段,则需引入专门设计用来处理 WS(WSS) 类型通信流的数据采集工具—**WebSocket request-response Sampler** 或者其他适合的选择[^3]。该元件允许指定目标 URL 及其路径、查询字符串等细节;同时提供选项设定超时时间以及期望收到的消息格式等内容。 对于已经存在的会话链路而言,可选用对应的子类目来简化流程控制逻辑,比如仅当先前建立好的通道仍然有效时才继续下一步动作。 #### 结果验证与性能分析 最后一步是在整个场景构建完毕之后加入监听设备以便收集反馈数据供后续评估参考之用。推荐采用 **View Results Tree Listener** 组件实时观察各节点间传递的信息片段及其状态码变化情况,进而判断整体架构运行状况是否正常稳定[^4]。 ```python import time def check_connection_status(response): if response.status_code == 101: print("成功切换到Websocket") elif response.status_code != 200: raise Exception(f"错误响应 {response.status_code}") time.sleep(5) # 等待一段时间让连接生效 check_connection_status(http_response) ``` 以上就是利用 Apache JMeter 工具来进行基于 HTTP 的 WebSocket 握手及消息交换的基本指南。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值