SpringBoot整合WebSocket实现聊天室系统

1.网络通信协议

  • TCP协议
    TCP协议是一种面向连接的协议,就是在通信之前需要先建立连接。举个列子,就像打电话一样,需要对方先接通,才能进行通话。
  • UDP协议
    UDP是面向无连接的协议,在通信的时候不需要建立连接,只需要直接将数据发出即可。
  • 安全性分析
    两者相比,TCP更加安全,更加可靠,TCP提供可靠传输服务,无差错,不丢失,不重复。UDP通信则可靠性低,因为是无连接的,同时不管信息是否已经传输成功,芷尽最大努力实现交付。
  • 效率比较
    TCP相对来说效率较低
    UDP传输效率更高,适用于高速传输和实时传输。

2.WebSocket

  • 什么是webSocket?
    WebSocket是一中在TCP连接上进行全双工通信的协议。
  • 什么是全双工通信
    在通信时双方可同时进行信息的传输,不需要等待另一方传输完毕再进行传输。

3.主要代码

  • 主要前端代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
    <script src="/jquery.js" type="text/javascript"></script>
    <style>
        #message{
            width: 910px;
            height: 610px;
        }
        #box{
            float: left;
            width: 600px;
            height: 610px;
            display: none;
        }
        #box1{
            float: left;
            background-color: gray;
            width: 300px;
            height: 600px;
        }
        #chatArea{
            width: 600px;
            height: 300px;
            background-color: gainsboro;
        }
        #sendArea{
            width: 600px;
            height: 300px;
            background-color: coral;
        }
        #content{
            width: 500px;
        }
        #haoyou{
            height: 300px;
        }
        #userList{
            list-style: none;
        }
        #broadList{
            list-style: none;
        }
        #msgRight{
            width: 100%;
            text-align: right;
            color: coral;
        }
        #msgLeft{
            width: 100%;
            color: aqua;
        }
    </style>
    <script>
        var toName;
        var username;
        function showCat(name){
            toName = name;
            //现在聊天对话框
            $("#box").css("display","block");
            //清空聊天区
            $("#chatArea").html("");
            $("#chatMes").html("正在和<font face=\"楷体\">"+toName+"</font>聊天");

            //存储聊天记录 sessionStorge
            var chatData = sessionStorage.getItem(toName);
            if(chatData != null){
                //将聊天记录渲染到聊天去
                $("#chatArea").html(chatData);
            }

        }

        $(function (){
            $.ajax({
                url:"/user/getUsername",
                success:function (res){
                    username = res;
                    // $("#username").html("用户:"+res+"<span style='float: right;color: green'>在线</span>");
                },
                async:false
            });


           if("WebSocket" in window){
               //创建一个websocket
               var ws = new WebSocket("ws://localhost:8080/chat");
           }else{
               alert("该浏览器不支持websocket");
           }
            //给ws绑定事件
            ws.onopen = function (){
                //在建立连接后需要做什么事?
                //1.显示在线信息
                $("#username").html("用户:"+username+"<span style='float: right;color: green'>在线</span>");
            }
            //接收到服务端推送的消息后,触发这个事件
            ws.onmessage = function (evt){
                //获取服务端推送过来的消息
                var dataStr = evt.data;
                //将dataStr转换为json对象
                var res = JSON.parse(dataStr);
                console.log(res);
                //判断是否时系统消息
                if(res.system){
                    //系统消息
                    //1.好友列表展示
                    //2.系统广播展示
                    var names = res.message;
                    var userListStr = "";
                    var broadListStr = "";
                    for (var name of names) {
                        if(name != username){
                            userListStr += "<li><a οnclick='showCat(\""+name+"\")'>"+name+"</a></li>";
                            broadListStr +="<li>您的好友"+name+"已上线</li>";
                        }


                    }
                    //渲染好友列表和广播
                    $("#userList").html(userListStr);
                    $("#broadList").html(broadListStr);
                }else{
                    //不是系统消息
                    //将服务端推送的消息进行展示
                    var str = " <div id=\"msgLeft\">"+res.message+"</div>"

                    if(toName != res.fromName){
                        $("#chatArea").append(str);
                    }
                    $("#chatArea").append(str)
                    //存储数据
                    var chatData = sessionStorage.getItem(res.fromName);
                    if(chatData != null){
                        //拿到聊天记录
                        str = chatData+str;
                    }
                    sessionStorage.setItem(res.fromName,str);

                }
            }
            ws.onclose = function (){
                $("#username").html("用户:"+username+"<span style='float: right;color: red'>离线</span>");
            }

           //点击发送按钮
           $("#send").click(function (){
               //获取输入的内容
               var data = $("#content").val();
               //清空输入区的内容
               $("#content").val("");

               //将消息数据展示在聊天区
               var str = "<div id=\"msgRight\">"+data+"</div>"
               $("#chatArea").append(str);
               var json = {"toName":toName,"message":data};

               //将聊天内容保存
               //先拿到用户,看是否存在聊天记录
               //如果不存在聊天记录,将将当前的聊天存起来
               var chatData = sessionStorage.getItem(toName);
               if(chatData != null){
                   //拿到聊天记录
                   str = chatData+str;
               }
               sessionStorage.setItem(toName,str);
               //将数据发送给服务端
               ws.send(JSON.stringify(json));
           })
        })
    </script>
</head>
<body>
  <span id="username"></span>
  <div id="message">
      <div id="box">
          <span id="chatMes"></span>
          <div id="chatArea">

          </div>
          <div id="sendArea">
              <textarea id="content"></textarea>
              <button id="send">发送</button>
          </div>
      </div>
      <div id="box1">
          <div id="haoyou">
              <span style="background-color: aqua;font-size: 20px">好友列表</span>
              <ul id="userList">

              </ul>
          </div>

          <div>
              <span style="background-color: aqua;font-size: 20px">广播列表</span>
              <ul id="broadList">

              </ul>
          </div>
      </div>
  </div>


</body>
</html>
  • 主要的后端代码
    实体类
package com.dfc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {

    private String toName;
    private String message;
}
package com.dfc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;



//登录使用
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {

    private boolean flag;
    private String message;
}


package com.dfc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultMessage {

    private boolean isSystem;
    private String fromName;
    private Object message;//如果是系统推送消息是数组
}


package com.dfc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String username;
    private String password;
}

controller层

package com.dfc.controller;

import com.dfc.pojo.Result;
import com.dfc.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public Result login(User user, HttpSession session){
        Result result = new Result();
        if(user != null && "123".equals(user.getPassword())){
            result.setFlag(true);
            //将用户保存到session中
            session.setAttribute("user",user.getUsername());
        }else{
            result.setFlag(false);
            result.setMessage("登录失败");
        }
        return result;
    }

    @RequestMapping("/getUsername")
    public String getUsername(HttpSession session){
        String username = (String) session.getAttribute("user");
        return username;
    }
}

WebSocket主要实现代码

package com.dfc.ws;

import com.dfc.pojo.Message;
import com.dfc.utils.MessageUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value="/chat",configurator = GetHttpSessionConfigurator.class)

@Component
public class ChatEndpoint {

    //定义一个对象用来存储客户端对象对应的ChatEndpoint  双列
    private static Map<String,ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();
    //声明session对象,通过该对象可以发送消息给指定用户
    private Session session;
    //声明一个HttpSession对象,因为我们之前在HttpSession中存储了用户名
    private HttpSession httpSession;

    //连接建立时被调用
    @OnOpen
    public void onOpen(Session session, EndpointConfig config){
       //将局部session对象赋值给成员session
        this.session = session;
        //获取HttpSession对象
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;

        //从httpSession对象中获取用户名
        String username = (String) httpSession.getAttribute("user");
        //将当前对象存储到容器中
        onlineUsers.put(username,this);
        //将当前在线用户名推送给所有的客户端
        //1.获取消息
        String message = MessageUtils.getMessage(true,null,getNames());
        //2.调用方法进行系统消息的推送
        broadAllUsers(message);
    }
    //从onlineUsers中得到在线用户名字
    private Set<String> getNames(){

        return onlineUsers.keySet();
    }
    private void broadAllUsers(String message){
        try{
            //要将该消息推送给所有的客户端
            Set<String> names = onlineUsers.keySet();
            for (String name : names) {
                ChatEndpoint chatEndpoint = onlineUsers.get(name);
                chatEndpoint.session.getBasicRemote().sendText(message);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
    //接收到客户端发送的数据被调用
    @OnMessage
    public void onMessage(String message,Session session){
        //将message转换成message对象
        ObjectMapper mapper = new ObjectMapper();
        try {
            Message mess = mapper.readValue(message, Message.class);
            //获取接收数据的用户
            String toName = mess.getToName();
            //获取消息数据
            String data = mess.getMessage();
            //获取当前登录的用户
            String username = (String) httpSession.getAttribute("user");
            //获取推送给指定用户的消息格式的数据
            String resultMessage = MessageUtils.getMessage(false,username,data);
            //发送数据
            onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //连接关闭时被调用
    @OnClose
    public void onClose(Session session){
        String username = (String) httpSession.getAttribute("user");
        //处理用户已经下线
        //从容器中删除指定的用户
        onlineUsers.remove(username);
        //获取推送的消息
        String message = MessageUtils.getMessage(true,null,getNames());
        broadAllUsers(message);
    }
}

//需要将这个类托管给spring来管理
package com.dfc.ws;

import lombok.val;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        //将httpSession对象存储到配置对象中
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}


//消息处理工具类
package com.dfc.utils;

import com.dfc.pojo.ResultMessage;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MessageUtils {

    public static String getMessage(boolean isSystemMessage,String fromName,Object message){
        try {
            ResultMessage resultMessage = new ResultMessage();
            resultMessage.setSystem(isSystemMessage);
            resultMessage.setMessage(message);
            if(fromName != null){
                resultMessage.setFromName(fromName);
            }
            ObjectMapper mapper = new ObjectMapper();

            return mapper.writeValueAsString(resultMessage);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

4.效果展示

在这里插入图片描述再浏览器中登录一个用户名为 123的用户,这是自己的用户名,然后再用另一个浏览器登录另一个账户,将会再好友列表中展示处已经上线的用户。这是服务器推送的广播消息
在这里插入图片描述

之后点击想要发送消息的用户就可以发送消息了
页面做的比较丑,请忽略
做了一个稍微能看的下去的
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201115212726176.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMDI0OTI1,size_16,color_FFFFFF,t_70#pic_center

代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>D-F</title>
    <script src="js/jquery.js"></script>
    <style>
        #box{
            display: flex;
            flex-direction: row;
        }
        #userList{
            width: 140px;
            height: 600px;
            border-radius: 10px;
            border: 1px solid gainsboro;
        }
        #chatArea{
            width: 300px;
            height: 600px;
            border-radius: 10px;
            border: 1px solid gainsboro;
        }
        #boradArea{
            width: 200px;
            height: 600px;
            border-radius: 10px;
            border: 1px solid gainsboro;
        }
        #userList-head{
            display: flex;
            flex-direction: row;
            align-items: center;
            border-radius: 10px;
            background-color: antiquewhite;    
        }
        #boradArea-head{
            display: flex;
            flex-direction: row;
            /* width: 100%;
            height: 20px; */
            border-radius: 10px;
            align-items: center;
            background-color: antiquewhite;
            box-sizing: border-box;   
        }
        #userList-head img{
            width: 17px;
            height: 17px;
        }
        #common{
            display: none;
            width: 140px;
            height: 583px;
            border-radius: 10px;
        }
        #userInfo{
            background-color: antiquewhite;
            border-radius: 10px;
            text-align: center;
        }
        /* 聊天区域 */
        #chatContent{
            width: 100%;
            height: 450px;
            /* border-bottom:1px solid black; */
        }
        #right{
            width: 100%;
            display: flex;
            justify-content: end;
        }
        #right-item img{
            width: 20px;
            height: 20px;
        }
        /* 接收消息展示 */
        #left{
            width: 100%;
            display: flex;
            justify-content: start;
        }
        #left-item img{
            width: 20px;
            height: 20px;
        }
        #message-box{
            display: flex;
            flex-direction: row;
            background-color: rgb(252, 251, 251);
        }
        #message{
            width: 80%;
            height: 30px;
            border-radius: 20px;
        }
        #submit{
            width: 15%;
            height: 20px;
            margin-top: 8px;
            background-color: rgb(48, 250, 48);
            text-align: center;
            border-radius: 20px;
        }
        #diy{
            display: flex;
            flex-direction: row;
            justify-content: space-around;
            background-color: rgb(252, 251, 251);
        }
        #diy-item{
            margin-top: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        #diy-item img{
            width: 20px;
            height: 20px;
        }

        /* 展示好友列表 */
        #flist{
            list-style: none;
            line-height: 40px;
        }
        #flist a{
            text-decoration: none;
        }
        #myfont{
            position: fixed;
            width: 200px;
            height: 100px;
            background-color: azure;
            top: 350px;
            left: 200px;
            display: none;
        }
    </style>

    <script>
        //点击好友进行聊天
        function show(name){
            $("#name").html(name);
            $("#action").css("display","block");
            $("#other").css("display","none");
        }
       //弹出显示框
        function change(index){
            if(index == 1){
               $("#myfont").css("display","block");  
            }
        }
        $(function(){
            var flag = true;
            $("#down").click(function(){
            if(flag){
                 //获取下拉图标id
                $("#common").css("display","block");
                $("#down").attr("src","img/up.png");
                flag = false;
            }else{
                //点击上拉按钮收回好友列表
                $("#common").css("display","none");
                $("#down").attr("src","img/down.png");
                flag = true;
            }
            });

            //点击返回返回首页
            $("#back").click(function(){
                $("#action").css("display","none");
                $("#other").css("display","block");
            });
            //点击改变字体
            $("#commit").click(function(){
                var data = $("#fontValue").val();
                $("#myfont").css("display","none");  
                $("#msg").css("font-size",data+"px");
                $("#msg1").css("font-size",data+"px");
            })
        });
    </script>
</head>
<body>
    <div id="box">
        <div id="userList">
            <div id="userList-head">
                <span id="title">在线好友</span>
                <img src="img/down.png"/ id="down">
            </div>
            <!-- 展示好友列表 -->
            <div id="common">
                <ul id="flist">
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('张三丰')">张三丰</a></li>
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('张无忌')">张无忌</a></li>
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('赵无极')">赵无极</a></li>
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('赵敏')">赵敏</a></li>
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('迪丽热巴')">迪丽热巴</a></li>
                    <li><img src="img/girl.png" style="width: 20px;height: 20px;" alt=""><a onclick="show('邓超')">邓超</a></li>
                </ul>
            </div>
        </div>
        <div id="chatArea">
            <!-- 聊天区域 -->
            <!-- 聊天头部 -->
            <div id="action"  style="display: none;">
                <div id="userInfo">
                    <img src="img/back.png" style="width: 20px;height:20px; float: left;" id="back"/>
                    正在和<font style="font-weight:200;color: chartreuse;" id="name"></font>聊天
                </div>
                <div id="chatContent">
                    <div id="right">
                        <div id="right-item">
                            <span id="msg">你好</span>
                            <img src="img/boy.png"/>
                        </div>
                    </div>
                    <div id="left">
                        <div id="left-item">
                            <img src="img/girl.png"/>
                            <span id="msg1">你好</span>
                        </div>
                    </div>
                </div>
                <!-- 消息输入框 -->
                <div id="message-box">
                    <textarea id="message"></textarea>
                    <div id="submit">发送</div>
                </div>
                <!-- 用户自定义选项 -->
                <div id="diy">
                    <div id="diy-item" onclick="change(1)">
                        <img src="img/font.png"/>
                        <span>字体</span>
                    </div>
                    <div id="diy-item" onclick="change(2)">
                        <img src="img/pifu.png"/>
                        <span>颜色</span>
                    </div>
                    <div id="diy-item" onclick="change(3)">
                        <img src="img/add.png"/>
                        <span>更多</span>
                    </div>
                </div>
            </div>

            <!-- 没有进行聊天的时候展示页面-->
            <div id="other">欢迎来到D-f聊天室</div>
            <!-- 选择字体 -->
            <div id="myfont">
                <input type="number" max="40" min="5" id="fontValue" placeholder="输入字体大小"/>
                <input type="submit" value="确认" id="commit"/>
            </div>
        </div>
        <div id="boradArea">
            <div id="boradArea-head">
                <span id="title">广播消息</span>
            </div>
        </div>
    </div>
</body>
</html>

`

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿成成毕设

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值