浅析websocket

最近做毕业设计里面要用到WebSocket,所以百度和Google了下相关的问题。下面的文章整理自:

http://cpper.info/2016/01/24/websocket.html

http://blog.csdn.net/swallowing_/article/details/47321241

此处只整理一些我用到的知识,第一个网址很详细地说明了WebSocket的原理可以参一下。

问题1:何为WebSocket

WebSocket是HTML5新增加的一种通信协议(参考RFC6455),我们知道HTTP协议是一种单向的网络协议,在建立连接后,它只允许浏览器客户端向WebServer发出请求资源后,WebServer才能返回相应的数据。即WebServer不能主动的推送数据,但是面对在web系统上实时聊天的这类需求,基于HTTP协议实现会很麻烦,公司项目组开发的在线客服系统中也有这样的需求,为了更好地实现,采用的是WebSocket技术。WebSocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(WebServer)之间能建立一个类似TCP的连接,从而方便c-s之间的通信。在WebSocket出现之前,web交互一般是基于HTTP协议的短连接或者长连接。WebSocket是为解决客户端与服务端实时通信而产生的技术。WebSocket协议本质上是一个基于TCP的协议,是先通过HTTP/HTTPS协议发起一条特殊的HTTP请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。

注意:此时不再需要原HTTP协议的参与了

问题2: websocket的优点

以前web server实现推送技术或者即时通讯,用的都是轮询(polling),在特点的时间间隔(比如1秒钟)由浏览器自动发出请求,将服务器的消息主动的拉回来,在这种情况下,我们需要不断的向服务器发送请求,然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源。而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。

  • 节省每次请求的header
    http的header一般有几十字节
  • Server Push
    服务器可以主动传送数据给客户端

 

问题3:WebSocket如何通讯

与http协议不同的请求/响应模式不同,Websocket在建立连接之前有一个Handshake(Opening Handshake)过程,在关闭连接前也有一个Handshake(Closing Handshake)过程,建立连接之后,双方即可双向通信。 在websocket协议发展过程中前前后后就出现了多个版本的握手协议,这里分情况说明一下:

  • 基于flash的握手协议 使用场景是IE的多数版本,因为IE的多数版本不都不支持WebSocket协议,以及FF、CHROME等浏览器的低版本,还没有原生的支持WebSocket。此处,server唯一要做的,就是准备一个WebSocket-Location域给client,没有加密,可靠性很差。

客户端请求:

GET /ls HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: www.qixing318.com
Origin: http://www.qixing318.com

服务器返回:

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.qixing318.com
WebSocket-Location: ws://www.qixing318.com/ls - 基于md5加密方式的握手协议

客户端请求:

GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2:
Upgrade: WebSocket
Sec-WebSocket-Key1:
Origin: http://www.qixing318.com
[8-byte security key]

服务端返回:

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.qixing318.com
WebSocket-Location: ws://example.com/demo
[16-byte hash response]
其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这几个头信息是web server用来生成应答信息的来源,依据 draft-hixie-thewebsocketprotocol-76 草案的定义。
web server基于以下的算法来产生正确的应答信息:

1. 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到一起放到一个临时字符串里,同时统计所有空格的数量;
2. 将在第(1)步里生成的数字字符串转换成一个整型数字,然后除以第(1)步里统计出来的空格数量,将得到的浮点数转换成整数型;
3. 将第(2)步里生成的整型值转换为符合网络传输的网络字节数组;
4. 对 Sec-WebSocket-Key2 头信息同样进行第(1)到第(3)步的操作,得到另外一个网络字节数组;
5. 将 [8-byte security key] 和在第(3)、(4)步里生成的网络字节数组合并成一个16字节的数组;
6. 对第(5)步生成的字节数组使用MD5算法生成一个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的请求,同意创建websocket连接
基于sha加密方式的握手协议
也是目前见的最多的一种方式,这里的版本号目前是需要13以上的版本。
客户端请求:

GET /ls HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: www.qixing318.com
Sec-WebSocket-Origin: http://www.qixing318.com
Sec-WebSocket-Key: 2SCVXUeP9cTjV+0mWB8J6A==
Sec-WebSocket-Version: 13

服务器返回:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: mLDKNeBNWz6T9SxU+o0Fy/HgeSw=

其中 server就是把客户端上报的key拼上一段GUID( “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″),拿这个字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,最后再返回给客户端。

Handshake请求

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13 服务器端响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Upgrade:WebSocket 表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。

Sec-WebSocket-Key :是一段浏览器base64加密的密钥,server端收到后需要提取Sec-WebSocket-Key 信息,然后加密。

Sec-WebSocket-Accept :服务器端在接收到的Sec-WebSocket-Key密钥后追加一段神奇字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,并将结果进行sha-1哈希,然后再进行base64加密返回给客户端(就是Sec-WebSocket-Key)。比如:

function encry($req) {
$key = $this->getKey($req); $mask = “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”;
//将 SHA-1 加密后的字符串再进行一次 base64 加密
return base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}
//如果加密算法错误,客户端在进行校检的时候会直接报错。如果握手成功,则客户端侧会出发onopen事件。

Sec-WebSocket-Protocol: 表示客户端请求提供的可供选择的子协议,及服务器端选中的支持的子协议,“Origin”服务器端用于区分未授权的websocket浏览器

Sec-WebSocket-Version:13 客户端在握手时的请求中携带,这样的版本标识,表示这个是一个升级版本,现在的浏览器都是使用的这个版本。

HTTP/1.1 101 Switching Protocols 101:为服务器返回的状态码,所有非101的状态码都表示handshake并未完成。

问题4:WebSocket的最大连接数

貌似都有些不同下面附上测试的代码,可以自行测试下

WebSocket现在很常用,想要测试tomcat的最大连接数,今天试了一个可行的办法和配置(之前是用全公司的设备一起来测试的,真机环境的测试收到网络的影响很大,其实真实环境应用中,网络才是websocket的最大瓶颈)

废话不多说~

Setp 1.Tomcat需要设置的地方(很多人从网上找的配置试了不可行):

Setp 2.修改catalina.sh或者catalina.bat文件,加上这句话
(JAVA_OPTS=”-server -Xms2048m -Xmx2048m -Xmn512m”)。

Setp 3.修改conf下server.xml,

<Connector port="8180" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" acceptCount="4000"  maxThreads="500" maxConnections="4000" enableLookups="false"  redirectPort="8443" />

上面的设置1.1影响最终你的最大连接数,1.2的maxConnections就是最大连接数,有时候觉得设置无效,请把1.1的内存一起调大!

package com.jh.order;
import java.NET.URI;
import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
@ClientEndpoint
public class WebSocketTest {
    private String deviceId;
    private Session session;

    public WebSocketTest() {
    }

    public WebSocketTest(String deviceId) {
        this.deviceId = deviceId;
    }

    protected boolean start() {
        WebSocketContainer Container = ContainerProvider.getWebSocketContainer();
        String uri = "ws://192.168.12.161/orderapp/recommend?role=1&group=recommend&dldm=" +
            deviceId;
        System.out.println("Connecting to " + uri);

        try {
            session = container.connectToServer(WebSocketTest.class,
                    URI.create(uri));
            System.out.println("count: " + deviceId);
        } catch (Exception e) {
            e.printStackTrace();

            return false;
        }

        return true;
    }

    public static void main(String[] args) {
        for (int i = 1; i < 50000; i++) {
            WebSocketTest wSocketTest = new WebSocketTest(String.valueOf(i));

            if (!wSocketTest.start()) {
                System.out.println("?????");

                break;
            }
        }
    }
}

问题5:WebSocket一次可以传输多少字符?

经过我不完美的测试时大概8192字符左右….你问我如何一次性传输大量数据???

分段

加入讨论

电子邮件地址不会被公开。