`

HTML5中的服务器‘推送’技术 -WebSocket

阅读更多
HTML5中的服务器‘推送’技术 -WebSocket
转帖:http://www.developersky.net/thread-81-1-1.html

除了Server-Sent Event之外,即将到来的HTML5标准还包含了WebSockets。WebSocket使得我们可以建立双向的通信通道。和Server-Sent Event相反,WebSocket协议不是建立在HTTP之上的。但是WebSocket协议订立了HTTP握手的行为来将已经存在的HTTP连接转换为WebSocket连接。WebSocket没有试图在HTTP之上模拟server推送的通道,而是直接在TCP之上定义了帧协议,因此WebSocket能够支持双向的通信。
和server-Sent Event规范相同,WebSocket定义了API和相应的协议。WebSocket API规范中包含一个新的HTML元素:WebSocket。下面的代码就是一个使用WebSocket的HTML的例子:

<html>
   <head>
     <mce:script type='text/javascript'><!--
        var ws = new WebSocket('ws://localhost:8876/Channel', 'mySubprotocol.example.org');
        ws.onmessage = function (message) {
          var messages = document.getElementById('messages');
          messages.innerHTML += "<br>[in] " + message.data;
        };
       
        sendmsg = function() {
          var message = document.getElementById('message_to_send').value
          document.getElementById('message_to_send').value = ''
          ws.send(message);
          var messages = document.getElementById('messages');
          messages.innerHTML += "<br>[out] " + message;
        };
    
// --></mce:script>
  </head>
  <body>
     <form>
       <input type="text" id="message_to_send" name="msg"/>
       <input type="button" name="btn" id="sendMsg" value="Send" onclick="javascript:sendmsg();">
       <div id="messages"></div>
     </form>
  </body>
</html>

当创建一个WebSocket实例的时候,相应的WebSocket连接就会被建立。构造函数需要两个参数(第二个参数是可选的)。第一个参数是WebSocketURL,该参数定义了要连接的URL。WebSocketURL以ws或wss开头。ws开头的是普通的WebSocket连接,wss开头的是安全的WebSocket连接(类似https)。第二个参数是要使用的子协议,该参数是可选的。
我们可以定义WebSocket实例的onMessage处理函数,每次收到消息的时候,该处理函数都会被调用。如果要发送消息到服务器端,可以通过WebSocket的send()方法发送消息。
当一个新的WebSocket实例被创建的时候,首先底层的user agent会建立一个普通的到指定URL的HTTP(S)连接,然后对该连接进行升级(upgrade)。HTTP规范在消息头中定义了upgrade域来进行该操作。Upgrade头提供了简单的机制来将HTTP协议转换为其他的不兼容的协议。WebSocket利用了HTTP协议的这个能力来讲新创建的HTTP连接转换为WebSocket连接。同时添加了WebSocket-Protocal来制定要使用的子协议。
下面是一个Upgrade的Request和Response消息的例子。

REQUEST:
GET /Channel HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: myServer:8876
Origin: http://myServer:8876
WebSocket-Protocol: mySubprotocol.example.org


RESPONSE:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://myServer:8876
WebSocket-Location: ws://myServer:8876/Channel
WebSocket-Protocol: mySubprotocol.example.org

当收到HTTP response消息之后,只有WebSocket帧能够通过该连接传送,所有的数据都会根据WebSocket协议进行转换。WebSocket帧能够在任何时候向任何方向发送。WebSocket协议定义了两种类型的帧:文本帧(text frame)和二进制帧(binary frame)。文本帧以字节0x00开头,以字节0xFF结尾。当中的文本内容要转换成UTF8编码。所以为了打包,文本帧需要添加两个额外的字节。下面是两个文本帧的例子:

Text frame of "GetDate":
0x00 0x47 0x65 0x74 0x44 0x61 0x74 0x65 0xFF

Text frame of "Sat Mar 13 14:00:25 CET 2010":
0x00 0x53 0x61 0x74 0x20 0x4D 0x61 0x72 0x20 0x31
0x33 0x20 0x31 0x34 0x3A 0x30 0x30 0x3A 0x32 0x35
0x20 0x43 0x45 0x54 0x20 0x32 0x30 0x31 0x30 0xFF

如果要传送二进制数据,就需要使用二进制帧。二进制帧以字节0x80开始。和文本帧相反,二进制帧没有结束标志。在二进制帧的开始标识字节(0x80)之后就是长度字节。长度字节的字节数是不固定的,根据需要来决定。下面是两个例子,第一个例子中需要传递的数据量很小,因此长度字节就只有一个字节;第二个例子中需要传递的数据量比较大,就使用了2个字节作为长度字节。

binary frame of 0x00 0x44:
0x80 0x02 0x00 0x44

binary frame of 0x30 0x31 0x32 0x33  0x34  0x35 […] 0x39 (1000 bytes):
0x80 0x87 0x68 0x30 0x31 0x32 0x33 0x34 0x35 […] 0x39

因为JavaScript不能操作字节数组形式的二进制数据,因此二进制帧目前无法被JavaScript使用。除了文本帧和二进制帧之外,WebSocket协议将来还有可能引入新的类型的帧格式。WebSocket帧在设计的时候就考虑了支持新的帧类型。
WebSocket的连接可以在任何时候关闭,不需要额外的‘结束连接’字节或帧。
管理WebSocket的额外开销是很小的。如Bayeux和BOSH这样的Comet协议是建立在HTTP协议之上的,这就迫使这些协议要实现复杂的会话和连接的管理。而WebSocket是建立在TCP协议之上的,不会碰到这些由于HTTP协议的局限性引起的麻烦。
另一方面,WebSocket基本上没有实现可靠性的功能。它既没有包括重建连接的处理,也不支持向Server-Sent Event那样的保证消息成功传递的机制。而且,由于WebSocket不是基于HTTP协议的,因此也无法利用HTTP协议内建的可靠性的特性。例如HTTP协议支持当网络故障是的自动重试功能(一个Get方法应该不会改变任何服务器端的资源的状态,因此我们可以重复的执行Get方法而没有任何的副作用。当网络故障的时候,浏览器或HttpClients可以自动重新执行Get方法)。
由于WebSocket没有这些功能,因此在应用程序(或者说子协议)层面上我们就需要实现可靠性的功能,包括发送“维持通信”消息来避免代理服务器在一段时间没有消息之后关闭连接。另外,在多个页面之间共享WebSocket通常会带来麻烦。和Server-Sent Event不同,WebSocket会包含一个难以共享的上游的管道。例如,并发的读写操作必须要同步,这并不是一个简单的任务。因此当使用WebSocket的时候,‘每个服务器一个连接’必须要仔细考虑。
Web浏览器限制浏览器端的编程语言(如JavaScript)连接到其他的服务器,因此web页面上的WebSocket只能连接和当前页面在同一个域中的服务器。对于独立的WebSocket客户端则没有这个限制。如下面的例子中我们通过Java客户端来使用WebSocket:

class MyWebSocketHandler implements IWebSocketHandler {

  public void onConnect(IWebSocketConnection wsCon) throws IOException {
               
  }

  public void onMessage(IWebSocketConnection wsCon) throws IOException {
    IWebMessage msg = wsCon.readMessage();
    System.out.println(msg.toString());
  }
           
  public void onDisconnect(IWebSocketConnection wsCon) throws IOException {
               
  }
}

       
MyWebSocketHandler hdl = new MyWebSocketHandler ();
IWebSocketConnection wsCon = httpClient.openWebSocketConnection(
    "ws://myServer:8876/WebSocketsExample",
    "mySubprotocol.example.org", hdl);
       
wsCon.writeMessage(new TextMessage("GetDate"));
// ...

下面是一个WebSocket服务器实现的简单例子。服务器要实现两个接口:IHttpRequestHandler和IWebSocketHandler。IHttpRequestHandler接口用于处理普通的HTTP请求,IWebSocketHandler据诶和用于处理WebSocket连接和消息。当一个标准的HTTP请求到来的时候(不包含upgrade请求),IHttpRequestHandler的onRequest()方法会被调用。如果客户端打开WebSocket,服务器会收到HTTP upgrade请求,IWebSocketHandler的onConnect()方法会被调用。每次收到WebSocket的消息,IWebSocketHandler的onMessage()方法都会被调用。
在onConnect()方法中,我们可以检查一些先决条件。例如检查要求的子协议是否被支持,下面的例子中如果要求的子协议不被支持,则会返回一个错误状态。下面的例子啊红我们还驾车了请求头中的origin字段。Origin字段是HTTP Origin Header RFC(还是草案)定义的,该字段由浏览器自动设置。

class ServerHandler implements IHttpRequestHandler, IWebSocketHandler {
           
           
  // IHttpRequestHandler method
  public void onRequest(IHttpExchange exchange) throws IOException {
  String requestURI = exchange.getRequest().getRequestURI();
               
  if (requestURI.equals("/WebSocketsExample")) {
    sendWebSocketPage(exchange, requestURI);
                   
  } else {
    exchange.sendError(404);
  }
}
           
           
  private void sendWebSocketPage(IHttpExchange exchange, String uri)
          throws IOException {
    String page = "<html>\r\n " +
                  "  <head>\r\n" +
                  "     <mce:script type='text/javascript'><!--
\r\n" +
                  "        var ws = new WebSocket('ws://" +
                           exchange.getRequest().getHost() + "/Channel',
                           'mySubprotocol.example.org');\r\n" +
                  "        ws.onmessage = function (message) {\r\n" +
                  "          var messages = document.getElementById('messages');\r\n" +
                  "          messages.innerHTML += \"<br>[in] \" +
                             message.data;\r\n"+
                  "        };\r\n" +
                  "        \r\n" +
                  "        sendmsg = function() {\r\n" +
                  "          var message = document.getElementById
                             ('message_to_send').value\r\n" +
                  "          document.getElementById('message_to_send').value = ''\r\n" +
                  "          ws.send(message);\r\n" +
                  "          var messages = document.getElementById('messages');\r\n" +
                  "          messages.innerHTML += \"<br>[out] \" + message;\r\n"+
                  "        };\r\n" +
                  "    
// --></mce:script>\r\n" +
                  "  </head>\r\n" +
                  "  <body>\r\n" +
                  "     <form>\r\n" +
                  "       <input type=\"text\" id=\"message_to_send\"
                          name=\"msg\"/>\r\n" +
                  "       <input type=\"button\" name=\"btn\" id=\"sendMsg\"
                          value=\"Send\" onclick=\"javascript:sendmsg();\">\r\n" +
                  "       <div id=\"messages\"></div>\r\n" +
                  "     </form>\r\n" +
                  "  </body>\r\n" +
                  "</html>\r\n ";
               
    exchange.send(new HttpResponse(200, "text/html", page));
  }

           
           
  // IWebSocketHandler method
  public void onConnect(IWebSocketConnection webStream) throws IOException, BadMessageException {
    IHttpRequestHeader header = webStream.getUpgradeRequestHeader();

    // check origin header
    String origin = header.getHeader("Origin");
    if (!isAllowed(origin)) {
      throw new BadMessageException(403);
    }
               
    // check the subprotocol 
    String subprotocol = header.getHeader("WebSocket-Protocol", "");
    if (!subprotocol.equalsIgnoreCase("mySubprotocol.example.org")) {
      throw new BadMessageException(403);
    }
  }

  private boolean isAllowed(String origin) {
    // check the origin
    // ...
    return true;
  }
           
           
  // IWebSocketHandler
  public void onMessage(IWebSocketConnection webStream) throws IOException {
    WebSocketMessage msg = webStream.readMessage();
    if (msg.toString().equalsIgnoreCase("GetDate")) {
      webStream.writeMessage(new TextMessage(new Date().toString()));
    } else {
      webStream.writeMessage(new TextMessage(
              "unknown command (supported: GetDate)"));
    }
  }
           
  // IWebSocketHandler
  public void onDisconnect(IWebSocketConnection webStream)
          throws IOException {  }

}
       
XHttpServer server = new XHttpServer(8876, new ServerHandler());
server.start();

上面的例子中,我们会使用内部的白名单来检查origin header,并拒绝我们不期望的请求。这样,我们可以防止某些攻击者将公共页面上的JavaScript代码拷贝并添加到他们自己的页面中。这种情况下,浏览器会将origin header设置为攻击者自己的页面所在的域,服务器处理升级请求的时候就会拒绝该请求。这个技术用来防范Cross-Site Request攻击。Origin header规范和WebSocket协议规范是相互独立的,但是WebSocket协议定义了WebSocket-Origin header,该字段必须被包含在WebSocket升级请求中。

因为WebSocket连接是有HTTP连接升级而来,因此WebSocket协议也可以和HTTP代理服务器一起工作。浏览器总是和代理服务器通信,代理服务器将HTTP request和response进行转发。当浏览器使用HTTP代理并打开一个WebSocket的时候,首先浏览器会打开一个到代理服务器的通道。通过发送HTTP/1.1连接请求,浏览器要求HTTP代理创建一个到被代理的服务器(WebSocket服务器)的TCP连接。当连接建立以后,HTTP代理服务器的功能就被缩小为到WebSocket服务器的TCP代理。使用这个被代理的连接,浏览器发送WebSocket升级请求到WebSocket服务器。下面的列表描述了整个过程。

1. REQUEST:
CONNECT myServer:8876 HTTP/1.1
Host: myServer:8876
User-Agent: xLightweb/2.12-HTML5Preview6
Proxy-Connection: keep-alive


1. RESPONSE:
HTTP/1.1 200 Connection established
Proxy-agent: myProxy


2. REQUEST:
GET /Channel HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: myServer:8876
Origin: http://myServer:8876
WebSocket-Protocol: mySubprotocol.example.org


2. RESPONSE:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://myServer:8876
WebSocket-Location: ws://myServer:8876/Channel
WebSocket-Protocol: mySubprotocol.example.org 

转帖:http://www.developersky.net/thread-81-1-1.html

分享到:
评论
1 楼 itusername 2011-11-11  
请教一下,诸如 :IHttpRequestHandler,IWebSocketHandler,IHttpExchange之类的是自己写的借口还是支持包?有点不太明白!能详细说明下么?方便的话咱们加QQ探讨一下 583772189

相关推荐

    spring-boot-starter-websocket.zip

    5. **集成WebSocket到Controller**:在需要实时推送的业务逻辑中,使用WebSocketTemplate或WebSocketSession对象与客户端进行交互。 6. **前端集成**:在JavaScript中使用WebSocket API或STOMP库(如stomp.js)与...

    java-websocket jar包

    WebSocket协议是HTML5规范的一部分,它允许客户端和服务器之间建立持久性的连接,从而实现数据的双向推送,极大地提高了实时性。在这个场景下,`java-websocket` jar包被设计用于Java和Android平台,方便开发者在...

    em-websocket-master

    【标题】"em-websocket-master" 是一个与Websocket相关的开源项目仓库的主分支名,通常在Git版本控制系统中使用。这个项目很可能是一个基于EventMachine的Websocket服务器实现,EventMachine是一个C++编写的高性能...

    springboot-websocket-demo.zip

    【描述】"springboot-websocket-demo"项目是一个教学或实践用的实例,它展示了如何在基于Spring Boot的Java应用程序中启用和使用WebSocket技术。WebSocket是一种在客户端和服务器之间建立长连接的协议,允许双方实时...

    Java-WebSocket源码

    WebSocket协议是HTML5标准的一部分,它提供了在客户端和服务器之间建立长连接的能力,从而实现双向通信,即服务器可以主动向客户端推送数据,而不仅仅是客户端请求服务器响应的传统模式。 WebSocket API设计简洁,...

    springMvc-websocket-demo

    而WebSocket则是一种在客户端和服务器之间建立长时间连接的协议,允许双向通信,即服务器可以主动向客户端推送数据。将WebSocket与Spring MVC集成,可以创建实时、低延迟的应用,例如聊天应用、实时股票更新或在线...

    jquery-websocket

    - **实时图表**:在股票、天气等实时数据展示中,WebSocket可以用来接收服务器推送的新数据,jQuery更新图表以反映这些变化。 ### 压缩包内容 `jquery-websocket-master`可能是包含一个jQuery WebSocket实现的项目...

    基础知识 - WebSocket

    服务器收到请求后,会回应一个101 Switching Protocols状态码,确认握手成功,并在响应中返回Sec-WebSocket-Accept头,该头的值是基于Sec-WebSocket-Key计算的。 2. **连接建立**:一旦握手完成,客户端和服务器之间...

    SpringWebSocket-master.zip

    SpringWebSocket-master.zip是一个包含Spring Boot应用的示例项目,展示了如何使用WebSocket技术和STOMP协议实现前后端通信。WebSocket是一种在单个TCP连接上进行全双工通信的协议,相较于传统的HTTP请求,它允许...

    spring-websocket实时统计报表示例

    Spring WebSocket是一个强大的功能,允许服务器向客户端推送数据,而不仅仅是响应客户端请求,这对于实时应用非常有用。Highcharts则是一个流行的JavaScript库,用于生成交互式图表和可视化数据。 首先,我们需要在...

    boot-websocket-study.rar

    客户端则通过JavaScript的WebSocket对象建立连接,使用`send()`方法发送消息,监听`onmessage`事件接收服务器推送的消息。 总的来说,"boot-websocket-study"项目是一个很好的学习资源,它涵盖了Spring Boot集成...

    springboot-websocket .rar

    WebSocket协议是HTML5引入的一种在单个TCP连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,极大地提高了交互性。在SpringBoot框架下,集成WebSocket可以让开发者轻松构建支持实时通信的应用,比如...

    服务器端-客户端,websocket长连接实现Android消息推送

    在Android应用程序中,WebSocket常用于实现服务器端的消息推送,以实现实时通信功能,比如即时聊天、通知提醒等。 在"服务器端-客户端,WebSocket长连接实现Android消息推送"这个主题中,我们将探讨以下几个关键...

    springboot-websocket例子

    开发者可以通过这个模板向客户端推送消息,简化了服务器向客户端广播的流程。 5. **STOMP over WebSocket**: STOMP(Simple Text Oriented Messaging Protocol)是一种简单易用的消息传输协议,可以与WebSocket...

    websocket消息实时推送

    在这个"websocket消息实时推送"的小案例中,我们关注的是如何利用WebSocket技术来实现实时的数据更新,并在用户执行特定操作后通知页面刷新。 首先,我们要理解WebSocket的基础原理。WebSocket协议基于TCP,它通过...

    服务器推送技术

    源码在服务器推送技术的实现中扮演着关键角色,因为它涉及到网络协议的解析、数据处理和事件驱动编程。开发者通常会使用如Node.js、Java、Python等支持异步I/O的编程语言来编写服务器端代码,以实现高效的推送逻辑。...

    spring-boot-demo-cluster-redis-websocket.zip

    在现代Web开发中,实时通信功能日益重要,Spring Boot作为Java领域中的轻量级框架,结合WebSocket和Redis可以构建出高效的实时推送系统。本项目"spring-boot-demo-cluster-redis-websocket.zip"就是这样一个示例,它...

    webSocket 后端向前端推送消息

    在"webSocket 后端向前端推送消息"这个主题中,我们主要关注的是如何利用WebSocket实现服务器主动向浏览器推送数据的能力,这对于实时性要求高的应用场景,如股票交易、在线聊天、游戏、实时协作工具等,具有极大的...

    springboot-websocket-demo-master.zip

    5. **WebSocket与Redis结合**:在聊天应用中,服务器可以将接收到的消息通过Redis的发布/订阅功能广播给所有在线用户,实现消息的实时推送。这种方式减少了服务器的负担,提高了效率。 6. **聊天室架构**:通常,该...

    phonegap+websocket+tomcat实现推送功能

    总的来说,"phonegap+websocket+tomcat实现移动终端推送功能"项目涉及到的技术栈包括前端的PhoneGap应用开发、WebSocket的JavaScript客户端实现、后端的Tomcat服务器配置以及WebSocket的Java服务端编程。通过这一...

Global site tag (gtag.js) - Google Analytics