WebSocket实时异步通信

【一】WebSocket简介

  WebSocket是HTML5推出一个协议规范,用来B/S模式中服务器端和客户端之间进行实时异步通信。

  众所周知,传统的HTTP协议中,服务器端和客户端通信只能是在客户端发送一个请求之后,服务器端才能对其响应,也就是说服务器端是不能够主动向客户端发起数据的。

  当在服务器端和客户端之间需要频繁地实时通信时,传统HTTP协议只能通过Ajax轮询的方式,即客户端每隔一段时间请求一次服务器来获得更新,这样做的对服务器的性能要求特别高,而且占用很多无用的带宽、流量。

图一 传统HTTP请求会话

图二 WebSocket请求会话

  WebSocket在实时通信方面很好的弥补了传统HTTP协议的不足。它是一个建立在HTTP协议之上的协议,通过HTTP建立了WebSocket连接之后,服务器端和客户端就可以随时通信了。

【二】WebSocket的使用

服务器端

  Tomcat7.0.5版本及以后的版本之中已经内嵌了支持WebSocket标准协议的jar包websocket-api.jar;在服务器端开发中通过@ServerEndpoint注解表明WebSocket服务端运行在 ws://[Server端IP或域名]:[Server端口]/project-name/注解参数值 路径指定的位置,客户端在创建WebSocket对象的时候,通过该路径指定服务器响应的WebSocket。

  @OnOpen、@OnClose、@OnError注解WebSocket方法分别响应建立连接、关闭连接和出错时候的回调函数,@OnMessage注解的方法响应收到客户端发送数据的回调函数。Session相当于客户端,可以向客户端发送数据。

  WebSocket类处理客户端的所有请求,然后根据不同页面的请求调用不同的类对象进行处理。

import java.io.IOException;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import javax.websocket.Session;import org.apache.log4j.Logger;import net.sf.json.JSONObject;

@ServerEndpoint("/websocket")
public class WebSocket {

    public static Logger log = Logger.getLogger(WebSocket.class);
    private Statement stmt = null;
    private Connection conn = null;
    private Timer timer;

    @OnOpen
    public void onOpen(Session s) {
        log.info("client connected");
        timer = new Timer();
        //connect database
        try {
            ResourceBundle config = ResourceBundle.getBundle("config");
            String driver = config.getString("jdbc.driver");
            String url = config.getString("jdbc.cfg.url");
            String username = config.getString("jdbc.cfg.username");
            String password = config.getString("jdbc.cfg.password");
            Class.forName(driver);
            conn = DriverManager.getConnection(url, username, password);
            stmt = conn.createStatement();
            log.info("connect database successful");
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(final String message, Session s) throws IOException, InterruptedException{
        log.info("message="+message);
        final JSONObject json = JSONObject.fromString(message);
        if(json.getInt("pageIndex") == 2){        //热门配置页不循环推送
            HotConfig hotConfig = new HotConfig(s, stmt);
            hotConfig.sendWebSocket(json);
        }else if(json.getInt("pageIndex") == 3){
            final TrafficLog trafficLog = new TrafficLog(s, stmt);
            int isRealTimeSearch = json.getInt("isRealTimeSearch");
            if(isRealTimeSearch == 1){
                timer.schedule(new TimerTask() { //每隔5分钟执行一次
                    @Override
                    public void run() {
                        trafficLog.sendWebSocket(json);
                    }
                }, 0, 300000);
            }else{                                //只执行一次
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        trafficLog.sendWebSocket(json);
                    }
                }, 0);
            }
        }else if(json.getInt("pageIndex") == 4){
            final RuleLog ruleLog = new RuleLog(s, stmt);
            int isRealTimeSearch = json.getInt("isRealTimeSearch");
            if(isRealTimeSearch == 1){
                timer.schedule(new TimerTask() { //每隔5分钟执行一次
                    @Override
                    public void run() {
                        ruleLog.sendWebSocket(json);
                    }
                }, 0, 300000);
            }else{                                //只执行一次
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        ruleLog.sendWebSocket(json);
                    }
                }, 0);
            }
        }else{                                    //isp页面的请求
            final ISPLog ispLog = new ISPLog(s, stmt);
            int isRealTimeSearch = json.getInt("isRealTimeSearch");
            if(isRealTimeSearch == 1){
                timer.schedule(new TimerTask() { //每隔5分钟执行一次
                    @Override
                    public void run() {
                        ispLog.sendWebSocket(json);
                    }
                }, 0, 300000);
            }else{                                //只执行一次
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        ispLog.sendWebSocket(json);
                    }
                }, 0);
            }
        }
    }

    @OnClose
    public void onClose(Session s) {
        log.info("Client closed");
        try {
            timer.cancel();
            if (stmt != null) {
                stmt.close();
                stmt = null;
            }
            if (conn != null) {
                conn.close();
                conn = null;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Throwable e) {
        log.error("WebSocket Error");
        e.printStackTrace();
    }
}

  响应具体某个页面的请求

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.websocket.Session;
import org.apache.log4j.Logger;
import net.sf.json.JSONObject;

public class HotConfig {

    private Session session;
    private Statement stmt = null;
    private ResultSet rs = null;
    private static Logger log = Logger.getLogger(HotConfig.class);

    public HotConfig(Session s, Statement stmt){
        this.session = s;
        this.stmt = stmt;
    }

    public void sendWebSocket(final JSONObject json){
        String srcIp = json.getString("srcIp");
        String startTime = json.getString("startTime");
        String endTime = json.getString("endTime");
        Map<String, ArrayList<Long>> params = new HashMap<String, ArrayList<Long>>();
        ArrayList<Long> pzID = new ArrayList<Long>();             //different chart lines name
        ArrayList<Long> statCount = new ArrayList<Long>();        //chart x axis
        ArrayList<Long> pzIDReverse = new ArrayList<Long>();            //different chart lines name
        ArrayList<Long> statCountReverse = new ArrayList<Long>();        //chart x axis
        try {
            String srcIpCriteria = "";                        //拼接源IP SQL条件
            if(srcIp.length() > 0) {
                srcIpCriteria = " where CLIENT_IP=‘"+srcIp+"‘ ";
            }
            String startTimeCriteria = "";                    //拼接源IP SQL条件
            if(startTime.length() > 0) {
                if(srcIpCriteria.equals(""))
                    startTimeCriteria = " where STAT_TIME >= to_date(‘"+startTime +"‘,‘yyyy-mm-dd hh24:mi‘)";
                else
                    startTimeCriteria = " and STAT_TIME >= to_date(‘"+startTime +"‘,‘yyyy-mm-dd hh24:mi‘)";
            }
            String endTimeCriteria = "";                      //拼接源IP SQL条件
            if(endTime.length() > 0) {
                if(srcIpCriteria.equals("") && startTimeCriteria.equals(""))
                    endTimeCriteria = " where STAT_TIME < to_date(‘" +endTime+"‘,‘yyyy-mm-dd hh24:mi‘) ";
                else
                    endTimeCriteria = " and STAT_TIME < to_date(‘" +endTime+"‘,‘yyyy-mm-dd hh24:mi‘) ";
            }
            String sql = "select * from (select PZ_ID, sum(STAT_COUNT) as sumCount from RULE_HOT_CONFIG_INFO"
                    + srcIpCriteria + startTimeCriteria + endTimeCriteria
                    + " group by PZ_ID order by sumCount desc) where rownum <= 50";
            log.info("sql="+sql+"execute success");
            rs = stmt.executeQuery(sql);
            while(rs.next()){
                   pzID.add(rs.getLong(1));
                   statCount.add(rs.getLong(2));
             }
            for(int i = pzID.size() - 1; i >= 0; i--){
                pzIDReverse.add(pzID.get(i));
                statCountReverse.add(statCount.get(i));
            }
            params.put("pzID", pzIDReverse);
            params.put("statCount",statCountReverse);
            session.getBasicRemote().sendText(JSONObject.fromBean(params).toString());
            log.info("主动发给前端的数据="+JSONObject.fromBean(params).toString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

客户端

  目前主流的浏览器Firefox、Chrome已经支持WebSocket协议,这意味只不需要引用额外的js库就可以直接使用WebSocket了。

<script type="text/javascript">        //创建WebSocket对象,用于一开始建立连接,之后就不要再建立连接了
        var webSocket = new WebSocket(‘ws://localhost:8080/websocket/websocket‘);
        webSocket.onopen = function(event) {

        };
        webSocket.onmessage = function(event) {        //接收来自服务器的数据,这里客户端没有发送任何请求,任何时间接收到数据都可以异步调用
              onMessage(event);        var  m = JSON.parse(event.data);        alert(m);
        };
        webSocket.onerror = function(event) {

        };
        function onMessage(event) {

        }
</script>


推荐学习文章:http://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/

        http://www.tuicool.com/articles/7zyMvy6

        http://www.alixixi.com/web/a/2014032492868.shtml

时间: 2024-10-15 15:44:41

WebSocket实时异步通信的相关文章

使用Node.js+Socket.IO搭建WebSocket实时应用

Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新.它有着广泛的应用场景,比如在线聊天室.在线客服系统.评论系统.WebIM等. 作者:潘良虎链接:https://www.zhihu.com/question/20215561/answer/26419995来源:知乎原文地址:http://www.plhwin.com/2014/05/28/nodejs-socketio/ WebSocket简介 谈到Web实时推送,就不得不说

使用Node.js+Socket.IO搭建WebSocket实时应用【转载】

原文:http://www.jianshu.com/p/d9b1273a93fd Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新.它有着广泛的应用场景,比如在线聊天室.在线客服系统.评论系统.WebIM等. WebSocket简介 谈到Web实时推送,就不得不说WebSocket.在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询(Polling)和Comet技术,Comet又可细分为两种实现方

(转)使用Node.js+Socket.IO搭建WebSocket实时应用

Web领域的实时推送技术,也被称作Realtime技术.这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新.它有着广泛的应用场景,比如在线聊天室.在线客服系统.评论系统.WebIM等. WebSocket简介 谈到Web实时推送,就不得不说WebSocket.在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询(Polling)和Comet技术,Comet又可细分为两种实现方式,一种是长轮询机制,一种称为流技术,这两种方式实际上是对轮询技术的改进,这些方案

WebSocket 实时更新mysql数据到页面

使用websocket的初衷是,要实时更新mysql中的报警信息到web页面显示 没怎么碰过web,代码写的是真烂,不过也算是功能实现了,放在这里也是鞭策自己,web也要多下些功夫 准备 引入依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> <

Spring Boot 入门(十一):集成 WebSocket, 实时显示系统日志

以前面的博客为基础,最近一篇为Spring Boot 入门(十):集成Redis哨兵模式,实现Mybatis二级缓存.本篇博客主要介绍了Spring Boot集成 Web Socket进行日志的推送,并实时显示在页面上. 1.导入jar包 第一个jar包是websocket的,第二个jar包是关于环形队列的jar包,本案例是通过本地队列存储日志.有条件的话,最好通过中间件存储(eg:redis,mq……).通过本地队列存储日志会存在日志丢失的情况,且日志量太大,会把页面卡死. 1 <!--beg

Uniapp使用GoEasy实现websocket实时通讯

Uniapp作为近来最火的移动端开发技术,一套代码,可以打包成Android/iOS app和各种平台的小程序,可谓是没有最方便只有更方便. GoEasy上架DCloud Uniapp插件市场已经有一个多月了,收到很多Uniapp开发人员的赞扬和好评,小编在此隔着屏幕向大家鞠躬,小编和GoEasy团队会继续努力,持续为Uniapp开发者提供最简单且优雅的API,稳定高速可靠的即时通讯服务. 这段时间,也收到了很多朋友的一些问题?比如: 1.GoEasy和Uniapp websocket API有

websocket实时聊天(一)

今天简单看了一下webscoket的相关内容,写了一个入门demo,简单记录一下. 1.服务端 服务端使用springboot来搭建,引入spring-boot-starter-websocket模块,以及web模块来做页面.页面使用简单的thymeleaf模板. 配置websocket 服务的配置websocket需要先定义WebSocketExporter的bean,使用默认的构造方法即可. package com.chat.config; import org.springframewor

用PHP 和Websocket实现实时通讯---GoEasy

说到websocket大家一定不会陌生,WebSocket是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(full-duplex).一开始的握手需要借助HTTP请求完成,当浏览器和服务器握手成功后,浏览器和服务器之间就形成了一条快速通道.两者之间就直接可以数据互相传送.有了websocket, 大家就可以摒弃以往用轮询来实现实时通讯的方式了. 有了websocket后,应运而生的相关产品也不在少数,选择也成了最大的问题,在这里你可能会说"干嘛用别人的,我可以自己用原始的开发一个啊&q

PHP基于websocket的前台及后台实时推送

PHP websocket实时消息推送   实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个账号,并新建一个app. APP创建好后系统会为该app自动生成两个key, 一个既可以用来接收又可以用来推送 (supper key),另一个只可以用来接收(subscriber key). 2.        客户端订阅一个channel. a.        在客户端引入goeasy.js, js地址:http://cdn.goeasy.io/goe