Springboot websocket学习Demo
使用的是springboot2.1.4版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
要使用websocket需要一个Bean,否则报错Error during WebSocket handshake:之类的问题
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
后端代码WebSocketServer.java
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @ServerEndpoint("/imserver/{userId}")
@Component
@Slf4j
public class WebSocketServer { /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userId=""; public WebSocketServer() {
log.info(this.toString());
} /**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
//加入set中
}else{
webSocketMap.put(userId,this);
//加入set中
addOnlineCount();
//在线数加1
} log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("用户:"+userId+",网络异常!!!!!!");
}
} /**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//从set中删除
subOnlineCount();
}
log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
} /**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户消息:"+userId+",报文:"+message);
//可以群发消息
//消息保存到数据库、redis
if(isNotBlank(message)){
try {
//解析发送的报文
String toUserId = this.userId + "";
log.info(this.userId + ", msg = " + message);
webSocketMap.get(toUserId).sendMessage(message);
}catch (Exception e){
e.printStackTrace();
}
}
} private static boolean isNotBlank(String str){
return str!=null && !str.equals("");
} /**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
} /**
* 发送自定义消息
* */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("用户"+userId+",不在线!");
}
} public static synchronized int getOnlineCount() {
return onlineCount;
} public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
} public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
前端代码index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body>
<script>
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/demo/imserver/11");
}
else {
alert('当前浏览器 Not support websocket')
} //连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
}; //连接成功建立的回调方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功");
} //接收到消息的回调方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
} //连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
} //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
} //将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
} //关闭WebSocket连接
function closeWebSocket() {
websocket.close();
} //发送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script> </html>
后面是我的总结,如果有什么地方不对的,请留言:

Springboot websocket学习Demo的更多相关文章
- WebSocket 学习(三)--用nodejs搭建服务器
前面已经学习了WebSocket API,包括事件.方法和属性.详情:WebSocket(二)--API WebSocket是基于事件驱动,支持全双工通信.下面通过三个简单例子体验一下. 简单开始 ...
- (转)WebSocket学习
石墨文档:https://shimo.im/docs/3UkyOPJvmj4f9EAP/ (二期)17.即时通讯技术websocket [课程17]java We...实现.xmind0.1MB [课 ...
- WebSocket 学习--用nodejs搭建服务器
最简单的socket服务端 var net = require("net"); server1 = net.createServer(function(client){ clien ...
- springboot+websocket+sockjs进行消息推送【基于STOMP协议】
springboot+websocket+sockjs进行消息推送[基于STOMP协议] WebSocket是在HTML5基础上单个TCP连接上进行全双工通讯的协议,只要浏览器和服务器进行一次握手,就 ...
- 基于SpringBoot+WebSocket搭建一个简单的多人聊天系统
前言 今天闲来无事,就来了解一下WebSocket协议.来简单了解一下吧. WebSocket是什么 首先了解一下WebSocket是什么?WebSocket是一种在单个TCP连接上进行全双工 ...
- MacOS下SpringBoot基础学习
学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"springboot"获取视频和教程资料! b站在线视 ...
- Springboot+Websocket+JWT实现的即时通讯模块
场景 目前做了一个接口:邀请用户成为某课程的管理员,于是我感觉有能在用户被邀请之后能有个立马通知他本人的机(类似微博.朋友圈被点赞后就有立马能收到通知一样),于是就闲来没事搞了一套. 涉及技术栈 ...
- WebSocket学习笔记IE,IOS,Android等设备的兼容性问
WebSocket学习笔记IE,IOS,Android等设备的兼容性问 一.背景 公司最近准备将一套产品放到Andriod和IOS上面去,为了统一应用的开发方式,决定用各平台APP嵌套一个HTML5浏 ...
- WebSocket学习笔记——无痛入门
WebSocket学习笔记——无痛入门 标签: websocket 2014-04-09 22:05 4987人阅读 评论(1) 收藏 举报 分类: 物联网学习笔记(37) 版权声明:本文为博主原 ...
随机推荐
- css 实现换肤几种方式
说起换肤功能,前端肯定不陌生,其实就是颜色值的更换,实现方式有很多,也各有优缺点 一.可供选择的换肤 对于只提供几种主题方案,让用户来选择的,一般就简单粗暴的写多套主题 一个全局class控制样式切换 ...
- scrapy学习之爬虫练习平台35
前言 上一篇文章中爬取了爬虫练习平台的所有 ssr 网站,都是比较简单的,没有反爬措施,这次来爬一下后面的 spa 系列. 环境准备 这里沿用了上篇文章的环境和设置,就不重新搭建环境了. 开始爬取 s ...
- python3 多线程批量验证POC模板
#coding:utf-8 import threading,Queue,sys,os class RedisUN(threading.Thread): def __init__(self,queue ...
- 为什么TCP连接时是三次握手,而不是两次或四次?
TCP连接时有一个重要的任务就是服务端和客户端双方互相确认收发功能是否正常.图中步骤1,当客户端发起连接,服务端接收到请求,对于服务端来说,它此时知道客户端的发送功能和自己的接收功能是正常的. 图中步 ...
- Django踩坑记录1
from django.db import models # Create your models here. class Event(models.Model): name = models.Cha ...
- 编程入门选什么语言好?C 语言还是Python ?为你解析
前面我分享过计算机行业已经成了学校选择排名第一,家长和学生都很看好计算机类专业.现在IT行业也越来越火爆,程序员越来越被人看好.面对相比同龄人高薪资的诱惑,人们很难不心动,即使秃头也值得! 那么问题来 ...
- dubbo起停之服务注解
开始之前建议先去了解spring的BeanDefinition可以参考下这里:https://www.jianshu.com/p/56e42e82e9a0 当用户使用注解@DubboComponent ...
- bootstrap 按钮颜色属性
bootstrap 按钮颜色属性有几种
- 跟我一起学Redis之Redis持久化必知必会
前言 Redis是出了名的速度快,那是因为在内存中进行数据存储和操作:如果仅仅是在内存中进行数据存储,那就会导致以下问题: 数据随进程退出而消失:当服务器断电或Redis Server进程退出时,内存 ...
- C++ 虚基类的定义、功能、规定
原文声明:http://blog.sina.com.cn/s/blog_93b45b0f01011pkz.html 虚继承和虚基类的定义是非常的简单的,同时也是非常容易判断一个继承是否是虚继承的,虽然 ...