使用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:需要工程完整代码的朋友,私聊就可以得到哦!

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