WEB消息提醒实现之二 实现方式-websocket实现方式

#websocket实现方式

##原理

websocket的原理主要是,利用websocket提供的api,客户端只需要向服务器发起一次连接即可,然后服务器就可以主动地源源不断地向客户端发送数据,只要客户端不关闭浏览器,那么这个连接就会一直保持,从而达到真正意义上的长连接和服务器推。

优点:只需要建立一次连接,服务器就可以源源不断地推送数据,资源消耗少,持久连接
缺点:需要浏览器支持websocket技术

websocket的过程如图:

WEB消息提醒实现之二 实现方式-websocket实现方式_第1张图片

可以看到,只要客户端不关闭浏览器,连接会一直存在,相对于ajax轮询,这里服务器从被动变为主动,只要服务器喜欢,随时都可以向客户端发送数据。

##实例

websocket最经典的实例就是聊天室了,而且tomcat就自带了websocket聊天室的实例,需要导入websocket-api.jar,这个jar包也可以在tomcat的lib下找到,下面是服务器代码:

package com.myj.websocket.servlet;

    import java.io.IOException;
    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    import java.util.concurrent.atomic.AtomicInteger;

    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;

    //import org.apache.juli.logging.Log;
    //import org.apache.juli.logging.LogFactory;

    import util.HTMLFilter;

    //"/websocket/chat"为客户端发起连接的请求路径
    @ServerEndpoint(value = "/websocket/chat")
    public class ChatAnnotation {

    //private static final Log log = LogFactory.getLog(ChatAnnotation.class);

    private static final String GUEST_PREFIX = "Guest";
    private static final AtomicInteger connectionIds = new AtomicInteger(0);
    private static final Set<ChatAnnotation> connections =
            new CopyOnWriteArraySet<ChatAnnotation>();

    private final String nickname;
    private Session session;

    public ChatAnnotation() {
    	//每个客户端都有一个昵称
        nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
    }

    /**
     * 客户端发起连接请求,服务器这边会调用这个start方法,
     * 将该客户端的session和连接保存,然后向所有客户端广播消息
     * @param session
     */
    @OnOpen
    public void start(Session session) {
        this.session = session;
        connections.add(this);
        String message = String.format("* %s %s", nickname, "has joined.");
        broadcast(message);
    }

    /**
     * 如果有客户端关闭浏览器,连接就会断开,服务器会调用end方法,
     * 将保存在connections集合中的相应客户端连接移除,向剩余的客户端广播该
     * 客户端下线的消息
     */
    @OnClose
    public void end() {
        connections.remove(this);
        String message = String.format("* %s %s",
                nickname, "has disconnected.");
        broadcast(message);
    }

    /**
     * 客户端调用send向服务器发送消息的时候,服务器这边会调用incoming方法,
     * 将该客户端发送的消息向所有客户端进行广播
     * @param message
     */
    @OnMessage
    public void incoming(String message) {
        // Never trust the client
        String filteredMessage = String.format("%s: %s",
                nickname, HTMLFilter.filter(message.toString()));
        broadcast(filteredMessage);
    }



    /**
     * 出现异常时候会调用onError
     * @param t
     * @throws Throwable
     */
    @OnError
    public void onError(Throwable t) throws Throwable {
        //log.error("Chat Error: " + t.toString(), t);
    }

    /**
     * 向所有客户端广播消息的方法
     * @param msg
     */
    public static void broadcast(String msg) {
    	//遍历所有的客户端
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {
                	//向客户端推送消息
                    client.session.getBasicRemote().sendText(msg);
                }
            } catch (IOException e) {
            	//如果广播的时候出现异常会将该客户端移除,并向剩余的客户端广播消息
                //log.debug("Chat Error: Failed to send message to client", e);
                connections.remove(client);
                try {
                    client.session.close();
                } catch (IOException e1) {
                    // Ignore
                }
                String message = String.format("* %s %s",
                        client.nickname, "has been disconnected.");
                broadcast(message);
            }
        }
    }
    }

下面是客户端的代码:


    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
    <title>Apache Tomcat WebSocket Examples: Chattitle>
    <style type="text/css">
        input#chat {
            width: 410px
        }

        #console-container {
            width: 400px;
        }

        #console {
            border: 1px solid #CCCCCC;
            border-right-color: #999999;
            border-bottom-color: #999999;
            height: 170px;
            overflow-y: scroll;
            padding: 5px;
            width: 100%;
        }

        #console p {
            padding: 0;
            margin: 0;
        }
    style>
    <script type="application/javascript">
        var Chat = {};

        Chat.socket = null;
		//根据浏览器创建相应的websocket实例,并向服务器发起连接
        Chat.connect = (function(host) {
            if ('WebSocket' in window) {
                Chat.socket = new WebSocket(host);
            } else if ('MozWebSocket' in window) {
                Chat.socket = new MozWebSocket(host);
            } else {
                Console.log('Error: WebSocket is not supported by this browser.');
                return;
            }
			//客户端websocket和服务器建立连接之后会调用onopen方法
            Chat.socket.onopen = function () {
                Console.log('Info: WebSocket connection opened.');
                document.getElementById('chat').onkeydown = function(event) {
                    if (event.keyCode == 13) {
                        Chat.sendMessage();
                    }
                };
            };
			//监听服务器是否关闭,服务器关闭之后会调用onclose方法
            Chat.socket.onclose = function () {
                document.getElementById('chat').onkeydown = null;
                Console.log('Info: WebSocket closed.');
            };
			//监听服务器消息,服务器向客户端推送消息的时候,websocket会调用onmessage    方法
            Chat.socket.onmessage = function (message) {
                Console.log(message.data);
            };
        });
		//传入url,创建websocket并和服务器建立连接
        Chat.initialize = function() {
            if (window.location.protocol == 'http:') {
                Chat.connect('ws://' + window.location.host + '/WebSocketDemo/websocket/cha    t');
            } else {
                Chat.connect('wss://' + window.location.host + '/WebSocketDemo/websocket/ch    at');
            }
        };
		//发送消息
        Chat.sendMessage = (function() {
            var message = document.getElementById('chat').value;
            if (message != '') {
                Chat.socket.send(message);
                document.getElementById('chat').value = '';
            }
        });

        var Console = {};
		//在消息版面显示服务器发送过来的消息
        Console.log = (function(message) {
            var console = document.getElementById('console');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.innerHTML = message;
            console.appendChild(p);
            while (console.childNodes.length > 25) {
                console.removeChild(console.firstChild);
            }
            console.scrollTop = console.scrollHeight;
        });

        Chat.initialize();


        document.addEventListener("DOMContentLoaded", function() {
            // Remove elements with "noscript" class - 

如果要利用websocket按照前面实现消息提醒的话,可以在后来创建一个监听器和定时器,在应用启动的时候就初始化定时器,定时在后台检测数据有没有发生变化,有变化之后随时调用websocket的方法,将数据推送给客户端。

下面是监听器和定时器的代码

监听器

    package util;

    import java.util.Calendar;
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;

    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;



    /**
     * 系统启动时的监听类 初始化系统数据
     * 
     */
    public class InitListener implements ServletContextListener {
	
     public void contextDestroyed(ServletContextEvent arg0) {
      // TODO Auto-generated method stub
      // context销毁时,销毁初始化数据
     }
     public void contextInitialized(ServletContextEvent event) {
      // TODO Auto-generated method stub
      try {
	  System.out.println("初始化监听...");
	  goTimer();
	  System.out.println("初始化完毕");
      } catch (Exception e) {
       System.out.println("失败:" + e.getMessage());
      }
     }
     private void goTimer() {
      Timer timmerTask = new Timer();
      Calendar calEnviron = Calendar.getInstance();
      // 每天的02:00.am开始执行
      calEnviron.set(Calendar.HOUR_OF_DAY, 17);
      calEnviron.set(Calendar.MINUTE, 01);
      calEnviron.set(Calendar.SECOND, 00);
      // date为制定时间
      Date dateSetter = new Date();
      dateSetter = calEnviron.getTime();
      // nowDate为当前时间
      Date nowDateSetter = new Date();
      // 所得时间差为,距现在待触发时间的间隔
      long intervalEnviron = dateSetter.getTime() - nowDateSetter.getTime();
      if (intervalEnviron < 0) {
       calEnviron.add(Calendar.DAY_OF_MONTH, 1);
       dateSetter = calEnviron.getTime();
       intervalEnviron = dateSetter.getTime() - nowDateSetter.getTime();
      }
      //每5秒执行一次
      timmerTask.schedule(new UseTimer(), intervalEnviron, 5 * 1000);
     }
    }

定时器

      package util;

      import java.util.Timer;
      import java.util.TimerTask;

      import com.myj.websocket.servlet.ChatAnnotation;


      /**
       * 被调用执行类
       * 
       */
      public class UseTimer extends TimerTask {
	
	private int num = 0;
	
	 Timer timer = new Timer();
	 
	 public UseTimer(){
		 
	 }
	 
	 public UseTimer(Timer timer) {
	  this.timer = timer;
	 }
	 
	 public Timer getTimer() {
		return timer;
	}

	public void setTimer(Timer timer) {
		this.timer = timer;
	}

	/*
	  * 被调用具体的方法
	  */
	 public void run() {
		//模拟数据发生变化
		 num++;
		 //调用websocket的接口推送数据
		ChatAnnotation.broadcast("你有"+num+"条消息!");
	 }
	 
      }

你可能感兴趣的:(web,消息推送)