基于websocket 使用Spring Boot 完成简易的聊天室功能。
POM
//其他的就略去了 就是平常的springboot依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
配置
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter ServerEndpointExporter() {
return new ServerEndpointExporter();
}
}
后端
@Component
@ServerEndpoint(value = "/websocket/{info}")
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) {
try {
if(session != null){
synchronized (session) {
System.out.println("服务端发送数据:" + message);
session.getBasicRemote().sendText(message);
}
}
} catch(Exception e) {
System.out.println("发送消息异常:"+e.getMessage());
e.printStackTrace();
}
}
//给指定用户发送信息
public void sendInfo(String userName, String message){
Session session = sessionPools.get(userName);
try {
sendMessage(session, message);
}catch (Exception e){
e.printStackTrace();
}
}
@OnOpen
public void onOpen(@PathParam("info") String info, Session session) {
Map<String,String> obj = decodeInfo(info);
if(obj != null) {
sessionPools.put(obj.get("name"), session);
addOnlineCount();
System.out.println(obj.get("name") + "加入webSocket!当前人数为" + onlineNum);
Gson gson = new Gson();
String message = gson.toJson(new MapBean("cmd","openConnect","name",obj.get("name"),"onlineNum",onlineNum));
//向所有客户端发送消息
for( String userName : sessionPools.keySet()) {
Session sess = sessionPools.get(userName);
sendMessage(sess, message);
}
}
}
@OnMessage
public void onMessage(String message) {
String str = "客户端发送:" + message + ",服务端已收到";
// Gson gson = new Gson();
// Map result = gson.fromJson(message, HashMap.class);
//向所有登录的人发送消息
for (Session session: sessionPools.values()) {
sendMessage(session, message);
}
}
@OnClose
public void onClose(@PathParam("info") String info) {
Map<String,String> obj = decodeInfo(info);
if(obj != null) {
sessionPools.remove(obj.get("name"));
subOnlineCount();
System.out.println(obj.get("name") + "断开webSocket连接!当前人数为" + onlineNum);
Gson gson = new Gson();
String message = gson.toJson(new MapBean("cmd","closeConnect","name",obj.get("name"),"onlineNum",onlineNum));
//向所有客户端发送消息
for (Session session: sessionPools.values()) {
sendMessage(session, message);
}
}
}
@OnError
public void onError(Throwable error) {
error.printStackTrace();
}
public static void addOnlineCount(){
onlineNum.incrementAndGet();
}
public static void subOnlineCount() {
onlineNum.decrementAndGet();
}
private Map decodeInfo(String info) {
Map infoObj = null;
try {
String base64Decryption = EncryptionUtils.base64Decryption(info);
String decode = URLDecoder.decode(base64Decryption, "UTF-8");
Gson gson = new Gson();
infoObj = gson.fromJson(decode, HashMap.class);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return infoObj;
}
}
前端
<style>
.bodyDiv{
width: 100%;
height: 100%;
}
.contentShow {
height: 500px;
width: 500px;
overflow: scroll
}
.connectDiv {
width: 100%;
text-align: center;
}
.msgDiv {
padding: 5px;
}
.msgSpan {
border: 1px Solid #000;
padding: 2px;
border-radius: 5px;
width: 400px;
}
</style>
<script>
var ws;
var name;
/**
* 创建连接对象
*/
function startConnect(){
if(typeof(WebSocket) != "undefined") {
name = document.getElementById("nameText").value;
document.getElementById("nameText").readOnly = true;
document.getElementById("startBtn").style.display = "none";
document.getElementById("endBtn").style.display = "block";
if(ws){
ws.close();
}
var info = {
name: name
};
var encode = window.btoa(encodeURIComponent(JSON.stringify(info)));
ws = new WebSocket('ws://localhost:9090/websocket/' + encode);
//打开事件
ws.onopen = function() {
console.log("websocket已打开");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
ws.onmessage = function(msg) {
var result = JSON.parse(msg.data);
if(result.cmd == 'openConnect') {
//连接建立
var div = document.createElement('div');
div.className = "connectDiv";
div.innerHTML = result.name + " 进入链接";
document.getElementById("contentDiv").appendChild(div);
} else if(result.cmd == 'closeConnect') {
var div = document.createElement('div');
div.className = "connectDiv";
div.innerHTML = result.name + " 关闭链接";
document.getElementById("contentDiv").appendChild(div);
} else if(result.cmd == 'sendMsg') {
var div1 = document.createElement('div');
div1.setAttribute("style", "width: 100%;height: 40px;");
var div = document.createElement('div');
div.className = "msgDiv";
var span1 = document.createElement('span');
var span2 = document.createElement('span');
if(name === result.name) {
//自己发的
div.setAttribute("style", "float: right;");
span1.className="msgSpan";
span1.innerHTML = result.content;
span2.innerHTML = result.name;
} else {
div.setAttribute("style", "float: left;");
span1.innerHTML = result.name;
span2.className="msgSpan";
span2.innerHTML = result.content;
}
div.appendChild(span1);
div.appendChild(span2);
div1.appendChild(div);
document.getElementById("contentDiv").appendChild(div1);
}
};
//关闭事件
ws.onclose = function() {
console.log("websocket已关闭");
};
//发生了错误事件
ws.onerror = function() {
console.log("websocket发生了错误");
}
}
}
/**
* 连接关闭
*/
function endConnect(){
if(typeof(WebSocket) != "undefined") {
if(ws){
ws.close();
document.getElementById("nameText").readOnly = false;
document.getElementById("startBtn").style.display = "block";
document.getElementById("endBtn").style.display = "none";
name = document.getElementById('nameText').value;
}
}
}
/**
* 发送消息
*/
function sendMessage(){
if(typeof(WebSocket) != "undefined") {
// console.log("您的浏览器支持WebSocket");
if(name && ws){
var contentText = document.getElementById('contentText').value;
var msg = '{"cmd":"sendMsg","name":"'+name+'","content":"'+contentText+'"}';
ws.send(msg);
document.getElementById('contentText').value = "";
}
}
}
</script>
<body>
<div class="bodyDiv">
<table align="center" border="1px" cellpadding="10" cellspacing="0">
<tr>
<td colspan="2">姓名:
<input type="text" id="nameText" name="name"/>
</td>
<td>
<input type="button" id="startBtn" onclick="startConnect()" value="确定"/>
<input type="button" id="endBtn" style="display: none;" onclick="endConnect()" value="注销"/>
</td>
</tr>
<tr>
<td colspan="3">
<div id="contentDiv" class="contentShow">
<!-- <div class="connectDiv"><span>张三 进入链接</span></div>
<div class="msgDiv"><span>张三 <span><span class="msgSpan">你好</span></div>
<div class="msgDiv"><span>李四 <span><span class="msgSpan">你好</span></div>
<div class="msgDiv" style="float: right;"><span class="msgSpan">你好</span><span> 李四<span></span></div> -->
</div>
</td>
</tr>
<tr>
<td colspan="2">内容:
<input type="text" id="contentText" name="content"/>
</td>
<td>
<input type="button" onclick="sendMessage()" value="发送"/>
</td>
</tr>
</table>
</div>
</body>