Spring Boot——整合websocket构建在线聊天室(群聊 / 私聊)

    1.引入依赖

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

2.注入对象ServerEndpointExporter

/*
 *
 *编写一个WebSocketConfig配置类,注入对象ServerEndpointExporter,
 *      这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
 */

@Configuration
public class WebSocketConfig {

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

3.WebSocket 具体实现类

package com.example.springbootwebsocket.mysocket;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 *  
 * 虽然@Component默认是单例模式的,但是spring boot 还是会为每个websocket连接初始化一个bean,所以这里使用一个静态的set保存spring boot
 * 
 * 创建的bean--MyWebSocket.
 **/
@ServerEndpoint(value="/websocket/{nickname}")// websocket连接点映射.
@Component
public class MyWebSocket {
	
	//用来存储每个客户端对应的MyWebSocket对象.
	private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
	//用来记录sessionId和该session之间的绑定关系.
	private static Map<String,Session> map = new HashMap<String,Session>();
	
	private Session session;//当前会话的session.
	private String nickname;//昵称.
	
	/**
	 * 成功建立连接调用的方法.
	 */
	@OnOpen
	public void onOpen(Session session,@PathParam("nickname") String nickname){
		this.session = session;
		this.nickname = nickname;
		map.put(session.getId(), session);
		webSocketSet.add(this);//加入set中.
		this.session.getAsyncRemote().sendText(nickname+"上线了,(我的id号是"+session.getId()+")");
	}
	
	/**
	 * 连接关闭调用的方法.
	 */
	@OnClose
	public void onClose(Session session){
		webSocketSet.remove(this);//从set中移除.
		map.remove(session.getId());
	}
	
	/**
	 * 收到客户端消息后调用的方法.
	 */
	@OnMessage
	public void onMessage(String message,Session session,@PathParam("nickname") String nickname){
		
		//message 不是普通的string ,而是我们定义的SocketMsg json字符串.
		try {
			SocketMsg socketMsg = new ObjectMapper().readValue(message, SocketMsg.class);
			

			//单聊.
			if(socketMsg.getType() == 1){
				
				//单聊:需要找到发送者和接受者即可.
				socketMsg.setFromUser(session.getId());//发送者.
				//socketMsg.setToUser(toUser);//这个是由客户端进行设置.
				Session fromSession = map.get(socketMsg.getFromUser());
				Session toSession = map.get(socketMsg.getToUser());
				if(toSession != null){
					//发送消息.
					fromSession.getAsyncRemote().sendText(nickname+":"+socketMsg.getMsg());
					toSession.getAsyncRemote().sendText(nickname+":"+socketMsg.getMsg());
				}else{
					fromSession.getAsyncRemote().sendText("系统消息:对方不在线或者您输入的id号有误");
				}
			}else {
				//群发给每个客户端.
				broadcast(socketMsg,nickname);
			}
			
		} catch (JsonParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 发生错误时调用.
	 */
	public void onError(Session session,Throwable error){
		System.out.println("发生错误");
		error.printStackTrace();
	}
	
	/**
	 * 群发的方法.
	 * @param 
	 */
	private void broadcast(SocketMsg socketMsg ,String nickname){
		for(MyWebSocket item:webSocketSet){
			//发送消息.
			item.session.getAsyncRemote().sendText(nickname+":"+socketMsg.getMsg());
		}
	}
	
}

4.创建消息对象SocketMsg

package com.example.springbootwebsocket.mysocket;

public class SocketMsg {
	private int type;//聊天类型,0:群聊;1:单聊;
	private String fromUser;//发送者.
	private String toUser;//接受者. session.getId();
	private String msg;//消息.
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getFromUser() {
		return fromUser;
	}
	public void setFromUser(String fromUser) {
		this.fromUser = fromUser;
	}
	public String getToUser() {
		return toUser;
	}
	public void setToUser(String toUser) {
		this.toUser = toUser;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
}

5.static目录下创建客户端 webSocketTest.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>聊天室</title>
    <style>

        #message{
            margin-top:20px;
            border:1px solid gray;
            padding:20px;
        }
    </style>

</head>
<body>

昵称:<input type="text" id="nickname" />
<button onclick="conectWebSocket()">连接服务器</button>
<button onclick="closeWebSocket()">断开连接</button>

<br />
消息:<input id="text" type="text"  />
id号<input id="toUser" type="text" />
<button onclick="send()">发送消息</button>

<!-- 存放接收到的消息. -->
<div id="message">

</div>


<script type="text/javascript">

    //连接对象.
    var websocket = null;
    var nickname = null;
    function conectWebSocket(){
        nickname = document.getElementById("nickname").value;
        if(nickname == ''){
            alert("请输入昵称");
            return;
        }
        //判断当前的浏览器是否支持websocket.
        if("WebSocket" in window){
            websocket = new WebSocket("ws://localhost:8080/websocket/"+nickname);
        }else{
            alert("Not support websocket");
            return false;
        }

        //连接成功的方法.
        websocket.onopen = function(event){
            setMessgeHtml("Loc MSG:连接成功");
        }

        //连接关闭.
        websocket.onclose = function(event){
            setMessgeHtml("Loc MSG:连接关闭");
        }

        //连接异常.
        websocket.onerror = function(event){
            setMessgeHtml("Loc MSG:连接异常");
        }


        websocket.onmessage = function(event){
            setMessgeHtml(event.data);
        }

    }


    function setMessgeHtml(msg){
        var message = document.getElementById("message");
        message.innerHTML += msg+"<br/>";
    }

    /**
     发送消息.
     */
    function send(){
        var message = document.getElementById("text").value;
        var toUser = document.getElementById("toUser").value;
        var socketMsg = {msg:message,toUser:toUser};
        if(toUser == ''){
            socketMsg.type =0;//群聊.
        }else{
            socketMsg.type =1;//单聊.
        }

        //websocket.send(nickname+":"+message);
        websocket.send(JSON.stringify(socketMsg));//将json对象转换为json字符串.
    }


</script>



</body>
</html>

测试,打开多个登录窗口,输入http://127.0.0.1:8080/webSocketTest.html ,输入昵称点击连接即可登录成功,群聊直接发送消息即可,私聊输入完消息后,选择对应那个人的id号码就行,快去试试吧:

总结:了解了websocket底层是怎么实现的,websocket协议稍微麻烦,其实我们完全可以用它的子协议STOMP来实现本文所有的功能,因已封装的很好,用起来非常简单,实例:SpringBoot+STOMP 实现聊天室(单聊+多聊)及群发消息详解

参考:https://www.iteye.com/blog/412887952-qq-com-2405514

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李多肉同学

长得好看的人一般都喜欢发红包

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

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

打赏作者

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

抵扣说明:

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

余额充值