博客地址:https://ainyi.com/#/67

有一个月没有写博客了,也是因为年前需求多、回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!!

主题

基于 websocket 网页端聊天室

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工 (full-duplex) 通信——允许服务器主动发送信息给客户端。

使用 java 开发后台

需要导入一个jar包:javax.websocket-api-1.0-rc4.jar

后台代码

package com.krry.socket;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

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;

//该注解用来指定一个URI,客户端可以通过这个URI来连接到WebSocket。类似Servlet的注解mapping。无需在web.xml中配置。
@ServerEndpoint("/websocket")
public class MyWebSocket {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

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

    /**
     * 连接建立成功调用的方法
     * @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);

        //群发消息
        for(MyWebSocket item: webSocketSet){
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

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

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

前端代码

注意

前端需要实现这几个方法:

  // 注册事件
  // 监听打开连接
  ws.onopen = function(){
    openWs();
  };
  // 监听消息
  ws.onmessage = function(event){
    msgWs(event);
  };
  // 监听关闭连接
  ws.onclose = function(){
    closeWs();
  };
  // 监听发送错误
  ws.onerror = function(){
    errorWs();
  };

具体代码

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!doctype html>
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <meta name="keywords" content="">
    <meta name="description" content="">
    <title>
      基于Java服务器端的消息主动推送技术揭秘 --krry
    </title>
    <link rel="stylesheet" href="css/animate.css" />
    <link rel="stylesheet" type="text/css" href="css/sg.css" />
    <style>
      *{margin:0;padding:0;} body{background:url("images/5.jpg");background-size:cover;}
      h1{margin-top:50px;text-align:center;color:#fff;text-shadow:1px 1px 1px
      #000;font-family:-webkit-body;font-size:24px;} .box{width:700px;margin:20px
      auto;} .box span{color:#f60;font-size:16px;font-family:"微软雅黑";} .box .shu{text-indent:1em;height:24px;font-family:"微软雅黑";border:0;outline:none;font-size:14px;}
      .box .add{width:300px;margin-right:24px;} .box .user{width:200px;} .box
      .btn{width:80px;height:34px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;margin-top:20px;font-size:16px;font-family:"微软雅黑";}
      .box .area{line-height: 29px;height:280px;width:680px;padding:10px;overflow:auto;font-size:16px;font-family:"微软雅黑";margin:20px
      0;outline:none;box-shadow:1px 2px 18px #000} .box .setex{text-indent:1em;height:28px;border:1px
      solid #6c0;width:618px;outline:none;float:left;font-family:"微软雅黑";} .box
      .send{font-size:14px;width:80px;height:30px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;font-family:"微软雅黑";}
    </style>
  </head>

  <body>
    <h1>
      基于Java服务器端的消息主动推送技术揭秘 --krry
    </h1>
    <div class="box">
      <span>
        服务器地址:
      </span>
      <input type="text" class="shu add" value="www.ainyi.com/krry_NetChat/websocket"
      readonly/>
      <span>
        用户名:
      </span>
      <input type="text" class="shu user" value="匿名" />
      <input type="button" value="连接" class="btn" />
      <div class="area" id="boxx">
      </div>
      <div class="c_cen">
        <input type="text" class="setex" />
        <input type="button" value="发送" class="send">
      </div>
    </div>
    <script src="js/jquery-1.11.1.min.js">
    </script>
    <script src="js/sg.js">
    </script>
    <script src="js/sgutil.js">
    </script>
    <script>
      var close = true;
      var ws;
      $(function() {
        $(".c_cen").hide();
        //首先判断浏览器是否支持webSocket,支持h5的浏览器才会支持
        if (window.WebSocket) {
          printMsg("您的浏览器支持WebSocket,您可以尝试连接到聊天服务器!", "OK");
        } else {
          printMsg("您的浏览器不支持WebSocket,请选择其他浏览器!", "ERROR");
          //设置按钮不可点击
          $(".btn").attr("disabled", "true");
        }
      });
      //打印信息
      function printMsg(msg, msgType) {
        if (msgType == "OK") {
          msg = "<span style='color:green'>" + msg + "</span>";
        }
        if (msgType == "ERROR") {
          msg = "<span style='color:red'>" + msg + "</span>";
        }
        $(".area").append(msg + "<br/>");
        var boxx = document.getElementById("boxx");
        boxx.scrollTop = boxx.scrollHeight; //使滚动条一直在底部
      }

      //打开Socket
      function openWs() {
        printMsg("链接已建立", "OK");
        ws.send("【" + $(".user").val() + "】已进入聊天室");
        $(".c_cen").show();
      }

      //接收消息的时候
      function msgWs(e) {
        printMsg(e.data);
      }
      //关闭连接
      function closeWs() {
        $(".btn").val("连接");
        $(".c_cen").hide();
      }
      //产生错误
      function errorWs() {
        printMsg("您与服务器连接错误...", "ERROR");
      }

      //点击发送按钮
      $(".send").click(function() {
        var text = $(".setex").val();
        if (text == null || text == "") return;
        $(".setex").val("");
        ws.send("【" + $(".user").val() + "】:" + text);
      });

      //点击连接
      $(".btn").click(function() {
        if ($(".add").val() && $(".user").val()) {
          if (close) {
            printMsg("正在准备连接服务器,请稍等...");
            var url = "wss://" + $(".add").val();
            if ("WebSocket" in window) {
              ws = new WebSocket(url);
            } else if ("MozWebSocket" in window) {
              ws = new MozWebSocket(url);
            }
            //已连接
            $(".btn").val("断开");
            close = false;

            //注册事件
            ws.onopen = function() {
              openWs();
            };
            ws.onmessage = function(event) {
              msgWs(event);
            };
            ws.onclose = function() {
              closeWs();
            };
            ws.onerror = function() {
              errorWs();
            };

            //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
            window.onbeforeunload = function() {
              ws.send("【" + $(".user").val() + "】离开了聊天室");
              close = true;
              ws.close();
            };

          } else {
            ws.send("【" + $(".user").val() + "】离开了聊天室");
            close = true;
            ws.close();
          }
        } else {
          $.tmDialog.alert({
            open: "left",
            content: "服务器地址和用户名不能为空哦...",
            title: "提示哦~~~"
          });
        }
      });

      //回车键
      $(".setex").keypress(function(event) {
        if (event.keyCode == 13) {
          $(".send").trigger("click");
        }
      });
    </script>
  </body>

</html>

到这里大功告成

聊天方法

  1. 打开两个窗口输入项目地址进行聊天
  2. 可以把链接发给朋友打开,进行聊天

来一波截图

移动端

在线演示

PC 端:https://www.ainyi.com/krry_NetChat
移动端:https://www.ainyi.com/krry_NetChatPho

打完收工~

博客地址:https://ainyi.com/#/67

02-19 06:56