我带着小程序和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/ 之前咱们把 ...
随机推荐
- 这年头做开源项目,被冷嘲热讽,FreeSql 0.0.4
FreeSql 项目大概在20天前想着要做的,今天发布0.0.4在群里被一位大神讽刺. 这位无名氏哥们的观点,先声明这不是找安慰的文章,更加不是报复打击的目的. 1 所以这个比EF好在哪里 2 毕竟E ...
- PHP中反射的简单实用(动态代理)
<?php class mysql{ function connect($db){ echo "连接mysql数据库${db[0]} \r\n"; } } class ora ...
- openlayers4 入门开发系列之地图标绘篇(附源码下载)
前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...
- 多功能设备mfd驱动
一.概述 mfd是Multifunction device的简称,即多功能设备,是许多有共性的设备的集合,mfd由核心层(core)以及其下的"子设备"组成.从下文将会看到,mfd ...
- c/c++ 网络编程 陈硕老师视频理解之ttcp
ttcp 是干啥的:测试2台机器间的网络传输性能 wiki 功能如下图: 对应的视频是: 4.回顾基础的Sockets API.mkv 5.TTCP代码概览.mkv 6.使用TTCP进行网络传输性能测 ...
- Windows Server 2016-Hyper-V HNV 新增功能
本内容主要介绍了Hyper-V 网络虚拟化 (HNV) 功能在 Windows Server 2016 中的新增或更改内容,具体信息如下: HNV更新 功能中的功能 新的或改进 描述 可编程 Hype ...
- 使用 docker-compose 快速安装Jenkins
本文分享在 docker 环境中,使用 docker-compose.yml 快速安装 Jenkins,以及使用主机中的 docker 打包推送镜像到阿里云 博客园的第100篇文章达成,2019的第一 ...
- ubuntu-18.04 设置开机启动脚本-亲测有效
ubuntu-18.04不能像ubuntu14一样通过编辑rc.local来设置开机启动脚本,通过下列简单设置后,可以使rc.local重新发挥作用. 2.将下列内容复制进rc-local.servi ...
- Linux下Springboot解决`APR based Apache Tomcat Native library`提示
最近转行做java,开发基于Springboot的项目,版本号为2.1.0.RELEASE. 启动应用,发现以下提示: The APR based Apache Tomcat Native libra ...
- PHP内核之旅-5.强大的数组
PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6.垃 ...
- 微信小程序开发(后端Java)