Java+WebSocket制作聊天室(私聊与群聊)

导读:本篇文章讲解 Java+WebSocket制作聊天室(私聊与群聊),文章出自:https://blog.csdn.net/weixin_43316702/article/details/118469339希望对大家有帮助,欢迎收藏,转发!站点地址:www.javazhiyin.com.com

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

WebSocket是一种在单个TCP连接上进行全双工通信的协议。顾名思义我们的使用经常就是用于进行实时的消息推送,今天我们是用SpringBoot来实现简易的聊天室,其中包括私聊和群聊。
首先是先把自己需要的界面弄好,我就简单的制作了一下
在这里插入图片描述
讲解一下界面的设计,首先我们是需要有发送方和接收方,我是用用户id来记录,然后他们都是需要用session来保存,相当于是系统的用户在线状态嘛,所以首先步骤一,要进行用户登录(创建连接),然后就是请求获取设计好的websocket服务,连接好了服务就是相当于在一个聊天室内,如果没有指定接收方时就是群聊,有接收方就是私聊。
接下来就是代码啦,先给出html的代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html>
	<head>
		<meta charset="utf-8" />
		<title>WebSocket</title>
	</head>
	<script src="jquery-3.4.1.min.js"></script>
	<script type="text/javascript" src="websocket.js"></script>
	<script type="text/javascript" src="mine.js"></script>
	<style>
		#responseText{
			width:237px;
			height: 200px;
			border: 1px solid;
		}
		.userId{
			width: 110px;
		}
	</style>
	<body>
		<input type="text" id="sendId" class="userId"  placeholder="输入连接的用户ID" />
		<button onclick="checkTextById('sendId');createUser();">创建连接</button>
		<button onclick="checkTextById('sendId');openSocket();">请求连接</button><br /><br />
		<input type="text" id="requestText" placeholder="输入你的内容" />
		<input type="text" id="receiveId" class="userId" placeholder="输入接收的用户ID" />
		<button onclick="checkTextById('requestText');sendMessage();">发送消息</button><br /><br />
		<!--显示结果-->
		<div id="responseText">聊天室:</div><br />
		<span id="serverTxt">尚未连接服务</span>
	</body>
</html>

js代码:

//websocket.js

var socket;//注意:360浏览器不支持websocket
function openSocket() {
    if(typeof(WebSocket) == "undefined") {
        console.log("您的浏览器不支持WebSocket");
    }else {
        console.log("您的浏览器支持WebSocket");
        //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
        var userId = document.getElementById('sendId').value;
        var socketUrl = "ws://localhost:19429/webSocket/" + userId;
        console.log(socketUrl);
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打开事件
        socket.onopen = function () {
            console.log("websocket已打开");
            document.getElementById('serverTxt').innerText = userId+"已连接websocket";
            //socket.send("这是来自客户端的消息" + location.href + new Date());
        };
        //获得消息事件
        socket.onmessage = function (msg) {
            var serverMsg = "收到服务端信息:" + msg.data;
            console.log("类型:" + typeof (msg.data));
            var msgJson = eval('(' + msg.data + ')');
            var div = document.createElement("div");
            var responseText = document.getElementById('responseText');
            var type = "(私聊)";
            if (null == msgJson.toUserId || msgJson.toUserId == "") {
                type = "(群聊)";
            }
            div.innerHTML = msgJson.userId+type+":"+msgJson.contentText;
            responseText.appendChild(div);
            console.log(serverMsg);
            //发现消息进入    开始处理前端触发逻辑
        };
        //关闭事件
        socket.onclose = function () {
            console.log("websocket已关闭");
            document.getElementById('serverTxt').innerText = "尚未连接服务";
        };
        //发生了错误事件
        socket.onerror = function () {
            console.log("websocket发生了错误");
        }
    }
}

//websocket发送消息
function sendMessage() {
    var toUserId = document.getElementById('receiveId').value;//接收的用户ID
    var userId = document.getElementById('sendId').value;//发送的用户ID
    var contentText = document.getElementById('requestText').value;
    if(typeof(WebSocket) == "undefined") {
        console.log("您的浏览器不支持WebSocket");
    }else {
        // console.log("您的浏览器支持WebSocket");
        if (userId.trim().length>0 && contentText.trim().length>0){
            var msg = '{"userId":"'+userId+'","toUserId":"'+toUserId+'","contentText":"'+contentText+'"}';
            console.log(msg);
            socket.send(msg);
            if (toUserId.trim().length>0){
                var responseText = document.getElementById('responseText');
                var div = document.createElement("div");
                div.innerHTML = userId+"(私聊):"+contentText;
                responseText.appendChild(div);
            }
            alert("发送成功!");
        }else {
            alert("发送失败!");
        }
   }
}


//mine.js

//通过id判断文本组件是否为空
function checkTextById(id) {
    var Txt = document.getElementById(id);

    //判断是否存在这个组件
    if (Txt!=null){
        if (Txt.value.trim().length==0){
           alert("内容不允许为空!");
        }
    }else{
        alert("id为"+id+"组件不存在!");
    }
}

function createUser() {
    var userId = document.getElementById("sendId").value;
    if (userId.trim().length>0) {
        $.ajax({
            url: "/createUser",
            async: true,
            type: "POST",
            data: {"userId":userId},
            success: function (data) {
                if (data=="1"){
                    document.getElementById('serverTxt').innerText = userId+"已创建";
                }
            },
            error: function () {
                alert("请求失败");
            },
            dataType: "text"
        });
    }else{
        alert("创建用户id不能为空!");
    }
}

java代码部分我是通过搭建SpringBoot项目的,不过不影响使用,先贴出代码部分,首先需要有转换session的工具类,然后还需要登陆(创建session),用户实体类只需要弄一个uuid即可。

配置类以及转换类:

//配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebConfig {
    /**
     * 支持websocket
     * 如果不使用内置tomcat,则无需配置
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}


//转换session
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

/**
 * 用于从websocket中获取用户session
 */
public class HttpSessionConfigurator extends ServerEndpointConfig.Configurator  {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
    }
}

websocket服务:

import com.alibaba.fastjson.JSONObject;
import com.example.demo.config.HttpSessionConfigurator;
import com.example.demo.po.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

//注入 HttpSessionConfigurator.class配置httpSession类
@ServerEndpoint(value = "/webSocket/{sid}",configurator =  HttpSessionConfigurator.class)
@Component
public class WebSocketServer {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger onlineNum = new AtomicInteger();

    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();

    //发送消息
    public void sendMessage(Session session, String message) throws IOException {
        if(session != null){
            synchronized (session) {
//                System.out.println("发送数据:" + message);
                session.getBasicRemote().sendText(message);
            }
        }
    }

    //给指定用户发送信息
    public void sendInfo(String userId, String message){
        Session session = sessionPools.get(userId);
        try {
            if (sessionPools.get(userId) != null){
                sendMessage(session, message);
            }else{
                System.out.println("用户"+userId+"不在线");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //建立连接成功调用
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "sid") String userId, EndpointConfig config){
        try {
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            User user = (User) httpSession.getAttribute("userSession");
            //判断是否还需加入连接
            if (user!=null && !sessionPools.containsKey(user.getUuid())){
                sessionPools.put(String.valueOf(user.getUuid()), session);
                addOnlineCount();
                System.out.println(userId + "加入webSocket!当前人数为" + onlineNum);
                sendMessage(session, "欢迎" + user.getUuid() + "加入连接!");
            }else{
                System.out.println("没有连接加入,当前人数为"+onlineNum);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //关闭连接时调用
    @OnClose
    public void onClose(@PathParam(value = "sid") String userId){
        //判断是否有连接
        if (sessionPools.containsKey(userId)){
            sessionPools.remove(userId);
            subOnlineCount();
            System.out.println(userId + "断开webSocket连接!当前人数为" + onlineNum);
        }else{
            System.out.println("用户"+userId+"不存在连接,无需断开连接");
        }
    }

    //收到客户端信息
    @OnMessage
    public void onMessage(String message){
        try {
            //转换成为JSONObject对象
            JSONObject jsonObj = JSONObject.parseObject(message);

            if (jsonObj.containsKey("toUserId") && !"".equals(jsonObj.get("toUserId"))){
                //指定用户
                sendInfo((String) jsonObj.get("toUserId"),message);
            }else{
                //群发
                for (Session session: sessionPools.values()) {
                    try {
                        sendMessage(session, message);
                    } catch(Exception e){
                        e.printStackTrace();
                        continue;
                    }
                }
            }
        } catch(Exception e){
            e.printStackTrace();
        }
    }

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

    //增加在线人数
    public static void addOnlineCount(){
        onlineNum.incrementAndGet();
    }

    //减少在线人数
    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }
}

最后效果如下:
在这里插入图片描述
需要注意的是:
1.需要先创建连接(即创建session)
2.创建连接后要连接服务(加入至聊天室)
3.不指定接收方则是群聊

代码在资源里O(∩_∩)O