我带着小程序和Springboot后端终于战胜了WebSocket!!!胜利( •̀ ω •́ )y
WebSocket项目笔记
1. What is WebSocket?
(以下内容来源于百度百科)
- WebSocket是一种在单个TCP连接上进行全双工通信的协议
- WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
- 在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
- 背景:
- 推送技术的演进发展:轮询 ---> Comet ---> WebSocket
握手协议:
2. Let's try!
因为项目要求,我的小程序需要与后端服务器进行长连接,以传送数据。所以我需要在我配置好的Springboot框架中添加WebSocket。十分庆幸的是,Springboot已经集成好了WebSocket了。所以过程并不复杂。看了很多博客,内容都大同小异。
我有点懵,因为我发现大家都在说的是,客户端与服务器建立连接的过程的实现。所以有几个问题:
- 既然是服务器主动发送消息,那么服务器到底 “到底什么时候发送消息呢?怎么发送?”
- 如何传参数,传的参数如何接收与使用。
- 我只需要针对某个客户端发消息,在同时有多个socket连接的时候我怎么标识该客户端?
大概就有这些。下面我们在配置过程中将问题逐个击破!
- 开发环境:Springboot 1.5.19 Java1.8
- 配置pom文件
<!-- 引入 websocket 依赖类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
- 配置Springboot开启WebSocket支持
package com.cuc.happyseat.config.websocket; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter; /**
* 开启WebSocket支持
*/
@Configuration
public class WebSocketConfig { @Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
} }
- WebSocket服务类编写
这里对照下面的代码看:
- 第20行,@ServerEndpoint("/websocket/{userID}"),括号中的内容就是客户端请求Socket连接时的访问路径,userID是我要求客户端传来的参数,我这里算是为了标识该客户端吧。
- 第28行,在该类中添加属性 userID,并添加对应的getUserID()方法。
- 第46行,在onOpen()方法即建立连接的时候就接收参数userID,需要标识@PathParam("userID") 。接收参数后直接赋值给属性userID。
- 第140-157行,是针对特定客户端发送消息。服务器和客户端在建立连接成功后就生成了一个WebSocket对象,并存在集合中,对象里特有的属性是我们设置的userID。所以通过唯一的userID就能标识服务器与该客户端建立的那个连接啦!这样要求发送消息时,传入userID与消息,服务器在自己的WebSocket连接集合中遍历找到对应客户端的连接,就可以直接发消息过去啦~~
package com.cuc.happyseat.websocket; import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.EncodeException;
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; /*@ServerEndpoint注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket/{userID}")
@Component
public class WebSocketServer { //每个客户端都会有相应的session,服务端可以发送相关消息
private Session session; //接收userID
private Integer userID; //J.U.C包下线程安全的类,主要用来存放每个客户端对应的webSocket连接
private static CopyOnWriteArraySet<WebSocketServer> copyOnWriteArraySet = new CopyOnWriteArraySet<WebSocketServer>(); public Integer getUserID() {
return userID;
} /**
* @Name:onOpen
* @Description:打开连接。进入页面后会自动发请求到此进行连接
* @Author:mYunYu
* @Create Date:14:46 2018/11/15
* @Parameters:@PathParam("userID") Integer userID
* @Return:
*/
@OnOpen
public void onOpen(Session session, @PathParam("userID") Integer userID) {
this.session = session;
this.userID = userID;
System.out.println(this.session.getId());
//System.out.println("userID:" + userID);
copyOnWriteArraySet.add(this);
System.out.println("websocket有新的连接, 总数:"+ copyOnWriteArraySet.size()); } /**
* @Name:onClose
* @Description:用户关闭页面,即关闭连接
* @Author:mYunYu
* @Create Date:14:46 2018/11/15
* @Parameters:
* @Return:
*/
@OnClose
public void onClose() {
copyOnWriteArraySet.remove(this);
System.out.println("websocket连接断开, 总数:"+ copyOnWriteArraySet.size());
} /**
* @Name:onMessage
* @Description:测试客户端发送消息,测试是否联通
* @Author:mYunYu
* @Create Date:14:46 2018/11/15
* @Parameters:
* @Return:
*/
@OnMessage
public void onMessage(String message) {
System.out.println("websocket收到客户端发来的消息:"+message);
} /**
* @Name:onError
* @Description:出现错误
* @Author:mYunYu
* @Create Date:14:46 2018/11/15
* @Parameters:
* @Return:
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误:" + error.getMessage() + "; sessionId:" + session.getId());
error.printStackTrace();
} public void sendMessage(Object object){
//遍历客户端
for (WebSocketServer webSocket : copyOnWriteArraySet) {
System.out.println("websocket广播消息:" + object.toString());
try {
//服务器主动推送
webSocket.session.getBasicRemote().sendObject(object) ;
} catch (Exception e) {
e.printStackTrace();
}
}
} /**
* @Name:sendMessage
* @Description:用于发送给客户端消息(群发)
* @Author:mYunYu
* @Create Date:14:46 2018/11/15
* @Parameters:
* @Return:
*/
public void sendMessage(String message) {
//遍历客户端
for (WebSocketServer webSocket : copyOnWriteArraySet) {
System.out.println("websocket广播消息:" + message);
try {
//服务器主动推送
webSocket.session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
} /**
* @throws Exception
* @Name:sendMessage
* @Description:用于发送给指定客户端消息
* @Author:mYunYu
* @Create Date:14:47 2018/11/15
* @Parameters:
* @Return:
*/
public void sendMessage(Integer userID, String message) throws Exception {
Session session = null;
WebSocketServer tempWebSocket = null;
for (WebSocketServer webSocket : copyOnWriteArraySet) {
if (webSocket.getUserID() == userID) {
tempWebSocket = webSocket;
session = webSocket.session;
break;
}
}
if (session != null) {
//服务器主动推送
tempWebSocket.session.getBasicRemote().sendText(message); } else {
System.out.println("没有找到你指定ID的会话:{}"+ "; userId:" + userID);
}
} }
- Controller类的编写。
- 我在看博客的时候,发现有的博主写了Controller类,有的没写,我就有点疑惑了。后来,咳咳,发现特地写了一个Controller类只是为了测试。。
- 一般在实际项目中,在确保建立连接过程没有问题的情况下,我们就直接在一些写好的接口中写 WebSocketServer.sendMessage(param, message)语句就行了。
- 也因此,你写的位置就决定了你什么时候给你的客户端发消息,这样也就实现了主动推送消息的功能咯~
- 前提是:在你的Controller类里,以@Resource的方式注入WebSocket,而不是@Autowired方式哦(⊙o⊙)。
@Resource
WebSocketServer webSocket;
@Autowired
UserService userService;
调用示例:
if(userID>0) {
boolean location = userService.getLocation(userID);
if(location==false) {//验证用户当前不在馆内
boolean i = userService.modifyLocation(userID, true);
if(i==true) {
modelMap.put("successEnter", true);
//发消息给客户端
webSocket.sendMessage(userID, "success");
}
}else {
modelMap.put("successEnter", false);
//发消息给客户端
webSocket.sendMessage(userID, "fail");
}
}else {
modelMap.put("successEnter", false);
//发消息给客户端
webSocket.sendMessage(userID, "fail");
}
- 前端测试
因为我只写后端,前端部分小姐姐说看微信的官方文档就可以啦~ 链接:在此!微信封装好了吧,好像不难。
3. Problems
- 前期我看很多博客,的确产生很多问题,想不通,主要就是上面几个问题。然后我写了让前端先连WebSocket试了一下,想自己了解一下连接过程(也就是在控制台输出的文件中查),后来就慢慢通了。可以说是一帆风顺了??
- 不过我确保我没问题不算,得前端说了算对吧。所以,,我背了锅
我带着小程序和Springboot后端终于战胜了WebSocket!!!胜利( •̀ ω •́ )y的更多相关文章
- 微信小程序开发(后端Java)
微信使用的开发语言和文件很「特殊」. 小程序所使用的程序文件类型大致分为以下几种: ①WXML(WeiXin Mark Language,微信标记语言) ②WXSS(WeiXin Style Shee ...
- 【好好编程-技术博客】微信小程序开发中前后端的交互
微信小程序开发中前后端的交互 微信小程序的开发有点类似与普通网页的开发,但是也不尽然相同.小程序的主要开发语言是JavaScript,开发同普通的网页开发有很大的相似性,对于前端开发者而言,从网页开发 ...
- 记录一次用宝塔部署微信小程序Node.js后端接口代码的详细过程
一直忙着写毕设,上一次写博客还是元旦,大半年过去了.... 后面会不断分享各种新项目的源码与技术.欢迎关注一起学习哈! 记录一次部署微信小程序Node.js后端接口代码的详细过程,使用宝塔来部署. 我 ...
- 【node+小程序+web端】简单的websocket通讯
[node+小程序+web端]简单的websocket通讯 websoket是用来做什么的? 聊天室 消息列表 拼多多 即时通讯,推送, 实时交互 websoket是什么 websocket是一个全新 ...
- 微信小程序项目总结-记账小程序(包括后端)
一.小程序部分 这是理财系统的前端,江苏海洋大学微信小程序比赛,最后获得了一等奖 GitHub:https://github.com/GeorgeLeoo/finance 1. 项目描述 (1). 此 ...
- 微信小程序支付+php后端
最近在做自有项目后端用的是thinkphp5.1框架,闲话不说直接上代码 小程序代码 wxpay: function(e){ let thisid = e.currentTarget.dataset. ...
- 关于微信小程序登录,后端如何生成3rd_session?(后端为c#)
各位大神,请教一个问题,现在是小程序端调用wx.login后,将code传入后端接口,后端发起微信服务器request获取openid和session_key,后端再自定义生成一个登录状态:3rd_s ...
- 微信小程序支付C#后端源码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- 「小程序JAVA实战」小程序的springboot后台拦截器(61)
转自:https://idig8.com/2018/09/24/xiaochengxujavashizhanxiaochengxudespringboothoutailanjieqi60/ 之前咱们把 ...
随机推荐
- 补习系列(16)-springboot mongodb 数据库应用技巧
目录 一.关于 MongoDB 二.Spring-Data-Mongo 三.整合 MongoDB CRUD A. 引入框架 B. 数据库配置 C. 数据模型 D. 数据操作 E. 自定义操作 四.高级 ...
- Properties类对于文件的读取和写入
Properties类表示一个持久的属性集.Properties可保存在流中或从流中加载.Properties对象只能加载以 .Properties 为后缀的文件(文件我创建在src下). 开始时文件 ...
- vue slot+传参
插槽分为默认插槽和具名插槽: 默认插槽: //父组件<div> <h3>父组件</h3> <testChild> <div>默认插槽< ...
- transition-timing-function 属性
以相同的速度从开始到结束的过渡效果: div { transition-timing-function: linear; -moz-transition-timing-function: linear ...
- MySQL数据库Inception工具学习与测试 笔记
MySQL语句的审核,在业界都已经基本被认同了,实际上也是对MySQL语句写法的统一化,标准化,而之前的人工审核,针对标准这个问题其实是很吃力的,标准越多,DBA越累,开发也越累. 那么在这个都追求自 ...
- BI基础知识
- OKR能解决996吗?德鲁克怎么看?
最近网络上热议的“996”,不由让人想起我们的邻国日本.他们在20年前就有过一个热词“过劳死”,就是职场加班太严重导致的猝死. 最近有一本书新书<过劳时代>,说的就是日本20年前的过劳死. ...
- June 30th. 2018, Week 26th. Saturday
Curiosity is the wick in the candle of learning. 如果学习是一根蜡烛,那好奇心就是烛芯. From William Arthur Ward. Pleas ...
- 二维前缀和模板题:P2004 领地选择
思路:就是使用二维前缀和的模板: 先放模板: #include<iostream> using namespace std; #define ll long long ; ll a[max ...
- Vue2.x源码学习笔记-Vue源码调试
如果我们不用单文件组件开发,一般直接<script src="dist/vue.js">引入开发版vue.js这种情况下debug也是很方便的,只不过vue.js文件代 ...
- 微信小程序开发(后端Java)