我带着小程序和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/ 之前咱们把 ...
随机推荐
- java到底是引用传递还是值传递?
今天我们来讲讲一个在学习中容易误解的问题,面试中也偶尔问到,java方法调用时到底是值传递还是引用传递? 首先,请大家来做一个判断题,下面的3个问题是否描述正确 1. java基本数据类型传递是值传递 ...
- ArcGIS API for JavaScript 4.x 本地部署之IIS法
[导读] 关于如何在默认网站(Default Web Site,物理地址C:\inetpub\wwwroot\)启动,已有很多博客详尽地写好了. 本篇在自建网站(本机)中配置http而非https的j ...
- windows系统以及linux系统的优缺点以及区别
一.Linux以及Windows系统的优缺点对比 Windows Linux 优点 Windows Server系统相对于其他服务器系统而言,极其易用,极大降低使用者的学习成本. Linux系统是 ...
- 关于Django报错django.core.exceptions.ImproperlyConfigured: Requested setting DEBUG, but settings are not configure
报错代码:django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but se ...
- Mysql中concat()、concat_ws()和 group_concat()的用法
一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串.使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为+----+ ...
- PHP面向对象特性
目录 创建对象 成员属性 成员方法 构造方法 析构方法 垃圾回收机制 访问修饰符 魔术方法 对象比较 继承 重载 属性重载 方法重写 属性重写 静态属性 静态方法 多态 类型约束 抽象类 接口 fin ...
- chrome谷歌浏览器开发者工具-网络带宽控制
有时候我们想在本地测试一下图片预加载loading的加载情况,如下图: 可无奈一般网络带宽都比较好,基本上看不到效果,图片一下子就加载出来了, 可能这个时候有些小伙伴想到的办法是用定时器延迟加载 其实 ...
- 《白帽子讲Web安全》- 学习笔记
一.为何要了解Web安全 最近加入新公司后,公司的官网突然被Google标记为了不安全的诈骗网站,一时间我们信息技术部门成为了众矢之的,虽然老官网并不是我们开发的(因为开发老官网的前辈们全都跑路了). ...
- BeetleX之WebSocket详解
对于BeetleX来说编写WebSocket服务是一件非常简单的事情,当你实现一个Web Api应用的同时这些API方法也是WebSocket服务方法.接下来主要讲解如何通过JavaScript调用B ...
- ubuntu16.04无法获取ip地址的解决方案
当我们无法获取ip地址时可以使用dhcp来动态获取ip地址,安装dhcpcd5和dhcpcd-gtk sudo apt-get install dhcpcd5 sudo apt-get install ...
- 微信小程序开发(后端Java)