[后端] webSocket demo跑通
This commit is contained in:
parent
a85d7bcfba
commit
432ba2b812
10
README.md
10
README.md
@ -413,10 +413,10 @@ cnpm install
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run package
|
npm run package
|
||||||
npm run make
|
# npm run make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`npm run make` 等于先执行 `npm run package`(即 *Import project into Forge*),再 *Create distributable*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -468,6 +468,12 @@ npm run serve
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Step3. 启动项目门禁端
|
||||||
|
|
||||||
|
编译并运行client-entrance-guard目录下electron项目即可
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 停止项目
|
## 停止项目
|
||||||
|
|
||||||
### Step1. 停止各个微服务
|
### Step1. 停止各个微服务
|
||||||
|
@ -103,6 +103,12 @@
|
|||||||
<groupId>com.squareup.okhttp3</groupId>
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
<artifactId>okhttp</artifactId>
|
<artifactId>okhttp</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- WebSocket -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.cxyxiaomo.epp.access.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启WebSocket支持
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class WebSocketConfig implements ServletContextInitializer {
|
||||||
|
/**
|
||||||
|
* 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket,如果你使用外置的tomcat就不需要该配置文件
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter serverEndpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
package com.cxyxiaomo.epp.access.controller;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.websocket.*;
|
||||||
|
import javax.websocket.server.PathParam;
|
||||||
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
@ServerEndpoint("/websocket/{userId}")
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class WebSocketServer {
|
||||||
|
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
|
||||||
|
private Session session;
|
||||||
|
|
||||||
|
// session集合,存放对应的session
|
||||||
|
private static ConcurrentHashMap<Integer, Session> sessionPool = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
|
||||||
|
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立WebSocket连接
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session, @PathParam(value = "userId") Integer userId) {
|
||||||
|
log.info("WebSocket建立连接中,连接用户ID:{}", userId);
|
||||||
|
try {
|
||||||
|
Session historySession = sessionPool.get(userId);
|
||||||
|
// historySession不为空,说明已经有人登陆账号,应该删除登陆的WebSocket对象
|
||||||
|
if (historySession != null) {
|
||||||
|
webSocketSet.remove(historySession);
|
||||||
|
historySession.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("重复登录异常,错误信息:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
// 建立连接
|
||||||
|
this.session = session;
|
||||||
|
webSocketSet.add(this);
|
||||||
|
sessionPool.put(userId, session);
|
||||||
|
log.info("建立连接完成,当前在线人数为:{}", webSocketSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发生错误
|
||||||
|
*
|
||||||
|
* @param throwable e
|
||||||
|
*/
|
||||||
|
@OnError
|
||||||
|
public void onError(Throwable throwable) {
|
||||||
|
throwable.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭
|
||||||
|
*/
|
||||||
|
@OnClose
|
||||||
|
public void onClose() {
|
||||||
|
webSocketSet.remove(this);
|
||||||
|
log.info("连接断开,当前在线人数为:{}", webSocketSet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收客户端消息
|
||||||
|
*
|
||||||
|
* @param message 接收的消息
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message) {
|
||||||
|
log.info("收到客户端发来的消息:{}", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送消息到指定用户
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param message 发送的消息
|
||||||
|
*/
|
||||||
|
public static void sendMessageByUser(Integer userId, String message) {
|
||||||
|
log.info("用户ID:" + userId + ",推送内容:" + message);
|
||||||
|
Session session = sessionPool.get(userId);
|
||||||
|
try {
|
||||||
|
session.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("推送消息到指定用户发生错误:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群发消息
|
||||||
|
*
|
||||||
|
* @param message 发送的消息
|
||||||
|
*/
|
||||||
|
public static void sendAllMessage(String message) {
|
||||||
|
log.info("发送消息:{}", message);
|
||||||
|
for (WebSocketServer webSocket : webSocketSet) {
|
||||||
|
try {
|
||||||
|
webSocket.session.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("群发消息发生错误:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.cxyxiaomo.epp.access.pojo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Accessors(chain = true) // 链式写法
|
||||||
|
public class WebSocketData {
|
||||||
|
// 小程序: "miniprogram" 门禁: "guard"
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
// 需要执行的动作
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
// 传输的数据(JSON字符串)
|
||||||
|
private String json;
|
||||||
|
}
|
@ -127,6 +127,13 @@
|
|||||||
<version>4.10.0</version>
|
<version>4.10.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- WebSocket -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
<version>2.7.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Fastjson -->
|
<!-- Fastjson -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.fastjson2</groupId>
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
@ -118,6 +118,104 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "[微服务] 获取不限制的小程序码",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8002/access/wechat/getUnlimitedQRCode?scene=t=1&page=pages/index/index",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8002",
|
||||||
|
"path": [
|
||||||
|
"access",
|
||||||
|
"wechat",
|
||||||
|
"getUnlimitedQRCode"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "envVersion",
|
||||||
|
"value": "develop",
|
||||||
|
"disabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "scene",
|
||||||
|
"value": "t=1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "page",
|
||||||
|
"value": "pages/index/index"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "[线上] 获取不限制的小程序码",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"raw": "https://epp.only4.work/access/wechat/getUnlimitedQRCode?scene=t=1&page=pages/index/index",
|
||||||
|
"protocol": "https",
|
||||||
|
"host": [
|
||||||
|
"epp",
|
||||||
|
"only4",
|
||||||
|
"work"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"access",
|
||||||
|
"wechat",
|
||||||
|
"getUnlimitedQRCode"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
{
|
||||||
|
"key": "envVersion",
|
||||||
|
"value": "develop",
|
||||||
|
"disabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "scene",
|
||||||
|
"value": "t=1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "page",
|
||||||
|
"value": "pages/index/index"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TestProvider",
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "http://localhost:8011/hi/1",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"raw": "http://localhost:8011/hi/1",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "8011",
|
||||||
|
"path": [
|
||||||
|
"hi",
|
||||||
|
"1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
1
test-code/.gitignore
vendored
Normal file
1
test-code/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
44
test-code/package-lock.json
generated
Normal file
44
test-code/package-lock.json
generated
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "test-code",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "test-code",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^8.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
test-code/package.json
Normal file
14
test-code/package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "test-code",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "websocketTest.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^8.11.0"
|
||||||
|
}
|
||||||
|
}
|
45
test-code/websocketTest.js
Normal file
45
test-code/websocketTest.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
|
||||||
|
const webSocketUrl = 'ws://localhost:8002/websocket/1';
|
||||||
|
|
||||||
|
global.ws = null; // WebSocket 实例对象
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动完毕,输出配置信息
|
||||||
|
*/
|
||||||
|
console.log("Start running ...", "process.env", process.env);
|
||||||
|
|
||||||
|
function createWebSocket() {
|
||||||
|
//申请一个WebSocket对象,参数是服务端地址,同http协议使用http://开头一样,WebSocket协议的url使用ws://开头,另外安全的WebSocket协议使用wss://开头
|
||||||
|
global.ws = new WebSocket(webSocketUrl);
|
||||||
|
|
||||||
|
// 当WebSocket创建成功时,触发onopen事件
|
||||||
|
global.ws.onopen = function () {
|
||||||
|
console.log("webhook is open.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当客户端收到服务端发送的关闭连接请求时,触发onclose事件
|
||||||
|
global.ws.onclose = function (e) {
|
||||||
|
console.log("webhook is close.");
|
||||||
|
console.log("未知错误被关闭,等待 1s 尝试重新建立连接...");
|
||||||
|
setTimeout(function () {
|
||||||
|
createWebSocket();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
|
||||||
|
global.ws.onerror = function (e) {
|
||||||
|
console.log(e.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据
|
||||||
|
global.ws.onmessage = function (e) {
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建连接
|
||||||
|
createWebSocket();
|
Loading…
Reference in New Issue
Block a user