客户端请求:
- 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请求头。
只需要判断请求头中的Connection及Upgrade,判断新旧版本可以通过是否包含“Sec-WebSocket-Key1”和“Sec-WebSocket-Key2”。以下是一小段判断是否WEBSOCKET请求的代码:
注:Sec-WebSocket-Key1和Sec-WebSocket-Key2在旧的WEBSOCKET协议中是没有的,因为判断当前请求是否WEBSOCKET,主要还是通过请求头中的Connection是不是等于Upgrade以及Upgrade是否等于WebSocket,也就是说判断一个请求是否WEBSOCKET请求,只需要
服务端回应:
- 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。
(以下内容转自:http://lchshu001.iteye.com/blog/1184428)
简单介绍一下 WebSocket 它是实现了浏览器与服务器的全双工信息传输。Websocket协议基于Http 的 Upgrade 头和101的响应进行协议切换。经过简单的握手协议,建立一个长连接,按照协议的规则进行数据的传输。具体介绍可以参考google.
1.握手协议
版本0--3中:
握手通过请求头Sec-WebSocket-Key1 和 Sec-WebSocket-Key2 的值和 8 字节的请求实体,进行MD5加密,将加密结果,构造出一个16字节作为请求实体的内容返回。如下实例:
------------------请求--------------------------------------------
- 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:
- (\r\n)
- ^n:ds[4U
------------------响应--------------------------------------------
- HTTP/1.1 101 WebSocket Protocol Handshake
- Upgrade: WebSocket
- Connection: Upgrade
- Sec-WebSocket-Origin: http:
- Sec-WebSocket-Location: ws:
- Sec-WebSocket-Protocol: sample
- (\r\n)
- 8jKS'y:G*Co,Wxa-
------------------------------------------------------------------ 把第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此,这样得到两个整数,把每个整数写的四个字节里去,串为8个字节,然后和请求实体里面的8个字节串为16字节,将这16个字节进行MD5加密(如实例中的结果:8jKS'y:G*Co,Wxa-),得到一个16字节的数据作为响应实体的内容,返回给客户端,这样握手成功。 代码实现:
- int len = 8;
- byte[] key3 = new byte[len];
- if (in.read(key3) != len)
- throw new RuntimeException();
- log.debug(HelpUtil.formatBytes(key3));
- String key1 = requestHeaders.get("Sec-WebSocket-Key1");
- String key2 = requestHeaders.get("Sec-WebSocket-Key2");
- int k1 = HelpUtil.parseWebsokcetKey(key1);
- int k2 = HelpUtil.parseWebsokcetKey(key2);
-
- byte[] sixteenByte = new byte[16];
- System.arraycopy(HelpUtil.intTo4Byte(k1), 0, sixteenByte, 0, 4);
- System.arraycopy(HelpUtil.intTo4Byte(k2), 0, sixteenByte, 4, 4);
- System.arraycopy(key3, 0, sixteenByte, 8, 8);
- byte[] md5 = MessageDigest.getInstance("MD5").digest(sixteenByte);
在版本4之后,握手协议修改了: ------------------请求--------------------------------------------
- GET /chat HTTP/1.1
- Host: server.example.com
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- Sec-WebSocket-Origin: http:
- Sec-WebSocket-Protocol: chat, superchat
- (\r\n)
------------------响应--------------------------------------------
- HTTP/1.1 101 Switching Protocols
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
- Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
- Sec-WebSocket-Protocol: chat
使用请求头的值 Sec-WebSocket-Key,该值是BASE-64编码(base64-encoded)的,我们不需要转码,加上一个魔幻字符串: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",(结果:[dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11])使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 Sec-WebSocket-Accept 头的值,返回给客户端。 如果服务器端有 Sec-WebSocket-Nonce 头,表示要在Sec-WebSocket-Key 的值,和魔幻字符串之间加入该 Sec-WebSocket-Nonce 头的值,即“dGhlIHNhbXBsZSBub25jZQ==AQIDBAUGBwgJCgsMDQ4PEC==258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,进行 SHA-1 加密,之后和前面的相同。完成握手协议。
- public static final String GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- public static final String HEADER_CODE = "iso-8859-1";
-
- String code = requestHeaders.get("Sec-WebSocket-Key") + GUID;
- byte[] bts = MessageDigest.getInstance("SHA1").digest(code.getBytes(HEADER_CODE));
- code = HelpUtil.getBASE64(bts);
- resMap.put("Sec-WebSocket-Accept", code);
握手完成就是数据帧的传输了。 在版本 0 中, 数据帧比较的简单。数据帧以 0x00 开头,以0xFF结尾,中间的数据以utf-8编码的字符就可以了。当然这个简单的格式只能用来传输字符串。无法传输字节流。所以 版本 1 就做了修改了,后面的版本绝大部分是兼容的。 后面的这个帧结构就有点复杂了,如下所示(一行是4个字节,32 bit):
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-------+-+-------------+-------------------------------+
- |M|R|R|R| opcode|R| Payload len | Extended payload length |
- |O|S|S|S| (4) |S| (7) | (16/63) |
- |R|V|V|V| |V| | (if payload len==126/127) |
- |E|1|2|3| |4| | |
- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- | Extended payload length continued, if payload len == 127 |
- + - - - - - - - - - - - - - - - +-------------------------------+
- | | Extension data |
- +-------------------------------+ - - - - - - - - - - - - - - - +
- : :
- +---------------------------------------------------------------+
- : Application data :
- +---------------------------------------------------------------+
(后续的版本略有修改) 获取数据长度
- int dataLen = bt & PAYLOADLEN;
-
- if (dataLen == HAS_EXTEND_DATA) {
- bt = in.read();
- b2 = in.read();
- fram.setDateLength(HelpUtil.toShort((byte) bt, (byte) b2));
- } else if (dataLen == HAS_EXTEND_DATA_CONTINUE) {
- byte[] bts = new byte[8];
- if (in.read(bts) != 8){
-
- throw new RuntimeException(
- "reader Payload-Len-Extended-Continued data length < 64 bit");
- }
- fram.setDateLength(HelpUtil.toLong(bts));
- } else {
- fram.setDateLength(dataLen);
- }
[MORE] 表示一个数据通过多个帧进行传输, 如果是 0 表示后面还有数据帧,如果是 1 则表示是最后一个帧。 [RSV1][RSV2][RSV3][RSV4] 未做定义暂时全为零。 [opcode] 标识数据的格式,以及帧的控制,如:08标识数据内容是 文本,01标识:要求远端去关闭当前连接。 [Payload len] 如果小于126 表示后面的数据长度是 [Payload len] 的值。(最大125byte) 等于 126 表示之后的16 bit位的数据值标识数据的长度。(最大65535byte) 等于 127 表示之后的64 bit位的数据值标识数据的长度。(一个有符号长整型的最大值) [Extension data]没有提及怎么使用。 [Application data] 为应用提供的数据。 版本7之后,添加了 MASK 的概念。相当于对数据加密。而且要求客户端必须是MASK的。
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-------+-+-------------+-------------------------------+
- |F|R|R|R| opcode|M| Payload len | Extended payload length |
- |I|S|S|S| (4) |A| (7) | (16/63) |
- |N|V|V|V| |S| | (if payload len==126/127) |
- | |1|2|3| |K| | |
- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- | Extended payload length continued, if payload len == 127 |
- + - - - - - - - - - - - - - - - +-------------------------------+
- | |Masking-key, if MASK set to 1 |
- +-------------------------------+-------------------------------+
- | Masking-key (continued) | Payload Data |
- +-------------------------------- - - - - - - - - - - - - - - - +
- : Payload Data continued ... :
- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- | Payload Data continued ... |
- +---------------------------------------------------------------+
[opcode] 01标识数据内容是 文本,08标识 : 要求远端去关闭当前连接。 [MASK](即原先的RSV4)如果是 1 则数据是被 MASK 的。 [Masking-key] 如果MASK为 1 则有4字节的 Masking-key,用于与传输的数据 [Payload Data] 进行异或运算,4byte(32bit)进行一次运算,不足四位从前往后对应,如只有三位,则只与[Masking-key]的前三位进行运算。 解码 MASK 数据,使用了一个过滤流
- @Override
- public int read() throws IOException {
- if (readLength >= length)
- return -1;
- int b = 0;
- synchronized (lock) {
- if (readLength >= length)
- return -1;
- b = super.read();
- if (isMask) {
- b ^= maskKey[(int) (readLength % 4)];
- }
- readLength++;
- }
- return b;
- }
关于流的关闭:一般情况我们可以直接 使用socket.close() 进行关闭,客户端JS状态会显示 webSocket.readyState 的值为 2 (正在关闭的状态)。需要我们通过握手去要求远端关闭流。 有三个版本: 在版本 0 时:传两个字节 (0xff,0x00); 在版本 1--6 时:传三个字节 (0x80,0x01,0x00); 在版本 7--以上 时:传两个字节 (0x88,0x00); 经测试 只有 在版本 7--以上 时:传两个字节 (0x88,0x00); 这时可以实现 webSocket.readyState 的值为 3。 估计是我的代码有问题。如有发现请告知,谢谢!
分享到:
相关推荐
WebSocket 协议实现 下面是根据给定文件生成的相关知识点: 一、WebSocket 协议简介 WebSocket 协议是一种基于 TCP 协议的双向通信协议,旨在解决浏览器和服务器之间的全双工通信问题。它可以让浏览器和服务器...
WebSocket协议手册(rfc6455中文翻译)详细介绍了WebSocket通信协议的相关知识点,以下是手册中涵盖的知识点详解: 1. WebSocket协议概述: WebSocket是一种在单个TCP连接上进行全双工通信的协议,可以实现实时的双向...
RFC6455是WebSocket协议的官方文档标准版本,由互联网工程任务组(IETF)制定,是互联网标准跟踪文件。 ### WebSocket协议概述 WebSocket协议的主要目的是为了解决传统HTTP协议在需要服务器和客户端双向通信时效率...
WebSocket协议是一种在互联网上实现全双工通信的协议,它为客户端和服务器之间提供持久连接,使得数据可以双向实时传输。这个协议的诞生解决了HTTP协议的局限性,HTTP协议是基于请求-响应模型的,每次通信都需要...
WebSocket协议是一种在互联网上实现全双工通信的协议,它为客户端和服务器提供持久性的连接,使得双向数据传输成为可能。这个协议的正式规范是IETF的RFC6455,发布于2011年12月。WebSocket的诞生是为了克服HTTP协议...
WebSocket协议实现在受控环境中运行不受信任代码的一个客户端到一个从该代码已经选择加入通信的远程主机之间的全双工通信。用于这个的安全模型是通常由web浏览器使用的基于来源的安全模型。该协议包括一个打开阶段...
### WebSocket协议中文版知识点解析 #### 一、引言 **1.1 背景** WebSocket协议旨在解决传统HTTP连接对于实时交互场景的支持不足问题。传统的HTTP协议采用请求/响应模式,即客户端向服务器发出请求后等待服务器...
《语音识别(ASR)WebSocket协议WebAPI开发详解》 语音识别技术,即Automatic Speech Recognition (ASR),是计算机科学领域的重要组成部分,它能够将人类的口头语言转化为文本信息。在现代信息技术中,ASR被广泛...
我们需要准备Jmeter的WebSocket协议的支持插件: JMeterWebSocketSampler-1.0.2-SNAPSHOT.jar 要安装此插件,请将jar文件放入\ JMeter \ lib \ ext文件夹中。 请记住下载依赖项并将其放入同一文件夹。 您将在...
1. 文件无任何依赖可以编译成linux,window,arm平台都能使用 2. 单文件根据执行参数可以既可以当服务端用也可以当客户端用 3. 支持上传模式和下载...11. 基于websocket协议非常容易用nginx反向代理容易实现7层负载均衡
WebSocket协议是一种在客户端和服务器之间建立长连接的协议,它为双向通信提供了低延迟、高效的数据传输方式。在传统的HTTP协议中,每次请求-响应都需要重新建立连接,而WebSocket则在连接建立后可以保持长时间打开...
总的来说,WebSocket协议提供了一种高效、低延迟的双向通信机制,尤其适合实时应用,如在线游戏、股票实时报价、多人协作工具等。通过与HTTP的兼容性,它能够利用现有的网络基础设施,简化了服务器和客户端的部署和...
WebSocket协议是一种在客户端和服务器之间建立长久连接的网络通信协议,它允许双方进行全双工通信,即数据可以在任意方向上流动,而无需为每个数据包建立新的HTTP请求。WebSocket协议在现代Web应用中被广泛使用,...
基于C++实现的解析websocket协议+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于C++实现的解析websocket协议+源码,适合毕业设计、课程设计、项目开发...
WebSocket 协议分析及应用研究 WebSocket 协议是一种基于 TCP 的通信协议,旨在解决传统 HTTP 协议在实时通信中的不足。该协议的出现极大地提高了实时通信的效率和质量。WebSocket 协议的应用非常广泛,特别是在...
1. **TCP上实现**:直接基于TCP套接字构建WebSocket协议,这需要自己处理WebSocket的握手协议、帧编码和解码等细节。 2. **现有HTTP软件上实现**:利用Node.js的HTTP服务器,如`http`模块,处理WebSocket的升级请求...
WebSocket Sharp 是一个C#实现的WebSocket协议库,它支持客户端和服务端的功能,符合RFC 6455标准。这个组件不仅提供了基本的WebSocket连接管理,还包含了一些高级特性,如消息压缩、安全连接、HTTP身份验证、代理...
ActiveMQ,作为一款流行的开源消息中间件,也支持WebSocket协议,使得客户端可以通过WebSocket接口来接收和发送消息。 ActiveMQ是Apache软件基金会开发的消息队列产品,它遵循开放标准,如JMS(Java Message ...