socket.io client + socketio-netty server简析

一、 背景:

现在实时web消息推送一般会用到websocket,但是由于此技术并没有推广开来,所以各浏览器对其支持也不同,例如下图显示了各类浏览器的支持情况。

socket.io client + socketio-netty server简析_第1张图片

粉红色区域表示不支持Websocket。

至于IE浏览器,以及部分陈旧的桌面浏览器,可以选择Flashsocket作为替代品。

客户端如何把Websocket和Flashsocket结合在一起使用,可借鉴开源项目:web-socket-js (客户端的Websocket实现方案)

 

其思路和socket.io大致一致,仅仅提供对websocket的客户端的简单包装,若是Android 上原生浏览器,没有安装Flash Lite情况下,就无能为力了。

因此,仅仅凭借Websocket + Flashsocket,是不能够完成跨浏览器、统一客户端API的重任。

在这种情况下,socket.io就应运而生了。

 

 

二、socket.io的介绍以及优点

socket.io 支持以下通信信道传输协议:

  • WebSocket
  • Adobe® Flash® Socket
  • AJAX long polling
  • AJAX multipart streaming
  • Forever Iframe
  • JSONP Polling

socket.io并不是简单的封装了websocket,可以说websocket只是socket.io的其中一部分。使用socket.io客户端和服务器端双方约定适合当前浏览器的最佳通信信道,然后正常通信。并且还可以手动指定使用某种方式进行通信。只要我们指定socket.io transport的参数,就可以做到心里有数。

在socketio-netty服务器端配置:

transports = websocket,flashsocket,htmlfile,xhr-polling,jsonp-polling

在客户端,简单定义地址:

var socket = io.connect('http://localhost:9000');

在不远的将来,桌面版浏览器可能升级了最新版本的websocket草案,导致客户端原生的websocket协议无法被识别时,可使用Flashsocket作为替代品。但总会有一种通信协议垫底,可以保证正常的运转。

socket.io即提供了node.js服务器端又提供了客户端的整体解决方案,而socketio-netty则是基于JAVA服务器端,支持最新socket.io client最新版规范。对JAVA编程人员来讲,可以不用学习node.js,从而多了一个选择。

注:附1中简单的介绍了集中不同通信协议的优缺点。有兴趣的可以查看。

 

 

三、socket.io事件和方法简单介绍

Socket.IO内置了一些默认事件,我们在设计事件的时候应该避开默认的事件名称,并灵活运用这些默认事件。

服务器端事件:

server.addEventListener("con", Object.class, new DataListener() ;

添加监听的事件以及该时间中传输消息的实体对象,服务端可以通过此方法来捕捉到client传输过来的Message请求;

server.addConnectListener(new ConnectListener();

添加连接监听时间,当client连接时会首先触发此事件,server可以在这里进行一些初始化操作。

server.addDisconnectListener( new DisconnectListener();

添加断开连接监听时间,当client断开连接时会首先触发此事件,server可以在这里进行一些断线操作。(包括关闭浏览器,主动断开,掉线等任何断开连接的情况)。客户端事件:

connect:连接成功disconnect:断开连接message:同服务器端message事件 在这里要提下客户端socket发起连接时的顺序。当第一次连接时,事件触发顺序为:connecting->connect;当失去连接时,事件触发顺序为:disconnect->reconnecting(可能进行多次)->connecting->reconnect->connect。

注意:刷新浏览器时,相当与客户端首先disconnect然后重新建立一次connect。并且此时socket.io会默认重连之前断开的连接。

 

客户端常用方法:

socket.emit()和socket.on();这两种都可以用来发送消息,只是在写法上有稍微不同。

socket.emit('action');表示发送了一个action命令,命令是字符串的,也可以这么写: socket.on('action',function(){...});

socket.emit('action',data);表示发送了一个action命令,还有data数据,也可以这么写: socket.on('action',function(data){...});

socket.emit(action,arg1,arg2); 表示发送了一个action命令,还有两个数据,也可以这么写: socket.on('action',function(arg1,arg2){...});

在emit方法中包含回调函数,例如:

socket.emit('action',data, function(arg1,arg2){...} );那么这里面有一个回调函数可以在另一端调用,也可以这么写:socket.on('action',function(data,fn){   fn('a','b') ;  });

上面的data数据可以有0个或者多个,相应的在另一端改变function中参数的个数即可,function中的参数个数和顺序应该和发送时一致

上面的fn表示另一个端传递过来的参数,是个函数,写fn('a','b') ;会回调函数执行。一次发送不应该写多个回调,否则只有最后一个起效,回调应作为最后一个参数。

 

四、简单的代码应用(聊天室)

服务端:

package com.corundumstudio.socketio.demo;

import java.util.HashMap;

import java.util.Map;

import com.corundumstudio.socketio.listener.*;

import com.corundumstudio.socketio.*;

 

public class ChatLauncher {

private static Map clients;

public static void main(String[] args) throws InterruptedException {

 

Configuration config = new Configuration();

//设置主机名称UR了

config.setHostname("localhost");

//设置端口,此处必须设置,不设置启动时会报错

config.setPort(80);

 

final SocketIOServer server = new SocketIOServer(config);

server.addEventListener("con", ChatObject.class, new DataListener() {

@Override

public void onData(SocketIOClient client, ChatObject data, AckRequest ackRequest) {

// broadcast messages to all clients

data.setMessage("用户处于在线状态");

System.out.println("uuid"+client.getSessionId());

server.getBroadcastOperations().sendEvent("chatevent", data);

//回调当前clicent的函数

//client.sendEvent("chatevent", data);

}

});

server.addEventListener("discon", ChatObject.class, new DataListener() {

@Override

public void onData(SocketIOClient client, ChatObject data, AckRequest ackRequest) {

// broadcast messages to all clients

data.setMessage("用户离线了");

server.getBroadcastOperations().sendEvent("chatevent", data);

System.out.println("离线了!!!");

client.disconnect();

//回调当前clicent的函数

//client.sendEvent("chatevent", data);

}

});

//当client连接时触发此事件

server.addConnectListener(new ConnectListener(){

@Override

public void onConnect(SocketIOClient client) {

System.out.println(client.getSessionId()+"在线了!!!");

}

});

//当client离线时触发此事件

server.addDisconnectListener( new DisconnectListener(){

 

@Override

public void onDisconnect(SocketIOClient client) {

System.out.println(client.getSessionId()+"离线了!!!");

}

});

//监听端口上的chatevent事件

server.addEventListener("chatevent", ChatObject.class, new DataListener() {

@Override

public void onData(SocketIOClient client, ChatObject data, AckRequest ackRequest) {

// broadcast messages to all clients

server.getBroadcastOperations().sendEvent("chatevent", data);

//回调当前clicent的函数

//client.sendEvent("chatevent", data);

}

});

 

server.start();

 

Thread.sleep(Integer.MAX_VALUE);

 

server.stop();

}

}

 

客户端:

Demo Chat

 

Netty-socketio Demo Chat


 

运行服务端.java文件后,打开客户端页面即可进行聊天。如下图:

图 1

 

 

图 2

 

注意:完整代码在方的参考区域,有兴趣的可以下载下来一起学习。

 

五、学习心得

在html5之前,因为http协议是无状态的,要实现 浏览器与服务器的实时通讯,如果不使用 flash、applet 等浏览器插件的话,就需要定期轮询服务器来获取信息。这造成了一定的延迟和大量的网络通讯。随着HTML5 的出现,这一情况有望彻底改观,这与需要实现与服务器实时通信的应用来说,是一种极大的进步,而且随和用户对网络实时通信的要求越来越高,学习sockey.io这门技术也是很有发挥空间的。

 

附1

不断地轮询(俗称“拉”,polling)是获取实时消息的一个手段:Ajax 隔一段时间(通常使用 JavaScript 的 setTimeout 函数)就去服务器查询是否有改变,从而进行增量式的更新。但是间隔多长时间去查询成了问题,因为性能和即时性造成了严重的反比关系。间隔太短,连续不断的请求会冲垮服务器,间隔太长,务器上的新数据就需要越多的时间才能到达客户机。

    • 优点:服务端逻辑简单;
    • 缺点:其中大多数请求可能是无效请求,在大量用户轮询很频繁的情况下对服务器的压力很大;
    • 应用:并发用户量少,而且要求消息的实时性不高,一般很少采用;
  • 长轮询技术(long-polling):客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息或超时(设置)才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
    • 优点:实时性高,无消息的情况下不会进行频繁的请求;
    • 缺点:服务器维持着连接期间会消耗资源;
  • 基于Iframe及htmlfile的流(streaming)方式:iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长链接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。
    • 优点:消息能够实时到达;
    • 缺点:服务器维持着长连接期会消耗资源;
  • 插件提供socket方式:比如利用Flash XMLSocket,Java Applet套接口,Activex包装的socket。
    • 优点:原生socket的支持,和PC端和移动端的实现方式相似;
    • 缺点:浏览器端需要装相应的插件;
  • WebSocket:是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。
    • 优点:更好的节省服务器资源和带宽并达到实时通讯;
    • 缺点:目前还未普及,浏览器支持不好;

综上,考虑到浏览器兼容性和性能问题,采用长轮询(long-polling)是一种比较好的方式。

 

参考代码:

https://github.com/mrniko/netty-socketio

https://github.com/mrniko/netty-socketio-demo

 

参考文章:

http://tech.qq.com/a/20120521/000296.htm

http://www.cnblogs.com/luxiaoxun/p/4279997.html

http://blog.csdn.net/mengxianhua/article/details/44778733

http://blog.csdn.net/kelong_xhu/article/details/50846483 (socketio分布式)

 

 

 

 

你可能感兴趣的:(Websocket)