基于STOMP协议 实现的简单在线聊天 技术点:springboot+websocket+sockjs
1、websocket 简单了解
WebSocket协议RFC 6455提供了一种标准化方法,可以通过单个TCP连接在客户端和服务器之间建立全双工双向通信通道。它是与HTTP不同的TCP协议,但旨在通过端口80和443在HTTP上工作,并允许重复使用现有的防火墙规则。
WebSocket交互始于一个HTTP请求,该请求使用HTTP Upgrade
标头进行升级,或者在这种情况下切换到WebSocket协议。以下示例显示了这种交互:
GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket (1)
Connection: Upgrade (2)
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080
(1)该Upgrade
头
(2)使用Upgrade
连接
具有WebSocket支持的服务器代替通常的200状态代码,返回类似于以下内容的输出:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp
以上均摘自spring官网,具体的就不介绍了,我把链接放这里,需要了解的自己点开看好了
具体链接:点我去spring官网查看具体的websocket
2、具体实现
主要功能: 1、联系人列表上要有所有上线的人员,并且实现实时更新
2、实现一对一的在线实时聊天
先上效果图:
2个浏览器 模拟 唐僧 和悟空 登录
唐僧找悟空聊天
唐僧发了: 空空,我们去花果山ktv吧
悟空回复:不去,你上次还没有结账呢
一个简易的实时在线聊天,这是个简单的demo,所以布局和聊天的逻辑还不是很完善。
这个效果实现需要的了解几个知识:
1、前端:ui框架是用的layui 可以自行百度了解一下
2 个html互相传值的问题,网上大概有3中方法,这里用的是window.open和window.opener之间传值。
2、后端:springboot+websocket
先上后端的代码:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo.chat</groupId>
<artifactId>chat-chat</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
</project>
WebSocketConfig类
用来配置WebSocketConfig相关信息
@Configuration
///注解开启使用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 配置消息代理
* @param config
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
//允许订阅的前缀 点对点应配置一个/user消息代理,广播式应配置一个/topic消息代理
config.enableSimpleBroker("/topic","/user");
//路由到controller
config.setApplicationDestinationPrefixes("/app");
}
/**
* //注册STOMP协议的节点(chat),并映射指定的url
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册一个STOMP的endpoint,并指定使用SockJS协议
registry.addEndpoint("/chat").withSockJS();
}
}
ChatController类
用来处理更新在线人数 点对点实时消息发送
@Controller
public class ChatController {
@Autowired
SimpMessagingTemplate template;
/**
* 登录的controller
* @param username
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/login")
public void login(String username, HttpServletRequest request, HttpServletResponse response) throws IOException {
//维护一个在线人员的列表
Contains.list.add(username);
//把当前登录人存到session
request.getSession().setAttribute("uname",username);
response.sendRedirect("main.html");
}
/**
* 获取当前登录的用户信息
* @param request
* @param response
* @return
* @throws IOException
*/
@RequestMapping("/userInfo")
@ResponseBody
public String login(HttpServletRequest request, HttpServletResponse response) throws IOException {
String info = request.getSession().getAttribute("uname").toString();
return "{\"info\":\""+info+"\"}";
}
/**
* server 更新在线人数列表的方法
* 他会给所有订阅了/topic/userList的ws客户端发送消息
* @return
*/
@RequestMapping("/userList")
@ResponseBody
public String userList(){
String allUser="";
for (String s : Contains.list) {
allUser+=s+",";
}
allUser = allUser.substring(0,allUser.length()-1);
template.convertAndSend("/topic/userList",allUser);
return "success";
}
/**
* 客户端发送消息方法
* @param message
* @param username
* @return
*/
@RequestMapping("/chatInfo")
@ResponseBody
public String chat(String message,String username){
System.out.println("-------------------"+message+"---"+username);
template.convertAndSendToUser(username,"chat",message);
return "success";
}
}
Application类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
前端界面就不展示,毕竟是简单的实现,把主要的js代码贴上来。
chat.html的 js代码:
<script>
var sendToname ="";//发送给谁
var info = "";//自己是誰
$(function() {
sendToname = parent.$("#sendUsername").val();
info = parent.$("#loginName").val();
$("#header").text(sendToname); //设置聊天信息的人
$("#sendButton").click(function () {
var chatMsg = $("#sendVal").val();
$.ajax({
url: "/chatInfo",// 发送聊天信息给对应的人
type: "post",
data:'message='+chatMsg+'&username='+sendToname,
dataType: "json",
success: function (data) {
}
});
});
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
//订阅聊天的消息,发给我
stompClient.subscribe('/user/'+info+"/chat", function (data) {
console.log("我收到信息了----------"+data.body);
initChatData(data.body);
});
});
})
function initChatData (messgae) {
//动态添加聊天信息
$("#content").append(" <li ><div class=\"msg\">"+messgae+"</div></li>");
};
</script>
contacts.html的 js代码:
<script>
//JavaScript代码区域
layui.use('element', function(){
var element = layui.element;
// element.render("nav");
});
var info="";
$(function(){
$.ajax({
url:"/userInfo",//发送在线人数给所有客户端
type:"post",
dataType:"json",
success:function(data){
console.log("当前登录的用户:"+data);
info=data.info;
$("#loginName").val(data.info); //存下当前登录的名字
}
});
//连接ws
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
//订阅-------获取在线人数
stompClient.subscribe('/topic/userList', function (data) {
initOnLine(data.body)
});
/* //訂閲 聊天的消息,發給我
stompClient.subscribe('/user/'+info+"/chat", function (data) {
console.log("当前"+info+"我收到的消息:"+data);
});*/
$.ajax({
url:"/userList",//发送在线人数给所有客户端
type:"post",
dataType:"json",
success:function(data){
}
})
});
})
function chat(obj){
//把sendToUserName存到一个input
$("#sendUsername").val(obj);
layui.use('layer', function() {
layer.open({
area: ['700px', '900px'],
type: 2,
content: 'chat.html',
maxWidth: 600,
});
});
}
function initOnLine(data){
//user1,user2,user3,user4
data = data.split(",");
//[user1,user2.....]
$("#onLine").html("");
$.each(data,function(i,v){
if (info==v){
//如果当前登录的是自己,则标注是(自己)
$("#onLine").append(" <li class=\"layui-nav-item\"><a href=\"javascript:chat(\'"+v+"\')\">"+v+' (自己)'+"</a></li>");
}else {
$("#onLine").append(" <li class=\"layui-nav-item\"><a href=\"javascript:chat(\'"+v+"\')\">"+v+"</a></li>");
}
})
}
</script>
代码比较简单,上面都有注释,有问题谢谢指正。