一、 背景:

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

粉红色区域表示不支持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<Object>() ;

添加监听的事件以及该时间中传输消息的实体对象,服务端可以通过此方法来捕捉到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<String,String> 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<ChatObject>() {

@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<ChatObject>() {

@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<ChatObject>() {

@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();

}

}

客户端:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<title>Demo Chat</title>

<link href="bootstrap.css" rel="stylesheet">

<style>

body {

padding:20px;

}

#console {

height: 400px;

overflow: auto;

}

.username-msg {color:orange;}

.connect-msg {color:green;}

.disconnect-msg {color:red;}

.send-msg {color:#888}

</style>

<script src="js/socket.io/socket.io.js"></script>

<script src="js/moment.min.js"></script>

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>

<script>

var userName = 'user' + Math.floor((Math.random()*1000)+1);

//页面打开时,进行连接,url后为端口信息,若不设置默认为连接80端口

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

//监听连接事件

socket.on('connect', function() {

//var jsonObject = {userName: userName,

// message: '1'};

//socket.emit("con",jsonObject);

output('<span class="connect-msg">Client has connected to the server!</span>');

});

//监听chatevent事件

socket.on('chatevent', function(data) {

output('<span class="username-msg">' + data.userName + ':</span> ' + data.message);

});

//监听离线事件

socket.on('disconnect', function() {

output('<span class="disconnect-msg">The client has disconnected!</span>');

});

function sendDisconnect() {

socket.disconnect();

// var jsonObject = {userName: userName,

// message: '2'};

//socket.emit("discon",jsonObject);

}

function sendReconnect(){

socket.connect();

}

function sendMessage() {

var message = $('#msg').val();

$('#msg').val('');

var jsonObject = {userName: userName,

message: message};

//1、通过send方法发送数据

//chatSocket.json.send(jsonObject);

//2、原生emit方法

socket.emit('chatevent', jsonObject);

}

function output(message) {

var currentTime = "<span class='time'>" + moment().format('HH:mm:ss.SSS') + "</span>";

var element = $("<div>" + currentTime + " " + message + "</div>");

$('#console').prepend(element);

}

//回车事件

$(document).keydown(function(e){

if(e.keyCode == 13) {

$('#send').click();

}

});

</script>

</head>

<body>

<h1>Netty-socketio Demo Chat</h1>

<br/>

<div id="console" class="well">

</div>

<form class="well form-inline" οnsubmit="return false;">

<input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>

<button type="button" onClick="sendMessage()" class="btn" id="send">Send</button>

<button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>

<button type="button" onClick="sendReconnect()" class="btn">REconnect</button>

</form>

</body>

</html>

运行服务端.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分布式)

socket.io client + socketio-netty server简析相关推荐

  1. python socketio async client_electron/socket.io client-python socketio/aiohttp server//连接失败

    此websocket连接失败.有趣的是这几天前还在起作用.我把电子从6降到了5.0.6,但这没用. 服务器 from aiohttp import web import socketio app = ...

  2. 使用JAVA实现Socket通信,TCP、UDP简析。

    Socket通信是一种非常重要的通信方式,它使用起来简单方便,也很容易学会,下面就我所知道的简单记录一下. 首先是UDP的方式.(通过Android与PC端进行通信) 发送端: package com ...

  3. socket.io java 点对点_netty-socket.io点对点通讯和聊天室通讯

    netty-socketio是基于netty的socket.io服务实现,可以无缝对接前端使用的socketio-client.js. 相对于javaee的原生websocket支持(@serverE ...

  4. socket.io介绍

    Socket.IO 是一个库,可以在客户端和服务器之间实现低延迟.双向和基于事件的通信.它建立在 WebSocket 协议之上,并提供额外的保证,例如回退到 HTTP 长轮询或自动重新连接. 几种可用 ...

  5. phaser.min.js_如何使用Phaser 3,Express和Socket.IO构建多人纸牌游戏

    phaser.min.js I'm a tabletop game developer, and am continually looking for ways to digitize game ex ...

  6. Node.js + Socket.io 实现一对一即时聊天

    实现一对一即时聊天应用,重要的一点就是消息能够实时的传递,一种方案就是熟知的使用 Websocket 协议,本文中我们使用 Node.js 中的一个框架 http://Socket.io 来实现. 效 ...

  7. Socket.io使用介绍

    如何使用 安装 npm install socket.io 与Node http server联合使用 Server(app.js) var app = require('http').createS ...

  8. webrtc+canvas+socket.io从零实现一个你画我猜 | 掘金技术征文

    开场白 最近键盘坏了,刚好看到掘金有声网的技术征文,想整个键盘.于是就开始从零开始学习webrtc, 一开始看文档就是个素质三连.这么难啊,这咋整啊,这谁顶的住啊.于是就开始全网找资料,很幸运的在掘金 ...

  9. 【Spring Boot】集成Netty Socket.IO通讯框架

    服务端 1 @Configuration 2 public class NettySocketConfig { 3 4 private static final Logger logger = Log ...

最新文章

  1. 【组队学习】【29期】1. 编程实践(数据可视化)
  2. vue-cli 如何打包上线的方法示例
  3. 分析隐藏在比特币区块链中的Pony CC服务器
  4. ECCV18 Oral | MIT谷歌视频运动放大让计算机辅助人眼“明察秋毫”
  5. VTK:Math之LeastSquares
  6. [原创]快速排序(C++版)
  7. C#编写窗体(Form)常用属性
  8. Nodejs正则表达式函数之match、test、exec、search、split、replace使用详解
  9. 《程序是怎样跑起来的》读书笔记——第三章 计算机进行小数运算时出错的原因...
  10. 【HISI系列】之H264/H265/JPEG切帧方法
  11. Flutter - Stateful(有状态) 和 stateless(无状态) widgets
  12. win7计算机重启遇到错误,安装Win7系统过程出现计算机意外地重新启动或遇到错误提示的解决方法...
  13. 工业相机与SCARA机械臂的坐标系标定
  14. 怎么给图片添加水印?教你一键添加水印
  15. uniapp里css不是识别*,报 error at token “*“
  16. 用计算机弹起风了歌词,起风了歌词(买辣椒也用券演唱)
  17. Imperva WAF使用笔记
  18. Error: docker-ce conflicts with 2:docker-1.13.1-209.git7d71120.el7.centos.x86_64
  19. 【Android -- 写作工具】Markdown 图片
  20. 用于生成随机数的python标准库模块是_详解Python基础random模块随机数的生成

热门文章

  1. autojs左右滑动脚本代码_基于AutoJs过滑块快手极速版脚本
  2. pyqt界面支持拖拽文件
  3. 每日一练20210527
  4. 一条指令在cpu中的执行流程(理解CPU组成)
  5. K8s问题之解决Kubelet stopped posting node status问题
  6. word批量替换回车符号
  7. 科技云报道:发布分布式云战略,中国电子云吹响冲锋号角
  8. 随笔:车辆游戏功能开发-思路
  9. 解决RabbitMQ admin账号登陆失败的问题
  10. STM32CubeMX上手初体验