zoukankan      html  css  js  c++  java
  • 分享基于 websocket 网页端聊天室

    博客地址: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{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{300px;margin-right:24px;} .box .user{200px;} .box
          .btn{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;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;618px;outline:none;float:left;font-family:"微软雅黑";} .box
          .send{font-size:14px;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

  • 相关阅读:
    Flutter
    Flutter
    Flutter
    使用Sublime Text 3作为React Native的开发IDE
    新建React Native项目步骤
    【一些容易忘记的node的npm命令】【收集】
    前端异步的一种方法库:axios
    【react】兄弟组件的通信方式,传统非redux
    【js】关于闭包和匿名函数
    【js】手机浏览器端唤起app,没有app就去下载app 的方法
  • 原文地址:https://www.cnblogs.com/ainyi/p/10398665.html
Copyright © 2011-2022 走看看