Web在线聊天室 --- 服务器中换收发消息

Posted 满眼*星辰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web在线聊天室 --- 服务器中换收发消息相关的知识,希望对你有一定的参考价值。

目录

搭建服务器框架

客户端

        sendMessage() 
          let message = 
            userId: this.user.userId,
            nickName: this.user.nickName,
            channelId: this.curChannelId,
            content: this.curInputMessage,
          
          this.websocket.send(JSON.stringify(message));
          console.log("send success! " + message.content);
          this.curInputMessage = "";
        ,
        initWebSocket() 
          // 登陆成功后, 初始化 WebSocket 对象
          if('WebSocket' in window)
            this.websocket = new WebSocket("ws://localhost:8080/java_chatroom/message/" + this.user.userId);
            console.log("link success")
          else
              alert('Not support websocket')
          
           //连接发生错误的回调方法
          this.websocket.onerror = function () 
            alert("连接发生错误!");
          ;

          //连接成功建立的回调方法
          this.websocket.onopen = function (event) 
            console.log("连接建立成功");
          

          //接收到消息的回调方法
          this.websocket.onmessage = function (event) 
            console.log("recv success! " + event.data);
            let message = JSON.parse(event.data);
            // let length = app.messages.length;
            // app.messages[length] = message;
            app.messages.push(message);
            // 不要忘了需要筛选指定的消息
            app.curChannelMessages = app.messages.filter((message, i) => 
              if (message.channelId == app.curChannelId) 
                return true;
              
              return false;
            );
          

          //连接关闭的回调方法
          this.websocket.onclose = function () 
            console.log("服务器断开连接");
          

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

服务器端

这里用到WebSocket技术,先写一个服务器端的框架,然后再进行补充填写

package org.example.servlet;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * Created with IntelliJ IDEA.
 * Description:服务器websocket
 * User: starry
 * Date: 2021 -05 -27
 * Time: 8:54
 */

@ServerEndpoint("/message/userId")
public class MessageWebsocket 

    @OnOpen
    public void onOpen(@PathParam("userId") Integer userId, Session session) 
        System.out.println("建立连接" + userId);
    

    @OnMessage
    public void onMessage(Session session, String message) 
        System.out.println("接收到的消息:" + message);
    

    @OnClose
    public void onClose(Session session) 
        System.out.println("关闭连接");
    

    @OnError
    public void onError(Throwable throwable) 
        System.out.println("出错了");
        throwable.printStackTrace();
    



实现效果

我们在页面上输入hello,然后点击发送

查看本地idea上输出的信息

没有问题,建立连接后,并收到了消息的所有属性,内容为“hello”

收发消息

编写messageCenter保存websocket需要的信息

编写一个model类messageCenter,用来把所有的在线用户的session保存在map集合中

  1. 客户端登录后与服务器端建立连接,这里是websocket的session进行建立
  2. 客户端发送消息的时候,把要发送的message对象发送给服务器端
  3. 服务器端调用onmessage方法通过websocket的session转发此条消息给所有在线的客户端
  4. 前端接收到消息的回调函数
  5. 前端把返回来的消息根据频道id来过滤数据并显示到对应频道房间内
package org.example.model;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:保存websocket需要的信息(所有客户端session)
 * User: starry
 * Date: 2021 -05 -27
 * Time: 9:48
 */
public class MessageCenter 

    /**
     * 支持线程安全的map结构,并且满足高并发(读写,读读并发,写写互斥,加锁粒度)
     */
    private static final ConcurrentHashMap<Integer, Session>  clients = new ConcurrentHashMap<>();

    /**
     * websocket建立连接时,添加用户id和客户端session,保存起来
     */
    public static void addOnlineUser(Integer userId, Session session) 
        clients.put(userId,session);
    

    /**
     * 关闭websocket连接,和出错时,删除客户端session
     */
    public static void delOnlineUser(Integer userId) 
        clients.remove(userId);
    

    /**
     * 接收到某用户的消息时,转发到所有客户端
     */
    public static void sendMessage(String message) 
        try 
            //遍历map的所有数据,然后转发到所有session对象中
            Enumeration<Session> e = clients.elements();
            while (e.hasMoreElements()) 
                Session session = e.nextElement();
                session.getBasicRemote().sendText(message);
            
         catch (IOException ioException) 
            ioException.printStackTrace();
        
    




但是存在一个消息转发所有客户端,存在新能问题如果接收到的信息数量m很多,同时在线的用户数量n也很多,那么要转发的次数就是m*n次,每个接收消息都是一个线程,都要等待websocket中的onmessage回调方法执行完,性能差

优化(使用阻塞队列的方式解决:并行并发的执行任务提交和执行任务)

服务器端调用该类

package org.example.servlet;

import org.example.model.MessageCenter;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * Created with IntelliJ IDEA.
 * Description:服务器websocket
 * User: starry
 * Date: 2021 -05 -27
 * Time: 8:54
 */

@ServerEndpoint("/message/userId")
public class MessageWebsocket 

    @OnOpen
    public void onOpen(@PathParam("userId") Integer userId, Session session) 
        // 1.把每个客户端的session都保存起来,之后转发消息到所有客户端要用
        MessageCenter.addOnlineUser(userId,session);
        // 2.查询本客户端(用户)上次登录前的消息(数据库查)
        System.out.println("建立连接" + userId);
    

    @OnMessage
    public void onMessage(Session session, String message) 
        // 1.遍历保存的所有session,每个都发送消息
        // 2.消息还要保存在数据库
        MessageCenter.sendMessage(message);
        System.out.println("接收到的消息:" + message);
    

    @OnClose
    public void onClose(Session session) 
        // 本客户端关闭连接,要在之前保存的session集合中,删除
        System.out.println("关闭连接");
    

    @OnError
    public void onError(Throwable throwable) 
        System.out.println("出错了");
        throwable.printStackTrace();
        //和关闭连接的操作一样
    


实现效果

我用谷歌浏览器登录一个账号

再用ie浏览器登录一个账号

阿星发消息hello,阿甜发消息hi

谷歌浏览器:

ie浏览器:

对应两个账号可以在同一房间内收到彼此的消息

以上是关于Web在线聊天室 --- 服务器中换收发消息的主要内容,如果未能解决你的问题,请参考以下文章

Web在线聊天室(12) --- 收发消息(单例模式+阻塞式队列)

Web在线聊天室(12) --- 收发消息(单例模式+阻塞式队列)

即时聊天软件的原理,Web网页的聊天又是怎么实现的呢?

在线聊天室 --- 需求分析及准备工作

Web在线聊天室(10) --- 插入消息

Web在线聊天室(10) --- 插入消息