WebSocket

/ 工具和中间件 / 2 条评论 / 1276浏览

WebSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

index.jsp发起

<body>

<div style="width:400px;margin:20px auto;border:1px solid lightgray;padding:20px;text-align:center;">
  当前比特币价格:¥<span style="color:#FF7519" id="price">10000</span>
  <div style="font-size:0.9em;margin-top:20px">查看的人数越多,价格越高, 当前总共 <span id="total">1</span> 个人在线</div>
  <div style="color:silver;font-size:0.8em;margin-top:20px">以上价格纯属虚构,如有雷同,so what?</div>

</div>

</body>

<script type="text/javascript">
  var websocket = null;
  //判断当前浏览器是否支持WebSocket
  if ('WebSocket' in window) {
    websocket = new WebSocket("ws://localhost:8080/ws/bitcoinServer");

    //连接成功建立的回调方法
    websocket.onopen = function () {
      websocket.send("客户端链接成功");
    }

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
      setMessageInnerHTML(event.data);
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
      alert("WebSocket连接发生错误");
    };

    //连接关闭的回调方法
    websocket.onclose = function () {
      alert("WebSocket连接关闭");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
      closeWebSocket();
    }

  }
  else {
    alert('当前浏览器 Not support websocket')
  }

  //将消息显示在网页上
  function setMessageInnerHTML(innerHTML) {
    var bitcoin = eval("("+innerHTML+")");
    document.getElementById('price').innerHTML = bitcoin.price;
    document.getElementById('total').innerHTML = bitcoin.total;
  }

  //关闭WebSocket连接
  function closeWebSocket() {
    websocket.close();
  }

</script>

ServerManager

维护一个线程安全的集合servers, 用于保存浏览器发起连接请求而创建的BitCoinServer.

public class ServerManager {
    private static Collection<BitCoinServer> servers = Collections.synchronizedCollection(new ArrayList<BitCoinServer>());

    public static void broadCast(String msg){
        for (BitCoinServer bitCoinServer : servers) {
            try {
                bitCoinServer.sendMessage(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static int getTotal(){
        return servers.size();
    }
    public static void add(BitCoinServer server){
        System.out.println("有新连接加入! 当前总连接数是:"+ servers.size());
        servers.add(server);
    }
    public static void remove(BitCoinServer server){
        System.out.println("有连接退出! 当前总连接数是:"+ servers.size());
        servers.remove(server);
    }
}

BitCoinServer

用注解@ServerEndpoint("/ws/bitcoinServer")把它标记为一个WebSocket Server

@ServerEndpoint("/ws/bitcoinServer")
public class BitCoinServer {

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        ServerManager.add(this);
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    @OnClose
    public void onClose(){
        ServerManager.remove(this);
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);
    }

    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("发生错误");
        error.printStackTrace();
    }
}

BitCoinDataCenter

@WebServlet(name="BitCoinDataCenter",urlPatterns = "/BitCoinDataCenter",loadOnStartup=1)
public class BitCoinDataCenter extends HttpServlet implements Runnable{
    public void init(ServletConfig config){
        startup();
    }

    public  void startup(){
        new Thread(this).start();
    }
    @Override
    public void run() {
        int bitPrice = 100000;
        while(true){

            //每隔1-3秒就产生一个新价格
            int duration = 1000+new Random().nextInt(2000);
            try {
                Thread.sleep(duration);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //新价格围绕100000左右50%波动
            float random = 1+(float) (Math.random()-0.5);
            int newPrice = (int) (bitPrice*random);

            //查看的人越多,价格越高
            int total = ServerManager.getTotal();
            newPrice = newPrice*total;

            String messageFormat = "{\"price\":\"%d\",\"total\":%d}";
            String message = String.format(messageFormat, newPrice,total);
            //广播出去
            ServerManager.broadCast(message);
        }
    }
}

nginx中

如果做了nginx和tomcat整合的话,那么nginx 需要加上一段话,才能够正常的把webSocket请求交给tomcat,不然tomcat也不知道怎么处理

location /ws/ {
        proxy_pass http://127.0.0.1:11180;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
}