使用SpringBoot2.x+WebSocket實現聊天功能

1. 最終效果與相關說明

1.1. 最終效果

1.2. 聊天過程演示

2. 工程的創建

筆者使用的開發工具是:STS4

  • 創建SpringBoot2.x工程

  • 添加相關starter

    跟本次演示功能直接相關的依賴:

    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-websocket</artifactId>
    		</dependency>
    

  • JavaConfig配置

    /**
     * <pre>
     * - WebSocket的核心配置
     * ClassName : com.teach.config.WebSocketConfig
     * Author : J.L.Zhou
     * E-Mail : [email protected]
     * Date : 2020-8-19 9:10:26
     * Version : 1.0
     * Copyright 2020 jlzhou.top Inc. All rights reserved. 
     * Warning: this content is only for internal circulation of the company. It is forbidden to divulge it or use it for other commercial purposes
     * </pre>
     */
    @Configuration
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    
  • WebSocket的服務器端

    注意: @ServerEndpoint是原型模式

    /**
     * <pre>
     * - 聊天
     * ClassName : com.teach.endpoint.ChatEndpoint
     * Author : J.L.Zhou
     * E-Mail : [email protected]
     * Date : 2020-8-19 9:15:13
     * Version : 1.0
     * Copyright 2020 jlzhou.top Inc. All rights reserved. 
     * Warning: this content is only for internal circulation of the company. It is forbidden to divulge it or use it for other commercial purposes
     * </pre>
     *
     */
    @ServerEndpoint("/chat/{id}")
    @Component
    public class ChatEndpoint {
    
    
    	/**
    	 * - 建立連接時調用的方法
    	 * @param session - WebSocket的連接會話
    	 * @param id - 連接路徑參數,這裏用做聊天室頻道
    	 */
    	@OnOpen
    	public void onOpen(Session session,@PathParam("id")String id) {
    
    	}
    
    	/**
    	 * - 發送消息時調用的方法
    	 * @param message - 發送的消息
    	 * @param session - 發送消息的會話
    	 */
    	@OnMessage
    	public void onMessage(String message,Session session) {
    
    	}
    
    	/**
    	 * - 發送錯誤時調用的方法
    	 * @param session - 發送錯誤的會話
    	 * @param throwable - 產生的異常
    	 */
    	@OnError
    	public void onError(Session session,Throwable throwable) {
    
    	}
    
    	/**
    	 * - 關閉時調用的方法
    	 * @param session
    	 */
    	@OnClose
    	public void onClose(Session session) {
    
    	}
    }
    

3. 服務器端的功能實現

@ServerEndpoint("/chat/{id}/{name}")
@Component
@Slf4j
public class ChatEndpoint {
	
	/**
	 * - 全局的聊天頻道和終端列表對應關係
	 */
	private final static Map<String, List<ChatEndpoint>> chats = new ConcurrentHashMap<>();
	
	/**
	 * - 當前終端對應的聊天室頻道
	 */
	private String id;
	
	/**
	 * - 當前終端的用戶名稱
	 */
	private String name;
	
	/**
	 * - 當前終端對應的會話
	 */
	private Session session;

	/**
	 * - 建立連接時調用的方法
	 * @param session - WebSocket的連接會話
	 * @param id - 連接路徑參數,這裏用做聊天室頻道
	 * @param name - 連接路徑參數,這裏用做聊天的名稱
	 */
	@OnOpen
	public void onOpen(Session session,@PathParam("id")String id,@PathParam("name")String name) {
		this.id = id;
		this.session = session;
		this.name = name;
		List<ChatEndpoint> list = chats.get(id);
		if(list==null) {
			list = new CopyOnWriteArrayList<>();
			list.add(this);
			chats.put(id, list);
			log.debug("創建了一個新頻道[{}],並加入了終端[{}]",id,session.getId());
		}else {
			list.add(this);
			log.debug("在頻道[{}]加入了終端[{}]",id,session.getId());
		}
	}
	
	/**
	 * - 發送消息時調用的方法
	 * - 接收到消息後,向所在頻道所有終端發送消息(不含自己)
	 * @param message - 發送的消息
	 * @param session - 發送消息的會話
	 */
	@OnMessage
	public void onMessage(String message,Session session) {
		final String msg = message.replace("\\", "\\\\").replace("\"", "\\\"");
		chats.get(this.id).forEach((endpoint)->{
			try {
				if(ChatEndpoint.this!=endpoint) {
					//發送的消息格式爲:msg("name","msg");
					endpoint.session.getBasicRemote().sendText("msg(\""+name+"\",\""+msg+"\")");
					log.debug("向終端{}發送了消息:{}",endpoint.session.getId(),msg);
				}
			} catch (IOException e) {
				log.warn("向終端{}發送消息失敗",endpoint.session.getId());
			}
		});
	}
	
	/**
	 * - 發送錯誤時調用的方法
	 * @param session - 發送錯誤的會話
	 * @param throwable - 產生的異常
	 */
	@OnError
	public void onError(Session session,Throwable throwable) {
		log.warn("終端"+session.getId()+"發送了異常",throwable);
		this.onClose(session);//發送錯誤後暫時直接關閉
	}
	
	/**
	 * - 關閉時調用的方法
	 * @param session
	 */
	@OnClose
	public void onClose(Session session) {
		chats.get(id).remove(this);
		log.debug("將終端{}從所在頻道{}中刪除",session.getId(),id);
		if(chats.get(id).size()==0) {//如果頻道中沒有了終端
			chats.remove(id);
			log.debug("將頻道{}刪除",id);
		}
		try {
			session.close();//嘗試關閉終端
		}catch (Exception e) {
		}
	}
}

4. 網頁端的實現

網頁代碼:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="css/chat.css">
    <script src="js/chat.js"></script>
</head>
<body>
    <form autocomplete="off" class="setting"><input required name="id" placeholder="請輸入頻道id"><input required packageName = require('packageName'); name="name" placeholder="請輸入姓名"><button>進入</button></form>
    <div class="chat-send"><textarea></textarea><button>發送</button></div>
</body>
</html>

核心JavaScript代碼:

window.onload = ()=> {
    let setting = document.body.firstElementChild;
    let msgEle = setting.nextElementSibling.firstElementChild;
    let btnEle = msgEle.nextElementSibling;
    console.debug(setting);
    console.debug(msgEle);
    console.debug(btnEle);

    let id=1,name="張三";
    setting.onsubmit = function(){
        let temp = new FormData(this);
        id = temp.get("id");
        name = temp.get("name");
        console.debug(`id=${id},name=${name}`);
        this.remove();
        temp=null;
        setting=null;
        initWebSocket();
        return false;
    }

    let ws = null;
 
    let initWebSocket = ()=>{
        ws = new WebSocket(`ws://${location.host}/chat/${id}/${name}`);
        ws.onopen = function(){
            console.debug("onopen");
            ws.send("我上線了");
        }
        ws.onerror = function(){
            initWebSocket();
        }
        ws.onclose = function(){
            initWebSocket();
        }
        ws.onmessage = function(msg){
            console.debug(msg);
            try{
                eval("window."+msg.data);
            }catch(e){
                console.warn(e);
            }
        }
    }


    let domParser = new DOMParser();

    window.msg = function(user,msg,me=false){
        let d = new Date();
        let html = `<div class="chat ${me?"":"chat-other"}">
        <a href="javascript"><img onerror="this.src='imgs/48.jpg'" align="聊天頭像" src="imgs/48.jpg"></a>
        <div>${user}</div>
        <pre>${msg}
<time>${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}</time></pre>
    </div>`;
        html = domParser.parseFromString(html,"text/html");
        document.body.appendChild(html.body.firstElementChild);
        window.scrollTo(0,99999);
    }

    btnEle.onclick = function(){
        window.msg(name,msgEle.value,true);
        ws.send(msgEle.value);
        msgEle.value="";
    }
}

5. 總結與說明

本文的主要目的是用於學習,主要介紹在SpringBoot2下怎麼進行WebSocket編程,只完成了不同頻道的簡單的文字聊天,對於表情,圖片,拍照,文件,視頻等聊天內容本文未做描述.

PS:需要工程完整代碼的朋友,私聊就可以得到哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章