欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

WebSockets 简介:将套接字引入网络[转]

shiping1 的头像

WebSockets 简介:将套接字引入网络[转]

mengyaoWoo:

支持的浏览器:OperaIE Safari Firefox Chrome 

问题:低延迟的客户端-服务器和服务器-客户端连接

一直以来,网络在很大程度上都是围绕着所谓 HTTP的请求/响应模式而构建的客户端加载一个网页,然后直到用户点击下一页之前,什么都不会发生。在 2005年左右,AJAX 开始让网络变得更加动态了但所有 HTTP 通信仍然是由客户端控制的,这就需要用户互动或定期轮询,以便从服务器加载新数据。

长期以来存在着各种技术,可让服务器得知有新数据可用时,立即将数据发送到客户端这些技术名目繁多,例如“推送”或 Comet 。最普遍的一种黑客手段是对服务器发起的链接创建假象,这称为长轮询利用长轮询,客户端可打开指向服务器的 HTTP连接,而服务器会一直保持连接打开,直到发送响应。服务器只要实际拥有新数据,就会发送响应(其他技术包括 Flash XHRmultipart 请求和所谓的htmlfiles 长轮询和其他技术非常好用,您在Gmail 聊天等应用中经常使用它们

但是,这些解决方案都存在一个共同的问题:它们带有 HTTP的开销,导致它们不适用于低延迟应用可以想象一下浏览器中的多人第一人称射击游戏,或者其他任何带有即时要素的在线游戏

WebSocket 简介:将套接字引入网络

WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据

只需调用WebSocket 构造函数即可打开WebSocket 连接:

var connection = newWebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

请注意 ws:这是WebSocket 连接的新网址架构对于安全WebSocket 连接还有wss:,就像 https: 用于安全 HTTP 连接一样

立即向连接附加一些事件处理程序可让您知道连接打开收到传入讯息或出现错误的时间。

第二个参数可接受可选子协议,它既可以是字符串,也可以是字符串数组每个字符串都应代表一个子协议名称,而服务器只能接受数组中通过的一个子协议。访问WebSocket 对象的 protocol 属性可确定接受的子协议

子协议名称必须是 IANA 注册表 中的某个注册子协议名称截止 2012 年 2 月,只有一个注册子协议名称 (soap)

// When the connection isopen, send some data to the server

connection.onopen = function() {

 connection.send('Ping'); // Send the message'Ping' to the server

};

// Log errors

connection.onerror = function(error) {

 console.log('WebSocket Error ' + error);

};

// Log messages from theserver

connection.onmessage =function (e) {

 console.log('Server: ' + e.data);

};

与服务器通信

与服务器建立连接后(启动 open 事件时),我们可以开始对连接对象使用send('yourmessage') 方法,向服务器发送数据该方法以前只支持字符串,但根据最新的规范,现在也可以发送二进制讯息了要发送二进制数据,您可以使用Blob 或 ArrayBuffer 对象

// Sending String

connection.send('yourmessage');

 

// Sending canvas ImageDataas ArrayBuffer

var img =canvas_context.getImageData(0, 0, 400, 320);

var binary = newUint8Array(img.data.length);

for (var i = 0; i <img.data.length; i++) {

 binary[i] = img.data[i];

}

connection.send(binary.buffer);

 

// Sending file as Blob

var file =document.querySelector('input[type="file"]').files[0];

connection.send(file);

同样,服务器也可能随时向我们发送讯息只要发生这种情况,就会启动onmessage 回调。该回调接收的是事件对象,而实际的讯息可通过 data 属性进行访问

在最新规范中,WebSocket 也可以接收二进制讯息。接收的二进制帧可以是 Blob或 ArrayBuffer 格式要指定收到的二进制数据的格式,可将WebSocket 对象的binaryType 属性设为“blob”或“arraybuffer”默认格式为“blob”(您不必在发送时校正 binaryType 参数)

// Setting binaryType to acceptreceived binary as either 'blob' or 'arraybuffer'

connection.binaryType ='arraybuffer';

connection.onmessage =function(e) {

 console.log(e.data.byteLength); //ArrayBuffer object if binary

};

WebSocket的另一个新增功能是扩展利用扩展,可以发送压缩 多路复用 帧等。您可以检查 open事件后的WebSocket 对象的extensions 属性,查找服务器所接受的扩展截止 2012年 2 月,还没有官方发布的扩展规范。

// Determining acceptedextensions

console.log(connection.extensions);

作为现代协议,跨源通信已内置在 WebSocket 中虽然您仍然应该确保只与自己信任的客户端和服务器通信,但WebSocket 可实现任何域上多方之间的通信服务器将决定是向所有客户端,还是只向驻留在一组指定域上的客户端提供服务

代理服务器

每一种新技术在出现时,都会伴随着一系列问题WebSocket也不例外,它与大多数公司网络中用于调解 HTTP连接的代理服务器不兼容WebSocket 协议使用 HTTP 升级系统(通常用于 HTTP/SSL)将 HTTP 连接“升级”为 WebSocket 连接某些代理服务器不支持这种升级,并会断开连接因此,即使指定的客户端使用了 WebSocket 协议,可能也无法建立连接。这就使得下一部分的内容更加重要了

立即使用 WebSocket

WebSocket仍是一项新兴技术,并未在所有浏览器中全面实施而在无法使用WebSocket 的情况下,只要通过一些使用了上述某个回调的库,就可以立即使用 WebSocket 了在这一方面使用非常普遍的库是 socket.io ,其中自带了协议的客户端和服务器实施,并包含回调(截止 2012年 2 月,socket.io 还不支持二进制讯息传输)。还有一些商业解决方案,例如 PusherApp ,通过提供向客户端发送 WebSocket 讯息的 HTTP API,可轻松地集成到任何网络环境中由于额外的 HTTP 请求,这些解决方案与纯 WebSocket 相比总是会有额外的开销。

使用 WebSocket 为服务器端应用带来了全新的用法虽然 LAMP 等传统服务器堆栈是围绕 HTTP 请求/响应循环而设计的,但是通常无法很好地处理大量打开的WebSocket 连接要同时维持大量连接处于打开状态,就需要能以低性能开销接收高并发数据的架构此类架构通常是围绕线程或所谓的非阻塞 IO 而设计的。

WebSocket结合hibernate+spring+json 实际经验

     最近一段时间因为需要,要做一个网页游戏在某一段时间需要页面实时刷新。目前做网站普遍的思路都是js轮询的方式。由于是创新式的小项目,同组的好友提议了html5中提到的WebSocket,首先进行了技术调研。目前java方面支持的WebSocket的不算多网上能搜到关于websocket实现框架的非常少。java EE7中加入WebSocket还没有看到。所以以来的包都来自于tomcat的支持。至少需要3个包tomcat-coyote,tomcat-catalina,tomcat-annotations-api,因为tomcat从7.027版本才开始比较好的支持websocket,在tomcat7.027之前的版本中,已经能使用websocket,但是会出现各式各样的问题比如websocket连接后静置几秒钟就断开连接了等等。所以比较好的选择是使用7.027以上的版本。这3个jar包在相应tomcat的lib文件夹下都有。自从研究生期间接触maven后,慢慢了解到maven的强大,在这里不得不感叹一下。因为是个小型的敏捷团队,版本控制是必须的在jar包控制这方面还是想通过maven来控制。就直接去maven中心库搜了搜。果真还是有的。小组讨论之后决定使用tomcat7.039(貌似40已经出了),到此解决了版本控制和jar包配置问题。pom关于tomcat3个jar包如下:

1         <dependency>

2             <groupId>org.apache.tomcat</groupId>

3            <artifactId>tomcat-coyote</artifactId>

4             <version>7.0.39</version>

5         </dependency>

6         <dependency>

7             <groupId>org.apache.tomcat</groupId>

8              <artifactId>tomcat-catalina</artifactId>

9               <version>7.0.39</version>

10        </dependency>

11        <dependency>

12              <groupId>org.apache.tomcat</groupId>

13             <artifactId>tomcat-annotations-api</artifactId>

14              <version>7.0.39</version>

15        </dependency>

接下来是解决架构问题现在在网上能搜到关于websocket的实践很少,基本能搜到的都是websocket架构和非常简单的例子(tomcat自带就有websocket例子),怎么样能将websocket机制运用起来首先基本框架准备使用hibernate+spring mvc结合websocket,但是在实际试验中spring mvc和websocket会有部分有冲突因为到前一段时间Spring Framework 4.0发布的版本中才有了JDK 8的支持和WebSocket编程的支持所以现阶段需要别的方式来实现spring mvc+websocket。简单的解决方案就是写一个工具类来手动获得bean解决spring和websocket支持之后需要解决的websocket的交互方式。websocket最直接的两个方法就是onTextMessage和onBinaryMessage,也就是字节流传输和字符流传输最优方式便是设计一套自己传输协议。通过字节流传输前后台分别解析协议获取交互操作。其次便可在onTextMessage也就是字符流上做文章。引入json便可以很好支持了。

配置websocket的步骤:

1实现一个类继承ContextLoaderListener,并且在web.xml中配置

1 importjavax.servlet.ServletContext;

2 importjavax.servlet.ServletContextEvent;

3

4 importorg.springframework.context.ApplicationContext;

5 importorg.springframework.web.context.ContextLoaderListener;

6 importorg.springframework.web.context.support.WebApplicationContextUtils;

7

8 publicclass SpringLoaderListener extends ContextLoaderListener{

9     

10    @Override

11    public void contextInitialized(ServletContextEvent event) {

12        super.contextInitialized(event);

13        ServletContext context=event.getServletContext();

14        ApplicationContextctx=WebApplicationContextUtils.getRequiredWebApplicationContext(context);

15        SpringContextutil.setContext(ctx);

16         

17     }

18

19 }

web.xml

1  <listener>

2        <listener-class>

3            XXXXXX.utils.SpringLoaderListener

4        </listener-class>

5 </listener>

获得spring bean工具类:

1 importorg.springframework.beans.BeansException;

2 importorg.springframework.beans.factory.NoSuchBeanDefinitionException;

3 importorg.springframework.context.ApplicationContext;

4

5 publicclass SpringContextUtil {

6     private static ApplicationContext context;

7

8     public static ApplicationContextgetContext() {

9         return context;

10     }

11

12    public static void setContext(ApplicationContext context) {

13        SpringContextutil.context = context;

14     }

15     

16    public static Object getBean(String name)throws BeansException{

17        return context.getBean(name);

18     }

19

20    @SuppressWarnings("unchecked")

21    public static Object getBean(String name, Class requiredType) throwsBeansException {   

22        return context.getBean(name, requiredType);   

23    }   

24   

25    public static boolean containsBean(String name) {   

26        return context.containsBean(name);  

27    }   

28   

29    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException{   

30        return context.isSingleton(name);  

31    }   

32   

33    @SuppressWarnings("unchecked")

34    public static Class getType(String name) throwsNoSuchBeanDefinitionException {   

35        return context.getType(name);   

36    }   

37   

38    public static String[] getAliases(String name) throwsNoSuchBeanDefinitionException {   

39        return context.getAliases(name);  

40    }   

41

42     

43 }

引入json所需jar包:

1     <dependency>

2      <groupId>com.google.code.gson</groupId>

3      <artifactId>gson</artifactId>

4       <version>2.2.3</version>

5     </dependency>

6     <dependency>

7         <groupId>net.sf.json-lib</groupId>

8        <artifactId>json-lib</artifactId>

9         <version>2.4</version>

10    </dependency>

11    <dependency>

12     <groupId>net.sf.ezmorph</groupId>

13      <artifactId>ezmorph</artifactId>

14      <version>1.0.6</version>

15    </dependency>    

后台需要增加两个文件一个继承WebSocketServlet:

1 importjavax.servlet.annotation.WebServlet;

2 importjavax.servlet.http.HttpServletRequest;

3

4 importorg.apache.catalina.websocket.StreamInbound;

5 importorg.apache.catalina.websocket.WebSocketServlet;

6

9@WebServlet("/room")

10 public class RoomSocketServlet extendsWebSocketServlet {

11     

12    private static final long serialVersionUID = -5853470534275847275L;

13     

14    @Override

15      protected StreamInbound createWebSocketInbound(Stringarg0,HttpServletRequest request) {

16         return new RoomMessageInbound();

17       }

18       

19     }

一个继承MessageInbound:

1 publicclass RoomMessageInbound extends MessageInbound{

2     private static RoomModel room ;

3     private UserModel user;

4     private CommandDispatcherUtilscommandDispatcher;

5     

6     public RoomMessageInbound() {

7         if (commandDispatcher == null) {

8             commandDispatcher =(CommandDispatcherUtils)SpringContextutil.getBean("commandDispatcher");

9             room =RoomListModel.getInstance().getRoom(0);

10         }

11     }

12  

14    @Override

15    protected void onOpen(WsOutbound outbound)

16        room.addUser(outbound.hashCode());

17      super.onOpen(outbound);

18     }

19

20     @Override

21    protected void onClose(int status) {

22      room.remove(getWsOutbound().hashCode());

23      super.onClose(status);

24     }

25

26    @Override

27    protected void onBinaryMessage(ByteBuffer buffer) throws IOException {

28

29     }

30

31    @Override

32    protected void onTextMessage(CharBuffer buffer) throws IOException {

33      String msg = buffer.toString();

34      JSONObject report = JSONObject.fromObject(msg);

35       TemplateCommandcommand =commandDispatcher.getCommandByKey(report.getString("command"));

36      command.execute(msg,user,room);

37     }

38

39 }

通过JSONObject report = JSONObject.fromObject(msg)就可以将字符串转换为json对象也就等于通过websocket实现了实时对象信息的传递。

在前端页面中只需要页面载入时的js中加入

1 $(function() {                

2        roomsocket = new WebSocket('ws://127.0.0.1:8080/XXXX/room);

3 }

在前端向后台发送时将数据JSON.stringify(data)就能json化

上述代码做了不少删减。所以代码不见得复制就能用。只是提供一种解决方案。况且不久java新的版本或者说Spring Framework 4.0就能很轻松的支持了websocket的实现。主要是还是给自己这段时间的websocket研究做一些总结随着各种新的技术的诞生,实时web技术已经越来越成熟。websocket是html5的重要特色是值得去看看,研究一下

 

来自 http://www.hiputto.com/post/2013-12-17/40060476713


问题:低延迟的客户端-服务器和服务器-客户端连接

一直以来,网络在很大程度上都是围绕着所谓 HTTP 的请求/响应模式而构建的。客户端加载一个网页,然后直到用户点击下一页之前,什么都不会发生。在 2005 年左右,AJAX 开始让网络变得更加动态了。但所有 HTTP 通信仍然是由客户端控制的,这就需要用户互动或定期轮询,以便从服务器加载新数据。

长期以来存在着各种技术,可让服务器得知有新数据可用时,立即将数据发送到客户端。这些技术名目繁多,例如“推送”或 Comet。最普遍的一种黑客手段是对服务器发起的链接创建假象,这称为长轮询。利用长轮询,客户端可打开指向服务器的 HTTP 连接,而服务器会一直保持连接打开,直到发送响应。服务器只要实际拥有新数据,就会发送响应(其他技术包括 FlashXHR multipart 请求和所谓的htmlfiles)。长轮询和其他技术非常好用,您在 Gmail 聊天等应用中经常使用它们。

但是,这些解决方案都存在一个共同的问题:它们带有 HTTP 的开销,导致它们不适用于低延迟应用。可以想象一下浏览器中的多人第一人称射击游戏,或者其他任何带有即时要素的在线游戏。

WebSocket 简介:将套接字引入网络

WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。

使用入门

只需调用 WebSocket 构造函数即可打开 WebSocket 连接:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

请注意 ws:。这是 WebSocket 连接的新网址架构。对于安全 WebSocket 连接还有wss:,就像 https: 用于安全 HTTP 连接一样。

立即向连接附加一些事件处理程序可让您知道连接打开、收到传入讯息或出现错误的时间。

第二个参数可接受可选子协议,它既可以是字符串,也可以是字符串数组。每个字符串都应代表一个子协议名称,而服务器只能接受数组中通过的一个子协议。访问 WebSocket 对象的 protocol 属性可确定接受的子协议。

子协议名称必须是 IANA 注册表中的某个注册子协议名称。截止 2012 年 2 月,只有一个注册子协议名称 (soap)。

// When the connection is open, send some data to the server
connection.onopen = function () {
  connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

与服务器通信

与服务器建立连接后(启动 open 事件时),我们可以开始对连接对象使用send('your message') 方法,向服务器发送数据。该方法以前只支持字符串,但根据最新的规范,现在也可以发送二进制讯息了。要发送二进制数据,您可以使用Blob  ArrayBuffer 对象。

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

同样,服务器也可能随时向我们发送讯息。只要发生这种情况,就会启动onmessage 回调。该回调接收的是事件对象,而实际的讯息可通过 data 属性进行访问。

在最新规范中,WebSocket 也可以接收二进制讯息。接收的二进制帧可以是 Blob ArrayBuffer 格式。要指定收到的二进制数据的格式,可将 WebSocket 对象的 binaryType 属性设为“blob”或“arraybuffer”。默认格式为“blob”(您不必在发送时校正 binaryType 参数)。

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
  console.log(e.data.byteLength); // ArrayBuffer object if binary
};

WebSocket 的另一个新增功能是扩展。利用扩展,可以发送压缩帧、多路复用帧等。您可以检查 open 事件后的 WebSocket 对象的 extensions 属性,查找服务器所接受的扩展。截止 2012 年 2 月,还没有官方发布的扩展规范。

// Determining accepted extensions
console.log(connection.extensions);

跨源通信

作为现代协议,跨源通信已内置在 WebSocket 中。虽然您仍然应该确保只与自己信任的客户端和服务器通信,但 WebSocket 可实现任何域上多方之间的通信。服务器将决定是向所有客户端,还是只向驻留在一组指定域上的客户端提供服务。

代理服务器

每一种新技术在出现时,都会伴随着一系列问题。WebSocket 也不例外,它与大多数公司网络中用于调解 HTTP 连接的代理服务器不兼容。WebSocket 协议使用 HTTP 升级系统(通常用于 HTTP/SSL)将 HTTP 连接“升级”为 WebSocket 连接。某些代理服务器不支持这种升级,并会断开连接。因此,即使指定的客户端使用了 WebSocket 协议,可能也无法建立连接。这就使得下一部分的内容更加重要了。

立即使用 WebSocket

WebSocket 仍是一项新兴技术,并未在所有浏览器中全面实施。而在无法使用 WebSocket 的情况下,只要通过一些使用了上述某个回调的库,就可以立即使用 WebSocket 了。在这一方面使用非常普遍的库是 socket.io,其中自带了协议的客户端和服务器实施,并包含回调(截止 2012 年 2 月,socket.io 还不支持二进制讯息传输)。还有一些商业解决方案,例如 PusherApp,通过提供向客户端发送 WebSocket 讯息的 HTTP API,可轻松地集成到任何网络环境中。由于额外的 HTTP 请求,这些解决方案与纯 WebSocket 相比总是会有额外的开销。

服务器端

使用 WebSocket 为服务器端应用带来了全新的用法。虽然 LAMP 等传统服务器堆栈是围绕 HTTP 请求/响应循环而设计的,但是通常无法很好地处理大量打开的 WebSocket 连接。要同时维持大量连接处于打开状态,就需要能以低性能开销接收高并发数据的架构。此类架构通常是围绕线程或所谓的非阻塞 IO 而设计的。

服务器端实施

协议版本

现在,WebSocket 的单线协议(客户端与服务器之间的握手和数据传输)是RFC6455。最新版的 Chrome 浏览器和 Android 版 Chrome 浏览器与 RFC6455 完全兼容(包括二进制讯息传输)。另外,Firefox 11 和 Internet Explorer 10 也会实现兼容。您仍可以使用旧版协议,但由于它们已知存在漏洞,我们不建议使用。如果您有旧版 WebSocket 协议的服务器实施,我们建议您将其更新到最新版本。

用例

如果您需要在客户端与服务器之间建立极低延迟、近乎即时的连接,则可使用 WebSocket。请记住,这可能需要您重新考虑构建服务器端应用的方式,将新的关注点放在事件队列等技术上。以下是一些用例:

  • 多人在线游戏
  • 聊天应用
  • 体育赛况直播
  • 即时更新社交信息流

演示

参考

来自 http://www.html5rocks.com/zh/tutorials/websockets/basics/
普通分类: