2021-03-10 16:38

通过WebSocket-实现服务器和浏览器客户端双向即时通信-web聊天

码自答

JavaEE

(1463)

(0)

收藏

利用WebSocket实现服务器和浏览器客户端双向即时通信

无标题.jpg

每个浏览器客户端和服务器连接,会创建一个新的WebSocket对象,通过sessionId来对客户端进行唯一标识

1 index.jsp

   负责和服务器建立连接,向服务器发送数据

<%--
  Created by IntelliJ IDEA.
  User: wanmait
  Date: 2021/3/9
  Time: 15:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>万码学堂wanmait</title>
    <meta http-equiv="author" content="万码学堂wanmait">
    <style type="text/css">
      #sendDiv{
        border: 1px solid gray;
        width: 800px;
        border-radius: 10px;
        margin: auto;
        height: 150px;
        display: table-cell;
        vertical-align: middle;
      }
    </style>
  </head>
  <body>
  <h1>WebSocket测试</h1>
  <div id="sendDiv">
    sessionId:${pageContext.session.id}
    <br>
    <input type="button" value="连接服务器" onclick="connectWebSocket()">
    <br>
    向服务器发送内容:<input id="sendText" type="text" />
    <input type="button" value="发送" onclick="sendMessage()">
    <br>
    <input type="button" value="关闭连接" onclick="closeWebSocket()">
  </div>

  <hr />
  <div id="message">
    <h1>message:</h1>
  </div>


  <script type="text/javascript">
    var websocket;//用来存放该客户端和服务器之间的连接对象
    //通过该连接对象发送和接收数据

    //连接服务器 WebSocket
    //url-->ws://host:tomcat端口/项目名/WebSocket类的@ServerEndpoint注解
    function connectWebSocket() {
      var sessionId = "${pageContext.session.id}";//获得当前会话的sessionId  作为该客户端的唯一标识  连接时发送给服务器WebSocket

      //不同浏览器的连接语句
      if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:8080/socketTest/webSocket/" + sessionId);
      } else if ('MozWebSocket' in window) {
        websocket = new MozWebSocket("ws://localhost:8080/socketTest/webSocket/" + sessionId);
      } else {
        websocket = new SockJS("localhost:8080/socketTest/webSocket/" + sessionId);
      }

      //连接成功建立的回调方法  连接成功 自动执行该方法
      websocket.onopen = function () {
        appendMessageToDiv("连接成功")
      }

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

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

      //设置接收到服务器发送消息的回调方法  接收到服务器发送的消息  调用acceptMessage
      websocket.onmessage = acceptMessage;
      //设置该回调方法 表示当服务器发送消息 该acceptMessage方法立即执行
    }
    //connectWebSocket方法的结束

    //id为message的div中间  追加显示信息
    function appendMessageToDiv(mes)
    {
      var messageDiv = document.getElementById("message");
      messageDiv.innerHTML += "<h4>"+mes+"</h4>";
    }

    //接收消息的方法
    function acceptMessage(event)
    {
      //event.dataWebSocket服务器发送的内容
      appendMessageToDiv(event.data);
      //接收的内容显示到message   div
    }

    //关闭WebSocket连接
    function closeWebSocket()
    {
      websocket.close();//断开和服务器的WebSocket连接
    }

    //发送消息
    function sendMessage()
    {
      var message = document.getElementById('sendText').value;//获得文本框输入的内容
      websocket.send(message);//发送给服务器
    }

    //添加浏览器关闭事件,当浏览器关闭时 断开连接。
    window.onbeforeunload = function () {
      websocket.close();//断开和服务器的WebSocket连接
    }
  </script>
  </body>
</html>

2 WebSocket.java

  服务器等待客户端连接,接收和发送数据

/*
author:万码学堂 wanmait
 */
package com.wanmait.socketTest.util;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//每次浏览器客户端连接  会自动创建一个新的WebSocket对象
@ServerEndpoint("/webSocket/{sessionId}")
public class WebSocket {
    private Session session;//该Session不等同于HttpSession  是WebSocket的一次会话
    private String sessionId;//存放客户端浏览器连接时发送的sessionId  是浏览器客户端的唯一标识

    //每个浏览器客户端来连接  自动调用该方法
    @OnOpen()
    public void onOpen(@PathParam("sessionId") String sessionId, Session session) throws IOException {
        this.sessionId = sessionId;//浏览器客户端发送的sessionId  客户端的唯一标识
        this.session = session;
        WebSocketUtils.getWebSocketUtils().addClient(sessionId,this);//将客户端连接创建的新WebSocket对象保存到Map 中间   客户端发送的sessionId是唯一标识
        System.out.println(sessionId+":客户端连接成功");
    }

    //浏览器客户端webSocket关闭  即客户端执行websocket.close()方法  该onClose方法 自动执行
    @OnClose()
    public void onClose() throws IOException {
        WebSocketUtils.getWebSocketUtils().remove(sessionId);//从map中间  移除该浏览器客户端生成的WebSocket对象
        System.out.println(sessionId+":客户端关闭");
    }

    //浏览器客户端发送消息  服务器接收 即浏览器执行websocket.send()方法  服务器该方法  自动执行
    //参数message 是浏览器客户端发送的数据
    @OnMessage()
    public void onMessage(String message) throws IOException {
        System.out.println(sessionId+"客户端发送:"+message);
    }

    //客户端服务器出现异常  执行该方法
    @OnError()
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    //向该WebSocket对应的浏览器客户端发送数据
    public void sendMessageToClient(String message)
    {
        this.session.getAsyncRemote().sendText(message);
    }
}

3 WebSocketUtils.java

   负责处理每个浏览器客户端连接的WebSocket对象

package com.wanmait.socketTest.util;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WebSocketUtils {
    //单一实例
    public WebSocketUtils(){}
    private static WebSocketUtils webSocketUtils = new WebSocketUtils();
    public static WebSocketUtils getWebSocketUtils()
    {
        return webSocketUtils;
    }

    private Map<String, WebSocket> clients = new ConcurrentHashMap<>();
    //每连接一个客户端  会自动创建一个新的WebSocket对象   clients用来存放所有的WebSocket对象
    //浏览器客户端发送的sessionId为clients的key关键字  创建的WebSocket对象为clients的value值

    //往clients集合中间  增加新的WebSocket对象  新的浏览器客户端
    public void addClient(String sessionId,WebSocket webSocket)
    {
        clients.put(sessionId,webSocket);
    }

    //将关闭的客户端创建的WebSocket对象  从clients中间移除
    public void remove(String sessionId)
    {
        clients.remove(sessionId);//根据key从map中间移除
    }

    //向其中的某个客户端发送数据
    public void sendMessageTo(String message,String sessionId)
    {
        WebSocket webSocket = clients.get(sessionId);//从map中间获得sessionId对应的客户端
        webSocket.sendMessageToClient(message);//向客户端发送数据
    }

    //向所有客户端发送数据
    public void sendMessageToAll(String message)
    {
        //clients.values()获得map中间所有的value值 即所有的客户端WebSocket对象
        for(WebSocket webSocket:clients.values())
        {
            webSocket.sendMessageToClient(message);//向客户端发送数据
        }
    }
}

注意:

需要下载javax.websocket-api.jar包

image.png

点击连接服务器,网页和服务器控制台会显示连接成功

点击发送按钮,将文本框内容发送给服务器,服务器控制台显示浏览器客户端发送的信息



测试以下,用SendNoticeServlet向所有的客户端发送消息

package com.wanmait.socketTest.controller;

import com.wanmait.socketTest.util.WebSocketUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/SendNoticeServlet")
public class SendNoticeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String message = "万码学堂,wanmait,做最负责人的教育";

        WebSocketUtils.getWebSocketUtils().sendMessageToAll(message);
    }
}

访问SendNoticeServlet,所有的浏览器客户端都会收到发送的信息

image.png


0条评论

点击登录参与评论