Spring Websocket实现简易在线聊天功能
针对Spring Websocket的实现,我参照了其他博主的文章https://www.cnblogs.com/leechenxiang/p/5306372.html
下面直接给出实现:
一、引入相关依赖
在之前的文章中,我们说到要使用websocket,我们首先要在maven中引入相关的依赖包,具体如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency> <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.1.3.RELEASE</version>
</dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
这里没有逐个添加注释,因为之前有写。
二、分析聊天功能的实现方式
在拓展了AbstractWebSocketHandler的websocket处理器类中,我们实现了处理连接建立后行为的方法、处理服务器接收来自客户端信息的行为方法以及处理关闭连接行为的方法,
客户端建立连接后发送信息给服务端,服务端通过TextMessage或WebSocketMessage类获取信息,在通过调用与指定客户端开启的Session将信息发送给指定客户端。因此我们需要
一个标识使得我们的服务端能够在接收的客户端发送的信息后,正确地将收到的信息发送给指定的客户端,从而完成聊天业务。

三、实现
在对每一个Session进行独立标识处理时,我使用了所参照博主的方法,通过设置拦截器来处理每一个建立连接后开启的Session,代码如下:
package com.example.websocket; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor; import javax.servlet.http.HttpSession;
import java.util.Map; public class WsInterceptor implements HandshakeInterceptor {
private static final Logger log = LoggerFactory.getLogger(WsInterceptor.class); @Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler,
Map<String, Object> map) throws Exception {
if (serverHttpRequest instanceof ServletServerHttpRequest) {
// 取用户连接姓名作为参数传入
ServletServerHttpRequest request = (ServletServerHttpRequest) serverHttpRequest;
String userName = request.getServletRequest().getParameter("name");
log.info("拦截请求 获取用户信息: " + userName);
// 拿到对应的Session
HttpSession session = request.getServletRequest().getSession(true);
if (session != null) {
// 唯一表示Session
map.put("username", userName);
log.info("获取Session: " + session.getId());
}
}
return true;
} @Override
public void afterHandshake(ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler, Exception e) { }
}
在配置文件中加入对拦截器的配置:
package com.example.config; import com.example.websocket.MyHandler;
import com.example.websocket.WsInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(myHandler(), "/myHandler")
.addInterceptors(wsInterceptor()).withSockJS();
} @Bean
public MyHandler myHandler() {
return new MyHandler();
} @Bean
public WsInterceptor wsInterceptor() {
return new WsInterceptor();
}
}
由于本人浏览器不支持websocket,所以我使用SockJS代替websocket
现在我们就可以开始编写我们的信息收发业务了,代码如下:
package com.example.websocket; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; public class MyHandler extends AbstractWebSocketHandler {
private static final Logger log = LoggerFactory.getLogger(MyHandler.class);
private static final List<WebSocketSession> sessions = new ArrayList<>();
private static int count = 0; @Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
log.debug("session" + session.getId() + "已关闭");
String userName = (String) session.getAttributes().get("username");
sendMessageToOthers(new TextMessage(userName + "已经下线了"), userName);
count = count - 1;
sessions.remove(session);
} @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
if (session != null) {
log.debug("连接成功成功 Session " + session.getId() + "已打开");
count = count + 1;
sessions.add(session);
String userName = (String)session.getAttributes().get("username");
if (userName != null) {
log.debug("用户" + userName + "已连接");
String message = "SystemInfo: " + userName + "上线了, 当前在线人数: " + count;
session.sendMessage(new TextMessage(message));
}
}
} @Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
String userName = (String) session.getAttributes().get("username");
log.debug(userName + "发送信息: " + message.getPayload());
sendMessageToOthers(new TextMessage(message.getPayload()), userName);
} public void sendMessageToOthers(TextMessage message, String userName) throws IOException {
for (WebSocketSession session: sessions) {
String theUser = (String) session.getAttributes().get("username");
if ((!userName.equals(theUser)) && session.isOpen()) {
session.sendMessage(message);
}
}
}
}
websocket服务端的部分完成了,现在我们来构建视图层,使用之前的Controller类:
package com.example.controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView; @Controller
@RequestMapping("/")
public class wsController { @RequestMapping(method = RequestMethod.GET)
public ModelAndView websocket() {
return new ModelAndView("websocket");
}
}
我们来手写一个jsp文件作为我们的视图层显示:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: asus1
Date: 2019/1/26
Time: 13:01
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>websocket在线聊天</title>
<c:set value="${pageContext.request.contextPath}" var="path" scope="page"/>
<link type="text/css" rel="stylesheet" href="/statics/css/communication.css">
</head>
<body>
<div class="communicationInfo"> </div>
<div class="message">
<label>
<textarea class="messageArea"></textarea>
</label>
<br/>
<div class="btnArea">
username:
<label><input id="username" type="text"/></label>
<input type="button" class="btn" id="connect" value="连接"/>
<input type="button" class="btn" id="send" value="发送"/>
</div>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="/statics/js/sockjs-1.0.0.min.js"></script>
<script>
$(function () {
var sock;
// 创建新连接
function getConnect() {
var username = $("#username").val();
var url = "http://localhost:8080/myHandler?name=" + username;
sock = new SockJS(url);
} // 绑定连接按钮事件
$("#connect").bind("click", function () {
getConnect();
sock.onopen = function () {
alert("连接成功");
};
sock.onerror = function () {
};
sock.onmessage = function (ev) {
var element = document.createElement("div");
element.style.textAlign = "left";
element.style.paddingLeft = "20px";
// element.style.border = "green solid 3px";
element.style.overflow = "hidden";
element.style.paddingTop = "5px";
console.log(ev.data);
var receivedFromOthers = ""; var infoHead = ev.data.substr(0, 11);
if (infoHead === "SystemInfo:") {
element.style.textAlign = "center";
receivedFromOthers = ev.data;
} else {
receivedFromOthers = '<div class="imageL">' +
'<img src="/statics/images/QQ图片20171104161032.jpg" alt=""/>' +
'</div>' +
'<span class="triangle-borderL">' +
'</span>' +
'<div class="message-divL">' + ev.data +
'</div>';
} element.innerHTML += receivedFromOthers;
$(".communicationInfo").append(element);
};
sock.onclose = function () {
alert("与服务器断开连接");
};
}); // 绑定发送按钮事件
$("#send").bind("click", function () {
if (sock != null) {
var msg = $(".messageArea").val();
console.log(msg);
sock.send(msg);
var element = document.createElement("div");
element.style.textAlign = "right";
element.style.paddingRight = "20px";
// element.style.border = "gold solid 3px";
element.style.overflow = "hidden";
element.style.paddingTop = "5px"; var sendToOthers = '<div class="imageR">' +
'<img src="/statics/images/photo.jpg" alt=""/>' +
'</div>' +
'<div class="triangle-borderR">' +
'</div>' +
'<span class="message-divR">' + msg +
'</span>';
element.innerHTML += sendToOthers;
$(".communicationInfo").append(element);
} else {
alert("未与服务器连接")
}
clearContent();
}); function clearContent() {
$(".messageArea").val("");
}
});
</script>
</body>
</html>
jsp对应的css代码如下:
/*
聊天样式
*/
body {
text-align: center;
}
.communicationInfo {
border: black solid 3px;
width: 40%;
height: 250px;
margin-top: 10%;
margin-left: auto;
margin-right: auto;
padding-top: 30px;
padding-bottom: 30px;
overflow-y: auto; }
.message {
border: black solid 3px;
width: 40%;
height: 100px;
margin: 0 auto;
padding-top: 10px;
padding-bottom: 10px;
}
.messageArea {
width: 80%;
height: 70px;
overflow-y: auto;
resize: none;
}
.btnArea {
width: 60%;
margin-left: 40%;
}
.imageL {
width: 30px;
height: 30px;
border: solid black 5px;
float: left;
}
img {
width: 30px;
height: 30px;
}
.triangle-borderL {
display: block;
width: 0;
height: 0;
margin-left: 20px;
margin-top: 10px;
margin-right: -1px;
border-style: solid;
border-color: transparent lawngreen transparent;
border-width: 10px 10px 10px 0;
float: left;
}
.message-divL {
text-align: center;
background-color: lawngreen;
padding: 15px 20px;
max-width: 180px;
margin-top: -5px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
float: left;
}
.imageR {
width: 30px;
height: 30px;
border: solid black 5px;
float: right;
}
.message-divR {
background-color: lawngreen;
text-align: center;
padding: 15px 20px;
max-width: 180px;
margin-top: -5px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
float: right;
}
.triangle-borderR {
display: block;
width: 0;
height: 0;
margin-left: -1px;
margin-right: 20px;
margin-top: 10px;
border-style: solid;
border-color: transparent lawngreen transparent;
border-width: 10px 0 10px 10px;
float: right;
}
需要说明的是,由于视图层只为测试后端代码使用,因此有些样式可能对不同的浏览器存在不兼容问题,本人使用Chrone和360浏览器
视图中引用的静态资源存放在/statics/images目录下
OK,现在让我们启动项目看看运行后结果:



再通过360浏览器创建一个用户:

发送消息:




Spring Websocket实现简易在线聊天功能的更多相关文章
- 使用websocket实现在线聊天功能
很早以前为了快速达到效果,使用轮询实现了在线聊天功能,后来无意接触了socket,关于socket我的理解是进程间通信,首先要有服务器跟客户端,服务的启动监听某ip端口定位该进程,客户端开启socke ...
- 基于PHP实现一个简单的在线聊天功能(轮询ajax )
基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...
- 编写Java程序,应用客户端和服务端通过 Eclipse 控制台的输入和显示实现简易的聊天功能
查看本章节 查看作业目录 需求说明: 应用客户端和服务端通过 Eclipse 控制台的输入和显示实现简易的聊天功能 实现思路: 创建 Java 项目,在项目中创建服务端类 ChatServerThre ...
- WebSocket(3)---实现一对一聊天功能
实现一对一聊天功能 功能介绍:实现A和B单独聊天功能,即A发消息给B只能B接收,同样B向A发消息只能A接收. 本篇博客是在上一遍基础上搭建,上一篇博客地址:[WebSocket]---实现游戏公告功能 ...
- webSocket实现多人聊天功能
webSocket实现多人在线聊天 主要思路如下: 1.使用vue构建简单的聊天室界面 2.基于nodeJs 的webSocket开启一个socket后台服务,前端使用H5的webSocket来创建一 ...
- springboot+websocket实现简单的在线聊天功能
效果如下: java实现逻辑: 1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId&g ...
- 学习WebSocket(二):使用Spring WebSocket做一个简单聊天室
聊天室高频率.低延时完全符合websocket的特点,所以聊天室使用websocket再适合不过了. 聊天室的功能并没有比上一节代码多多少,主要在握手阶段对用户的session做处理,对用户的消息进行 ...
- websocket简单实现在线聊天
WebSocket简介与消息推送 B/S架构的系统多使用HTTP协议,HTTP协议的特点: 1 无状态协议2 用于通过 Internet 发送请求消息和响应消息3 使用端口接收和发送消息,默认为80端 ...
- javaweb学习路之三--websocket多人在线聊天
在之前的项目基础上,加入了一个聊天室的功能,为了界面好看 引入了AmazeUI和umeditor最终效果图如下: 源码在 https://github.com/Zering/MyWeb 目前练习都在这 ...
随机推荐
- C++中的string类型转换为int类型
给定一个十进制整数n,输出n的各位数字之和 #include<iostream> #include<string> using namespace std; int main( ...
- 一键git push脚本(python版)
#!/usr/bin/env python import os import subprocess import sys import time gitconfig = { 'cwd': './blo ...
- jenkins 配置
配置: echo " aliyun.oss.access.key=LTAIz4Koeff8sCr8 " > ./src/main/resources/oss.dev.prop ...
- Java包装类介绍与类型之间相互转换
1.包装类存在的意义 通俗解释就是由于Java是面对对象的语言,而基本类型不具有面对对象的概念,为了弥补不足,引入了包装类方便使用面对对象的变成思想操作基本类型. 2.基本类型和包装类对应关系 byt ...
- BMIP002协议介绍
比原BMIP002协议 概述 比原链技术社区最近提出了一套资产规范提议,该提议允许在issue类型的交易中实现标准资产token.该标准定义资产在链上的基本功能,以及发行人通过智能合约管理资产的规范. ...
- 最简单获取appPackage和appActivity 的方法
appPackage和appActivity 进行appium自动化测试必须的两个参数,我们所测试的APP项目不同,这两个参数肯定也是不一样的.我们启动的包都是靠这两个参数去驱动的,那么我们怎么可以快 ...
- Request类源码分析
通过APIView进入找到Request的源码 可以看见一堆属性和方法,其中request.data其实是一个方法,被包装成一个属性 继续看__getattr__和query_params方法: 代码 ...
- JQ清空select的已选择状态
$('#payment').find("option:selected").attr("selected", false);
- Ocelot概述
Ocelot是一个基于netcoreapp2.0构建,.NET Core框架下的开源Api网关项目,用于开发基于.NET微服务架构或面向服务架构系统的统一入口.
- linux下的启停脚本
linux下的根据项目名称,进行进程的启停脚本 #!/bin/bash JAVA=/usr/bin/java APP_HOME=/opt/program/qa/wechat APP_NAME=prog ...