springboot框架下利用websocket实现即时通讯

springboot框架下利用websocket实现即时通讯(文章末尾有git项目打包文件,直接下载使用即可)

springboot框架下利用websocket实现即时通讯_第1张图片用websocket实现简单的在线聊天,先画个时序图,直观感受下流程

SystemController类

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * @描述 WebSocket消息推送控制器
 * @创建人 poneco
 * @创建时间 2021/11/16
 */
@RestController
public class SystemController {

    // 推送数据到websocket客户端 接口
    @GetMapping("/socket/push/{cid}")
    public Map pushMessage(@PathVariable("cid") String cid, String message) {
        Map<String, Object> result = new HashMap<>();
        try {
            HashSet<String> sids = new HashSet<>();
            sids.add(cid);
            WebSocketServer.sendMessage("服务端推送消息:" + message, sids);
            result.put("code", cid);
            result.put("msg", message);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
}

WebSocket类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @描述 开启WebSocket支持的配置类
 * @创建人 poneco
 * @创建时间 2021/11/16
 */
@Configuration
public class WebSocket {

	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}
}

WebSocketServer

import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @描述 WebSocket核心配置类
 * @创建人 poneco
 * @创建时间 2021/11/16
 */

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端。
 */
@Component
@Slf4j
@Service
@ServerEndpoint("/api/websocket/{userID}/{touserID}")
public class WebSocketServer {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger onlineCount = new AtomicInteger(0);
    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
   // private static ConcurrentHashMap webSocketSet = new ConcurrentHashMap<>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    //接收sid
    private String userID = "";
    private String touserID="";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userID") String userID,@PathParam("touserID") String touserID) {
        log.info(String.valueOf(session.getRequestURI()));
        this.session = session;
        this.userID = userID;
        this.touserID = touserID;
        log.info("接受者"+touserID);
        webSocketSet.add(this);     // 加入set中
        addOnlineCount();           // 在线数加1
        try {
            sendMessage("conn_success");
            log.info("有新客户端开始监听,sid=" + userID + ",当前在线人数为:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  // 从set中删除
        subOnlineCount();              // 在线数减1
        // 断开连接情况下,更新主板占用情况为释放
        log.info("释放的sid=" + userID + "的客户端");
        releaseResource();
    }

    private void releaseResource() {
        // 这里写释放资源和要处理的业务
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @Param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自客户端 sid=" + userID + " 的信息:" + message);
        // 群发消息
        HashSet<String> sids = new HashSet<>();
        if(this.touserID.equals("1")){
            for (WebSocketServer item : webSocketSet) {
                sids.add(item.userID);
            }
        }else {
            log.info("推送到:"+this.touserID);
            sids.add(this.touserID);
        }
        try {
            sendMessage(message, sids);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发生错误回调
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error(session.getBasicRemote() + "客户端发生错误");
        error.printStackTrace();
    }

    /**
     * 群发自定义消息
     */
    public static void sendMessage(String message, HashSet<String> toSids) throws IOException {
        log.info("推送消息到客户端 " + toSids + ",推送内容:" + message);
        JSONObject jsonObject = JSONObject.parseObject(message);
        //webSocketSet.get();
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给传入的sid,为null则全部推送
                if (toSids.size() <= 0) {
                    item.sendMessage(jsonObject.getString("msg"));
                } else if (toSids.contains(item.userID)) {
                    item.sendMessage(jsonObject.getString("msg"));
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    /**
     * 实现服务器主动推送消息到 指定客户端
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 获取当前在线人数
     *
     * @return
     */
    public static int getOnlineCount() {
        return onlineCount.get();
    }

    /**
     * 当前在线人数 +1
     *
     * @return
     */
    public static void addOnlineCount() {
        onlineCount.getAndIncrement();
    }

    /**
     * 当前在线人数 -1
     *
     * @return
     */
    public static void subOnlineCount() {
        onlineCount.getAndDecrement();
    }

    /**
     * 获取当前在线客户端对应的WebSocket对象
     *
     * @return
     */
    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }
}

pom.xml依赖添加

<!--websocket依赖包添加-->
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-websocket</artifactId>
     <version>5.3.12</version>
</dependency>
<!--日志依赖添加-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>

index.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<script src="JS/index.js" type="text/javascript"></script>
<link rel="stylesheet" href="CSS/style.css" type="text/css">
<style>
    body{margin: 0px;margin: 0px;background-image: url("image/bk-login.png")}
</style>
<body>
<div align="center">
    你的账户号<input type="text" id="userID" value="528409336">
    接受者账户号<input type="text" id="touserID" value="2995460432">
    <input type="button" onclick="con()" value="连接对方">
    <hr>
    <table class="table">
        <tr>
            <td>
                <input type="text" value="78269" style="display: none" id="accentID">
                <div style="height: 560px">
                    <div id="accentinformation"></div>
                    <div id="div">
                    </div>
                </div>
            </td>
        </tr>
    </table>
    <table>
        <tr>
            <td><textarea id="text"></textarea></td>
            <td><input type="button" style="width: 100px;height: 40px;background: #31acfb;border: none" onclick="send()" value="发送"></td>
        </tr>
    </table>
</div>
</body>
</html>
<script type="text/javascript">
    var websocket = null;
    function con(){
        //判断当前websocket是否处于连接状态
        try {
            if(websocket.readyState==1){
                websocket.close();
            }
        }catch (e){
        }
        var userID=document.getElementById("userID").value;
        var touserID =document.getElementById("touserID").value;
        //判断当前浏览器是否支持WebSocket
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://localhost:8066/api/websocket/" + userID+"/"+touserID);// 改成你的地址
        } else {
            alert('当前浏览器 Not support websocket')
        }
        //连接发生错误的回调方法
        websocket.onerror = function () {
        };
        //连接成功建立的回调方法
        websocket.onopen = function () {
            document.getElementById("accentinformation").innerHTML ="";
        }
        //接收到消息的回调方法
        websocket.onmessage = function (event) {
            var str = event.data;
            jieshou(str);
        }

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

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

    //关闭WebSocket连接
    function closeWebSocket() {
        websocket.close();
        alert('websocket.close: 关闭websocket连接')
    }
    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        try {
            websocket.send('{"msg":"' + message + '"}');
            fasong(message);
        } catch (err) {
            console.error("websocket.send: " + message + " 失败");
        }
    }
</script>

CSS代码:style.css(注意css代码文件放在index.html同级下的CSS【大写】文件夹中)

#div{width: 600px;height: 500px;}
#div div{width:100%;display: inline-block;padding: 0px;}
#div img{width: 40px;height: 40px}
#div li{list-style: none}
.right{float: right;}
.left{float: left}
.left,.right{padding: 10px;}
.left li,.right li{display: inline-table}
.div ul{padding: 0px;margin: 0px}
#text{width: 260px;height: 54px}
#frinddiv,#text,#div{overflow:auto;resize:none}
#frinddiv::-webkit-scrollbar,#text::-webkit-scrollbar,#div::-webkit-scrollbar{width: 1px;}
#frinddiv::-webkit-scrollbar-thumb,#text::-webkit-scrollbar-thumb,#div::-webkit-scrollbar-thumb{background: #31acfb}
.cueright,.cueleft{border-radius: 10px;}
.cueright p,.cueleft p{margin:5px}
.cueleft{background:#31acfb}
.cueright{background: #cccccc}
.table{padding: 0px;margin: 0px;border-collapse: collapse; border: aliceblue 1px solid}
#frinddiv{width: 300px;height: 560px;}
#frinddiv ul:hover{background: #cccccc}
#frinddiv li{list-style: none;padding: 0px;margin: 0px}
.friendul li{display: inline-table;}
.friendul img{width: 40px;height: 40px;float: left}
.xuanze li{display: inline-table;margin: 0px}
.xuanze span{display: inline-block;margin: 0px}
.spanfl{border-radius:10px;text-align:center;float:right;margin-top: 5px;margin-right: 5px;background: #f11d38;font-size: 4px;width: 15px;height: 15px;}

javascript文件 index.js(注意index.js文件放在index.html同级下的JS【大写】文件夹中)

function fasong(data){
    var str = document.getElementById("text");
    var div = document.getElementById("div");
    if(str.value!=""){
        var str0="";
        var count=0;
        var str1 = Array.from(data);
        for(var i=0;i<str1.length;i++){
            if(i>25){
                str0 +="
"
i=0; } if(count==str1.length){ break; } str0+=str1[count]; count++; } div.innerHTML += "
"+ "
    "+ "
  • "+str0+"

  • "
    + "
  • "
    + "
"
+ "
"
; } div.scrollTop = div.scrollHeight; document.getElementById("text").value ="" } function jieshou(data){ var div = document.getElementById("div"); var str0=""; var count=0; var str1 = Array.from(data); for(var i=0;i<str1.length;i++){ if(i>25){ str0 +="
"
i=0; } if(count==str1.length){ break; } str0+=str1[count]; count++; } div.innerHTML += "
\n" + "
    \n" + "
  • \n"
    + "
  • "+str0+"

  • \n"
    + "
\n"
+ "
"
; document.getElementById("text").value ="" div.scrollTop = div.scrollHeight; }


项目最终运行效果图,该项目中有三张图片没有上传,如有需要自行上网搜索使用自己喜欢的替换即可
欢迎大家github源码项目下载地址更多学习资料加QQ(528409336)

你可能感兴趣的:(java,springboot,websocket,java,spring,boot,github)