【WebSocket初探 】

众所周知,socket是编写网络通信应用的基本技术,网络数据交换大多直接或间接通过socket进行。对于直接使用socket的client与服务端,一旦连接被建立则均可主动向对方传送数据,而对于使用更上层的HTTP/HTTPS协议的应用,因为它们是非连接协议,所以通常仅仅能由client主动向服务端发送请求才干获得服务端的响应并取得相关的数据。而当前越来越多的应用希望可以及时获取服务端提供的数据,甚至希望可以达到接近实时的数据交换(比如非常多站点提供的在线客户系统)。为达到此目的,通常採用的技术主要有轮询、长轮询、流等,而伴随着HTML5的出现,相对更优异的WebSocket方案也应运而生。

一、            非WebSocket方案简单介绍

1.      轮询

轮询是由client定时向服务端发起查询数据的请求的一种实现方式。早期的轮询是通过不断自己主动刷新页面而实现的(在那个基本是IE统治浏览器的时代,那不断刷新页面产生的噪声就难以让人忍受),后来随着技术的发展,特别是Ajax技术的出现,实现了无刷新更新数据。但本质上这些方式均是client定时轮询服务端,这样的方式的最显著的缺点是假设client数量庞大而且定时轮询间隔较短服务端将承受响应这些client海量请求的巨大的压力。

2.      长轮询

在数据更新不够频繁的情况下,使用轮询方法获取数据时client常常会得到没有数据的响应,显然这样的轮询是一个浪费网络资源的无效的轮询。长轮询则是针对普通轮询的这样的缺陷的一种改进方案,其详细实现方式是假设当前请求没有数据能够返回,则继续保持当前请求的网络连接状态,直到服务端有数据能够返回或者连接超时。长轮询通过这样的方式降低了client与服务端交互的次数,避免了一些无谓的网络连接。可是假设数据变更较为频繁,则长轮询方式与普通轮询在性能上并无显著差异。同一时候,添加连接的等待时间,往往意味着并发性能的下降。

3.      流

所谓流是指client在页面之下向服务端发起一个长连接请求,服务端收到这个请求后响应它并不断更新连接状态,以确保这个连接在client与服务端之间一直有效。服务端能够通过这个连接将数据主动推送到client。显然,这样的方案实现起来相对照较麻烦,并且可能被防火墙阻断。

二、            WebSocket简单介绍

1.      WebSocket协议简单介绍

WebSocket是为解决client与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,此后服务端与client通过此TCP连接进行实时通信。

WebSocket规范当前还没有正式版本号,草案变化也较为迅速。Tomcat7(本文中的例程来自7.0.42)当前支持RFC 6455(http://tools.ietf.org/html/rfc6455)定义的WebSocket,而RFC
6455眼下还未冻结,将来可能会修复一些Bug,甚至协议本身也可能会产生一些变化。

RFC6455定义的WebSocket协议由握手和传输数据两个部分组成。

来自client的握手信息类似例如以下:

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

一旦client和服务端都发送了握手信息而且成功握手,则传输数据部分将開始。传输数据对client和服务端而言都是一个双工通信通道,client和服务端来回传递的数据称之为“消息”。

client通过WebSocket URI发起WebSocket连接,WebSocket URIs模式定义例如以下:

ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

ws是普通的WebSocket通信协议,而wss是安全的WebSocket通信协议(就像HTTP与HTTPS之间的差异一样)。在缺省情况下,ws的port是80而wss的port是443。

关于WebSocke协议规范的完整详尽说明,请參考RFC 6455。

2.      Tomcat7提供的WebSocket包简单介绍

Tomcat7提供的与WebSocket相关的类均位于包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的实现包括于文件catalina.jar之中),它包括有类Constants、MessageInbound、StreamInbound、WebSocketServlet、WsFrame、WsHttpServletRequestWrapper、WsInputStream、WsOutbound。这些类的关系如图
1所看到的。

图1

包org.apache.catalina.websocket中的这些类为WebSocket开发服务端提供了支持,这些类的主要功能简述例如以下:

        Constants:包org.apache.catalina.websocket中用到的常数定义在这个类中,它仅仅包括静态常数定义,无不论什么逻辑实现。

        MessageInbound:基于消息的WebSocket实现类(带内消息),应用程序应当扩展这个类并实现其抽象方法onBinaryMessage和onTextMessage。

       StreamInbound:基于流的WebSocket实现类(带内流),应用程序应当扩展这个类并实现其抽象方法onBinaryData和onTextData。

       WebSocketServlet:提供遵循RFC6455的WebSocket连接的Servlet基本实现。client使用WebSocket连接服务端时,须要将WebSocketServlet的子类作为连接入口。同一时候,该子类应当实现WebSocketServlet的抽象方法createWebSocketInbound,以便创建一个inbound实例(MessageInbound或StreamInbound)。

       WsFrame:代表完整的WebSocket框架。

       WsHttpServletRequestWrapper:包装过的HttpServletRequest对象。

       WsInputStream:基于WebSocket框架底层的socket的输入流。

       WsOutbound:提供发送消息到client的功能。它提供的全部向client的写方法都是同步的,能够防止多线程同一时候向client写入数据。

三、            基于Tomcat7的WebSocket例程

利用当前HTML5和Tomcat7为WebSocket提供的支持,基本仅仅须要编写简单的代码对不同的事件做对应的逻辑处理就能够实现利用WebSocket进行实时通信了。

Tomcat7为WebSocket提供了3个例程(echo、chat及snake),下面就当中的echo和chat分别做一简要解析。

1.      echo例程

echo例程主要演示下面功能:client连接服务端、client向服务端发送消息、服务端收到client发送的消息后将其原样返回给client、client收到消息后将其显示在网页之上。

在client页面选择streams或messages作为“Connectusing”,然后点击“Connect”button,能够在右側窗体看到WebSocket连接打开的消息。随后点击“Echo
message”button,client将向服务端发送一条消息,在右側窗体,能够看到,消息发出的后的瞬间,client已经收到了服务端原样返回的消息。

client页面及执行效果截图如图 2所看到的。

图2

client实现上述功能的核心脚本例如以下,其关键点通过凝视的形式加以说明:

<script type="text/javascript">
        var ws = null;
        // 界面元素可用性控制
        function setConnected(connected) {
            document.getElementById(‘connect‘).disabled = connected;
            document.getElementById(‘disconnect‘).disabled = !connected;
            document.getElementById(‘echo‘).disabled = !connected;
        }

        function connect() {
            // 取得WebSocket连接入口(WebSocket URI)
            var target = document.getElementById(‘target‘).value;
            if (target == ‘‘) {
                alert(‘Please select server side connection implementation.‘);
                return;
            }
            // 创建WebSocket
            if (‘WebSocket‘ in window) {
                ws = new WebSocket(target);
            } else if (‘MozWebSocket‘ in window) {
                ws = new MozWebSocket(target);
            } else {
                alert(‘WebSocket is not supported by this browser.‘);
                return;
            }
            // 定义Open事件处理函数
            ws.onopen = function () {
                setConnected(true);
                log(‘Info: WebSocket connection opened.‘);
            };
            // 定义Message事件处理函数(收取服务端消息并处理)
            ws.onmessage = function (event) {
                log(‘Received: ‘ + event.data);
            };
            // 定义Close事件处理函数
            ws.onclose = function () {
                setConnected(false);
                log(‘Info: WebSocket connection closed.‘);
            };
        }
        // 关闭WebSocket连接
        function disconnect() {
            if (ws != null) {
                ws.close();
                ws = null;
            }
            setConnected(false);
        }

        function echo() {
            if (ws != null) {
                var message = document.getElementById(‘message‘).value;
                log(‘Sent: ‘ + message);
                // 向服务端发送消息
                ws.send(message);
            } else {
                alert(‘WebSocket connection not established, please connect.‘);
            }
        }
	    // 生成WebSocket URI
        function updateTarget(target) {
            if (window.location.protocol == ‘http:‘) {
                document.getElementById(‘target‘).value =
‘ws://‘ + window.location.host + target;
            } else {
                document.getElementById(‘target‘).value =
‘wss://‘ + window.location.host + target;
            }
        }
        // 在界面显示log及消息
        function log(message) {
            var console = document.getElementById(‘console‘);
            var p = document.createElement(‘p‘);
            p.style.wordWrap = ‘break-word‘;
            p.appendChild(document.createTextNode(message));
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        }
    </script>

注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\echo.html

以上client能够依据“Connectusing”的不同选择连接不同的服务端WebSocket。比如messages选项相应的服务端代码例如以下,其核心逻辑是在收到client发来的消息后马上将其发回client。

public class EchoMessage extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    private volatile int byteBufSize;
    private volatile int charBufSize;

    @Override
    public void init() throws ServletException {
        super.init();
        byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);
        charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);
    }

    public int getInitParameterIntValue(String name, int defaultValue) {
        String val = this.getInitParameter(name);
        int result;
        if(null != val) {
            try {
                result = Integer.parseInt(val);
            }catch (Exception x) {
                result = defaultValue;
            }
        } else {
            result = defaultValue;
        }

        return result;
    }

    // 创建Inbound实例,WebSocketServlet子类必须实现的方法
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        return new EchoMessageInbound(byteBufSize,charBufSize);
    }
    // MessageInbound子类,完毕收到WebSocket消息后的逻辑处理
    private static final class EchoMessageInbound extends MessageInbound {
        public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {
            super();
            setByteBufferMaxSize(byteBufferMaxSize);
            setCharBufferMaxSize(charBufferMaxSize);
        }
        //  二进制消息响应
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            getWsOutbound().writeBinaryMessage(message);
        }
        // 文本消息响应
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // 将收到的消息发回client
            getWsOutbound().writeTextMessage(message);
        }
    }
}

注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\EchoMessage.java。

2.      chat例程

chat例程实现了通过网页进行群聊的功能。每一个打开的聊天网页都能够收到全部在线者发出的消息,同一时候,每一个在线者也都能够(也仅仅能够)向其他全部人发送消息。也就是说,chat实例演示了怎样通过WebSocket实现对全部在线client的广播。

图3

chat例程client核心代码例如以下,能够看到事实上现方式与echo例程形式上稍有变化,本质依然是对WebSocket事件进行响应与处理。

<script type="text/javascript">
        var Chat = {};

        Chat.socket = null;

        Chat.connect = (function(host) {
            // 创建WebSocket
            if (‘WebSocket‘ in window) {
                Chat.socket = new WebSocket(host);
            } else if (‘MozWebSocket‘ in window) {
                Chat.socket = new MozWebSocket(host);
            } else {
                Console.log(‘Error: WebSocket is not supported by this browser.‘);
                return;
            }
            // 定义Open事件处理函数
            Chat.socket.onopen = function () {
                Console.log(‘Info: WebSocket connection opened.‘);
                document.getElementById(‘chat‘).onkeydown = function(event) {
                    if (event.keyCode == 13) {
                        Chat.sendMessage();
                    }
                };
            };
            // 定义Close事件处理函数
            Chat.socket.onclose = function () {
                document.getElementById(‘chat‘).onkeydown = null;
                Console.log(‘Info: WebSocket closed.‘);
            };
            // 定义Message事件处理函数
            Chat.socket.onmessage = function (message) {
                Console.log(message.data);
            };
        });

        Chat.initialize = function() {
            if (window.location.protocol == ‘http:‘) {
                Chat.connect(‘ws://‘ +
window.location.host + ‘/examples/websocket/chat‘);
            } else {
                Chat.connect(‘wss://‘ +
window.location.host + ‘/examples/websocket/chat‘);
            }
        };
		// 发送消息至服务端
        Chat.sendMessage = (function() {
            var message = document.getElementById(‘chat‘).value;
            if (message != ‘‘) {
                Chat.socket.send(message);
                document.getElementById(‘chat‘).value = ‘‘;
            }
        });

        var Console = {};

        Console.log = (function(message) {
            var console = document.getElementById(‘console‘);
            var p = document.createElement(‘p‘);
            p.style.wordWrap = ‘break-word‘;
            p.innerHTML = message;
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        });

        Chat.initialize();

    </script>

注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\chat.html

chat例程服务端代码例如以下:

public class ChatWebSocketServlet extends WebSocketServlet {

    private static final long serialVersionUID = 1L;

    private static final String GUEST_PREFIX = "Guest";

    private final AtomicInteger connectionIds = new AtomicInteger(0);
    private final Set<ChatMessageInbound> connections =
            new CopyOnWriteArraySet<ChatMessageInbound>();
    // 创建Inbound实例,WebSocketServlet子类必须实现的方法
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        return new ChatMessageInbound(connectionIds.incrementAndGet());
    }
    // MessageInbound子类,完毕收到WebSocket消息后的逻辑处理
    private final class ChatMessageInbound extends MessageInbound {

        private final String nickname;

        private ChatMessageInbound(int id) {
            this.nickname = GUEST_PREFIX + id;
        }
        // Open事件
        @Override
        protected void onOpen(WsOutbound outbound) {
            connections.add(this);
            String message = String.format("* %s %s",
                    nickname, "has joined.");
            broadcast(message);
        }
        // Close事件
        @Override
        protected void onClose(int status) {
            connections.remove(this);
            String message = String.format("* %s %s",
                    nickname, "has disconnected.");
            broadcast(message);
        }
		// 二进制消息事件
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            throw new UnsupportedOperationException(
                    "Binary message not supported.");
        }
		// 文本消息事件
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // Never trust the client
            String filteredMessage = String.format("%s: %s",
                    nickname, HTMLFilter.filter(message.toString()));
            broadcast(filteredMessage);
        }
		// 向全部已连接的客户端发送文本消息(广播)
        private void broadcast(String message) {
            for (ChatMessageInbound connection : connections) {
                try {
                    CharBuffer buffer = CharBuffer.wrap(message);
                    connection.getWsOutbound().writeTextMessage(buffer);
                } catch (IOException ignore) {
                    // Ignore
                }
            }
        }
    }

注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\ChatWebSocketServlet.java。

通过上述例程能够看到WebSocket广播实际上是通过遍历全部连接并通过每一个连接向对应的client发送消息实现的。

四、            WebSocket实战

实时向在线用户推送通知是一个WebSocket应用的简单场景,后台提交通知信息以后,所在在线用户均应非常快收到这个通知。通过上述例程了解WebSocket后,能够尝试编写一个实现这个需求的WebSocket应用。

首先编写一个用户的Sample页面,该页面没有实质的内容,可是在收到后台发出的通知时要在右下角通过弹窗显示通知的内容。其代码例如以下:

<!DOCTYPE html>
<html>
<head>
    <title>Receive Message</title>
    <style type="text/css">
        #winpop { width:200px; height:0px;
            position:absolute;
            right:0; bottom:0;
            border:1px solid #999999;
            margin:0;
            padding:1px;
            overflow:hidden;
            display:none;
            background:#FFFFFF}
        #winpop .con { width:100%; height:80px;
            line-height:80px;
            font-weight:bold;
            font-size:12px;
            color:#FF0000;
            text-align:center}
    </style>
    <script type="text/javascript">
        // 弹窗相关
        function tips_pop(){
            var MsgPop=document.getElementById("winpop");
            var popH=parseInt(MsgPop.style.height);

            if(isNaN(popH)) {
                popH = 0;
            }

            if (popH==0){
                MsgPop.style.display="block";
                show=setInterval("changeH(‘up‘)",100);
            }
            else {
                hide=setInterval("changeH(‘down‘)",100);
            }
        }

        function changeH(str) {
            var MsgPop=document.getElementById("winpop");
            var popH=parseInt(MsgPop.style.height);

            if(isNaN(popH)) {
                popH = 0;
            }

            if(str=="up"){
                if (popH<=100){
                    MsgPop.style.height=(popH+4).toString()+"px";
                }
                else{
                    clearInterval(show);
                    setTimeout("tips_pop()", 5000);
                }
            }
            if(str=="down"){
                if (popH>=4){
                    MsgPop.style.height=(popH-4).toString()+"px";
                }
                else{
                    clearInterval(hide);
                    MsgPop.style.display="none";
                }
            }
        }

        // WebSocket相关
        var ws = null;

        function connect() {
            var target = ‘ws://‘ + window.location.host
                + "/test/NotifyWebSocketServlet";

            if (‘WebSocket‘ in window) {
                ws = new WebSocket(target);
            } else if (‘MozWebSocket‘ in window) {
                ws = new MozWebSocket(target);
            } else {
                alert(‘WebSocket is not supported by this browser.‘);
                return;
            }

            ws.onopen = function () {
                 document.getElementById(‘msg‘).innerHTML =
                    "WebSocket has opened, Waiting message.......";
            };

            ws.onmessage = function (event) {
                document.getElementById(‘infomsg‘).innerHTML = event.data;
                tips_pop();
            };

            ws.onclose = function () {
                document.getElementById(‘msg‘).innerHTML = "WebSocket has closed";
            };
        }

        function disconnect() {
            if (ws != null) {
                ws.close();
                ws = null;
            }
        }       

        connect();
    </script>
</head>
<body>
    <h1 align="center" id="msg">Try to connect websocket.</h1>
    <div id="winpop">
        <div class="con" id="infomsg"></div>
    </div>
</body>
</html>

当用户界面打开时,它会尝试通过/test/NotifyWebSocketServlet建立与server的WebSocket连接,而NotifyWebSocketServlet的实现代码则例如以下:

package net.yanzhijun.example;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;

public class NotifyWebSocketServlet extends WebSocketServlet {

    private static final long serialVersionUID = 1L;    

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        ServletContext application = this.getServletContext();
        return new NofityMessageInbound(application);
    }
}

与Tomcat给出的演示样例代码不同的是,在NotifyWebSocketServlet中并未将继承于MessageInbound的NofityMessageInbound作为一个内嵌类。前述演示样例代码中发送消息和接收消息都是在同一组client页面和服务端响应Servlet间进行的,而当前须要实现是在一个页面中提交通知,而在其他用户的页面上显示通知信息,因此须要将全部client与服务端的连接存储一个全局域中,故而NofityMessageInbound将不仅仅在当前Servlet中被使用,所以有必要将其独立出来。

NofityMessageInbound的完整代码例如以下:

package net.yanzhijun.example;

import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.servlet.ServletContext;

import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.MessageInbound;

public class NofityMessageInbound extends MessageInbound {
    private ServletContext application;
    private Set<NofityMessageInbound> connections = null;

    public NofityMessageInbound(ServletContext application) {
        this.application = application;
        connections =
            (Set<NofityMessageInbound>)application.getAttribute("connections");
        if(connections == null) {
            connections =
                new CopyOnWriteArraySet<NofityMessageInbound>();
        }
    }

    @Override
    protected void onOpen(WsOutbound outbound) {
        connections.add(this);
        application.setAttribute("connections", connections);
    }

    @Override
    protected void onClose(int status) {
        connections.remove(this);
        application.setAttribute("connections", connections);
    }

    @Override
    protected void onBinaryMessage(ByteBuffer message) throws IOException {
        throw new UnsupportedOperationException(
                "message not supported.");
    }

    @Override
    protected void onTextMessage(CharBuffer message) throws IOException {
        throw new UnsupportedOperationException(
                "message not supported.");
    }
}

后台发送通知的页面实现的相当简单,仅仅是一个表单提交一条通知信息。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>PushMessage</title>
    </head>
    <body>
        <h1 align="Center">Online Broadcast</h1>
        <form method="post" action="PushMessageServlet">
            <p>Message:<br/>
                <textarea name="message" rows="5" cols="30"></textarea>
            </p>
            <p><input type="submit" value="Send">  
                <input type="reset" value="Reset">
            </p>
        </form>
    </body>
</html>

接收提交通知的Servlet是PushMessageServlet,它在收到后台提交的通知后,就通过全部用户的WebSocket连接将通知发送出去。

package net.yanzhijun.example;

import java.io.PrintWriter;
import java.nio.CharBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PushMessageServlet extends HttpServlet {
     private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException {
            doPost(request, response);
        }

    @Override
    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException {

        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();

        String message = request.getParameter("message");
        if(message == null || message.length() == 0) {
            out.println("The message is empty!");
            return;
        }

        // 广播消息
        broadcast(message);

        out.println("Send success!");
    }

    // 将參数中的消息发送至全部在线client
    private void broadcast(String message) {
        ServletContext application=this.getServletContext();
        Set<NofityMessageInbound> connections =
(Set<NofityMessageInbound>)application.getAttribute("connections");
        if(connections == null){
            return;
        }

        for (NofityMessageInbound connection : connections) {
            try {
                CharBuffer buffer = CharBuffer.wrap(message);
                connection.getWsOutbound().writeTextMessage(buffer);
            } catch (IOException ignore) {
                // Ignore
            }
        }
    }
}

编译相关文件并完毕部署,尝试在后台发送消息,能够看到用户界面右下角出现的弹窗中显示了后台所提交的内容。

图4

五、            WebSocket总结

通过以上例程和实例能够看出,从开发角度使用WebSocket相当easy,基本仅仅须要创建WebSocket实例并对关心的事件进行处理就能够了;从应用角度WebSocket提供了优异的性能,图 5是来自websocket.org的性能測试图表(http://www.websocket.org/quantum.html),能够看到当并发和负载添加时轮询与WebSocket的差异。

图5

(以上例程client在IE10.0和Chrom28.0下測试通过。)

欢迎訪问梦断酒醒的博客http://blog.csdn.net/ishallwn

【WebSocket初探

时间: 2024-10-11 07:02:16

【WebSocket初探 】的相关文章

Spring WebSocket初探2 (Spring WebSocket入门教程)&lt;转&gt;

See more: Spring WebSocket reference整个例子属于WiseMenuFrameWork的一部分,可以将整个项目Clone下来,如果朋友们有需求,我可以整理一个独立的demo出来. 接上一篇:Spring WebSocket初探1 (Spring WebSocket入门教程) WebSocket前端准备 前端我们需要用到两个js文件:sockjs.js和stomp.js SockJS:SockJS 是一个浏览器上运行的 JavaScript 库,如果浏览器不支持 W

websocket初探

背景: 前段时间有个小需求:进入页面后需要根据数据库状态来自行跳转.去查了查,大部分都是用轮询做.自己觉得其实轮询也不是不可以,但是总觉比较尴尬. 因为本身的模式应该是以状态为主体,页面部分为客体,当状态发生变化的时候,主动把信号传给页面,然后页面跟着做跳转.如果用轮询,就变成了以页面为 主体,状态为客体,页面不断的给请求,如果状态变了,然后自己做跳转.然后看到了websocket,不过说实话不懂这东西.socket编程一直都有点模糊.于是 稍微看看这到底是神马. 主题: 去github上查了一

Spring WebSocket初探1 (Spring WebSocket入门教程)&lt;转&gt;

See more: Spring WebSocket reference整个例子属于WiseMenuFrameWork的一部分,可以将整个项目Clone下来,如果朋友们有需求,我可以整理一个独立的demo出来. WebSocket是html5带来的一项重大的特性,使得浏览器与服务端之间真正长连接交互成为了可能,这篇文章会带领大家窥探一下Spring 对WebSocket的支持及使用. 1. 基础环境 快速搭建Spring框架,我们使用Spring boot,这里先不讨论SpringBoot,只知

初探和实现websocket心跳重连

心跳重连缘由 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开, 而浏览器不会执行websocket 的 onclose方法,我们无法知道是否断开连接,也就无法进行重连操作. 如果当前发送websocket数据到后端,一旦请求超时,onclose便会执行,这时候便可进行绑定好的重连操作. 因此websocket心跳重连就应运而生. 如何实现 在websocket实例化的时候,我们会绑定一些事件: var ws =

WebSocket集成XMPP网页即时通讯1:Java Web Project服务端/客户端Jetty9开发初探

Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏.在线证券.设备监控.新闻在线播报.RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了.所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来说也是一个难题.在 WebSo

初探websocket

WebSocket是 HTML5开始提供的一种在单个 TCP连接上进行全双工通讯的协议. WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据. 在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道.两者之间就直接可以数据互相传送. WebSocket协议本质上是一个基于 TCP的应用层协议. 现在,很多网站为了实现推送技术,所用的技术都是 Ajax轮询.轮询是在特定的的时间间隔(如每1秒

国外物联网平台初探(五):Exosite Murano

国外物联网平台初探(五)--Exosite Murano 马智 ? 定位 Murano是一个基于云的IoT软件平台,提供安全.可扩展的基础设施,支持端到端的生态系统,帮助客户安全.可扩展地开发.部署和管理应用.服务以及联网产品. ? 功能 Murano平台简化了整个IoT技术栈,可视为集成在一起的多个云软件层. Murano提供IoT基础设施.开发环境和功能集成,包括设备连接.产品管理.数据路由.服务集成(如data store/告警/第三方分析平台).应用开放API.用户认证/角色/权限和应用

初探oVirt-重构-Self_Hosted_Engine

日期:2015/9/25 - 2015/9/29 time 19:07 主机:n72, n73, n86, n93, vm220 目的:初探oVirt-重构-Self_Hosted_Engine 操作内容: 一.基础环境 1.本次测试环境使用4台物理机来测试:node72, node73, n86, n93 CPU:    Intel(R) Xeon(R) CPU E5-2603 v2 @ 1.80GHz 内存:   32G     硬盘:   系统盘sda + 数据盘sdb ovirt-eng

国内物联网平台初探(五):机智云IoT物联网云服务平台及智能硬件自助开发平台

国内物联网平台初探(五)——机智云IoT物联网云服务平台及智能硬件自助开发平台 马智 平台定位 机智云平台是致力于物联网.智能硬件云服务的开放平台.平台提供了从定义产品.设备端开发调试.应用开发.产测.运营管理等覆盖智能硬件接入到运营管理全生命周期服务的能力. 机智云平台为开发者提供了自助式智能硬件开发工具与开放的云端服务.通过傻瓜化的工具.不断增强的SDK与API服务能力最大限度降低了物联网硬件开发的技术门槛,降低研发成本,提升开发者的产品投产速度,帮助开发者进行硬件智能化升级,更好的连接.服