首页 > 代码库 > WebSocket实时异步通信

WebSocket实时异步通信

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 one rror(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

WebSocket实时异步通信