From e668670da80478886afe6fca901b4be4ec8c1488 Mon Sep 17 00:00:00 2001 From: acgist <289547414@qq.com> Date: Fri, 11 Nov 2022 07:59:36 +0800 Subject: [PATCH] =?UTF-8?q?[*]=20=E7=BB=9F=E4=B8=80=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 2 +- README.md | 12 +- pom.xml | 25 +-- .../boot/config/BootAutoConfiguration.java | 8 + .../taoyao/boot/config/FormatStyle.java | 133 ------------ .../taoyao/boot/config/WebrtcProperties.java | 21 +- .../controller/TaoyaoControllerAdvice.java | 2 +- .../controller/TaoyaoErrorController.java | 2 +- .../boot/interceptor/SecurityInterceptor.java | 11 +- .../com/acgist/taoyao/boot/model}/Header.java | 22 +- .../com/acgist/taoyao/boot/model/Message.java | 176 ++++++++------- .../acgist/taoyao/boot/service/IdService.java | 6 +- .../boot/service/impl/IdServiceImpl.java | 43 +++- .../acgist/taoyao/boot/utils/DateUtils.java | 125 ++++++++++- .../acgist/taoyao/boot/utils/ErrorUtils.java | 39 +++- .../taoyao/boot/utils/ExceptionUtils.java | 43 ---- .../acgist/taoyao/boot/utils/JSONUtils.java | 6 +- taoyao-client/pom.xml | 30 --- taoyao-nat/README.md | 3 + taoyao-nat/pom.xml | 2 +- taoyao-server/LICENSE | 201 ++++++++++++++++++ taoyao-server/pom.xml | 4 - .../src/main/resources/application.yml | 9 +- taoyao-signal/README.md | 43 ++++ .../config/SignalAutoConfiguration.java | 17 +- .../com/acgist/taoyao/signal/event/Event.java | 15 -- .../taoyao/signal/event/EventAdapter.java | 10 - .../signal/event/client/RegisterEvent.java | 25 ++- .../listener/ApplicationListenerAdapter.java | 21 ++ .../listener/client/RegisterListener.java | 38 ++++ .../acgist/taoyao/signal/message/Message.java | 41 ---- .../taoyao/signal/protocol/Protocol.java | 29 +++ .../signal/protocol/ProtocolAdapter.java | 46 +++- .../protocol/ProtocolBodyMapAdapter.java | 44 ++++ .../protocol/ProtocolBodyObjectAdapter.java | 55 +++++ .../signal/protocol/ProtocolManager.java | 86 +++++++- .../signal/protocol/client/CloseProtocol.java | 33 +++ .../protocol/client/OfflineProtocol.java | 10 + .../protocol/client/OnlineProtocol.java | 32 +++ .../protocol/client/RegisterProtocol.java | 54 +++++ .../signal/protocol/system/ErrorProtocol.java | 32 +++ .../taoyao/signal/session/ClientSession.java | 66 ++++++ .../signal/session/ClientSessionAdapter.java | 88 ++++++++ .../signal/session/ClientSessionManager.java | 129 +++++++++++ .../signal/session/ClientSessionStatus.java | 12 ++ .../acgist/taoyao/signal/session/Session.java | 5 - .../taoyao/signal/session/SessionAdapter.java | 5 - .../taoyao/signal/session/SessionManager.java | 162 -------------- .../{TaoyaoSocket.java => SocketSignal.java} | 2 +- .../session/websocket/SessionWrapper.java | 72 ------- .../session/websocket/TaoyaoWebSocket.java | 60 ------ .../session/websocket/WebSocketSession.java | 37 ++++ .../session/websocket/WebSocketSignal.java | 94 ++++++++ taoyao-webrtc/pom.xml | 2 +- .../pom.xml | 12 +- taoyao-webrtc/taoyao-webrtc-mcu/pom.xml | 2 +- taoyao-webrtc/taoyao-webrtc-sfu/pom.xml | 2 +- 57 files changed, 1542 insertions(+), 764 deletions(-) delete mode 100644 taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/FormatStyle.java rename {taoyao-signal/src/main/java/com/acgist/taoyao/signal/message => taoyao-boot/src/main/java/com/acgist/taoyao/boot/model}/Header.java (73%) delete mode 100644 taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ExceptionUtils.java delete mode 100644 taoyao-client/pom.xml create mode 100644 taoyao-nat/README.md create mode 100644 taoyao-server/LICENSE create mode 100644 taoyao-signal/README.md delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/Event.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/EventAdapter.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/ApplicationListenerAdapter.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/RegisterListener.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Message.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyMapAdapter.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyObjectAdapter.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/CloseProtocol.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OfflineProtocol.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OnlineProtocol.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/RegisterProtocol.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/system/ErrorProtocol.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSession.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionAdapter.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionManager.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionStatus.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/Session.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionAdapter.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionManager.java rename taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/{TaoyaoSocket.java => SocketSignal.java} (77%) delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/SessionWrapper.java delete mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/TaoyaoWebSocket.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSession.java create mode 100644 taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSignal.java rename taoyao-webrtc/{taoyao-webrtc-mix => taoyao-webrtc-jni}/pom.xml (65%) diff --git a/LICENSE b/LICENSE index 261eeb9..6e6a6b0 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [2022] [acgist] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index b8a31ea..d12a075 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,17 @@ |模块|名称|描述| |:--|:--|:--| |taoyao|桃夭|桃之夭夭灼灼其华| -|taoyao-nat|内网穿透|STUN/TURN暂不实现(请用公共服务或者搭建coturn服务)| +|taoyao-nat|内网穿透|STUN/TURN| |taoyao-boot|基础|启动模块| |taoyao-live|直播|直播、连麦| |taoyao-media|媒体|录制、视频(美颜、AI识别)、音频(混音、变声)| -|taoyao-client|终端|帐号(移动端|浏览器)、摄像头| |taoyao-signal|信令|信令服务| |taoyao-server|服务|启动服务| |taoyao-meeting|会议|会议模式、广播模式、单人对讲| |taoyao-webrtc|WebRTC模块|| +|taoyao-webrtc-jni|WebRTC JNI|WebRTC本地接口| |taoyao-webrtc-sfu|WebRTC SFU架构实现|| |taoyao-webrtc-mcu|WebRTC MCU架构实现|| -|taoyao-webrtc-mix|WebRTC混合|MCU/SFU混合媒体服务| |taoyao-webrtc-mesh|WebRTC MESH架构实现|| ## STUN/TURN公共服务 @@ -33,7 +32,8 @@ stun:stun.stunprotocol.org:3478 ## 终端 -帐号(移动端|浏览器)可以管理媒体,摄像头只能被动管理。 +帐号(移动端|浏览器) +摄像头 ### 功能 @@ -55,6 +55,8 @@ stun:stun.stunprotocol.org:3478 恢复推流 掉线重连 +## 信令 + ### 信息 IP @@ -68,8 +70,6 @@ MAC ## 会议 -## - ## 证书 ``` diff --git a/pom.xml b/pom.xml index 79fb815..a44aa27 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,6 @@ taoyao-boot taoyao-live taoyao-media - taoyao-client taoyao-signal taoyao-webrtc taoyao-server @@ -123,11 +122,6 @@ taoyao-media ${project.version} - - com.acgist - taoyao-client - ${project.version} - com.acgist taoyao-signal @@ -148,6 +142,11 @@ taoyao-webrtc ${project.version} + + com.acgist + taoyao-webrtc-jni + ${project.version} + com.acgist taoyao-webrtc-sfu @@ -158,11 +157,6 @@ taoyao-webrtc-mcu ${project.version} - - com.acgist - taoyao-webrtc-mix - ${project.version} - com.acgist taoyao-webrtc-mesh @@ -226,15 +220,6 @@ false - - - ./ - false - META-INF/ - - LICENSE - - diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/BootAutoConfiguration.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/BootAutoConfiguration.java index 8535328..7cd6d34 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/BootAutoConfiguration.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/BootAutoConfiguration.java @@ -51,6 +51,8 @@ import com.acgist.taoyao.boot.controller.TaoyaoControllerAdvice; import com.acgist.taoyao.boot.controller.TaoyaoErrorController; import com.acgist.taoyao.boot.interceptor.SecurityInterceptor; import com.acgist.taoyao.boot.model.MessageCode; +import com.acgist.taoyao.boot.service.IdService; +import com.acgist.taoyao.boot.service.impl.IdServiceImpl; import com.acgist.taoyao.boot.utils.ErrorUtils; import com.acgist.taoyao.boot.utils.FileUtils; import com.acgist.taoyao.boot.utils.JSONUtils; @@ -80,6 +82,12 @@ public class BootAutoConfiguration { @Autowired private ApplicationContext context; + @Bean + @ConditionalOnMissingBean + public IdService idService() { + return new IdServiceImpl(); + } + @Bean @Primary @ConditionalOnMissingBean diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/FormatStyle.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/FormatStyle.java deleted file mode 100644 index 4b1187c..0000000 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/FormatStyle.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.acgist.taoyao.boot.config; - -import java.time.format.DateTimeFormatter; - -import lombok.Getter; - -/** - * 格式 - * - * @author acgist - */ -public interface FormatStyle { - - /** - * 默认日期格式 - */ - public static final String YYYY_MM_DD_HH24_MM_SS = "yyyy-MM-dd HH:mm:ss"; - - /** - * 日期时间 - * - * @author acgist - */ - @Getter - public static enum DateTimeStyle { - - // YYYY - YYYYMMDD_HH24_MM("yyyyMMdd HH:mm"), - YYYY_MM_DD_HH24_MM("yyyy-MM-dd HH:mm"), - YYYYMMDDHH24MMSS("yyyyMMddHHmmss"), - YYYYMMDDHH24MMSSSSS("yyyyMMddHHmmssSSS"), - YYYYMMDD_HH24_MM_SS("yyyyMMdd HH:mm:ss"), - YYYYMMDD_HH24_MM_SS_SSS("yyyyMMdd HH:mm:ss.SSS"), - YYYY_MM_DD_HH24_MM_SS("yyyy-MM-dd HH:mm:ss"), - YYYY_MM_DD_HH24_MM_SS_SSS("yyyy-MM-dd HH:mm:ss.SSS"), - // YY - YYMMDD_HH24_MM("yyMMdd HH:mm"), - YY_MM_DD_HH24_MM("yy-MM-dd HH:mm"), - YYMMDDHH24MMSS("yyMMddHHmmss"), - YYMMDDHH24MMSSSSS("yyMMddHHmmssSSS"), - YYMMDD_HH24_MM_SS("yyMMdd HH:mm:ss"), - YYMMDD_HH24_MM_SS_SSS("yyMMdd HH:mm:ss.SSS"), - YY_MM_DD_HH24_MM_SS("yy-MM-dd HH:mm:ss"), - YY_MM_DD_HH24_MM_SS_SSS("yy-MM-dd HH:mm:ss.SSS"), - // ISO - YY_MM_DD_HH24_MM_SS_ISO("yy-MM-dd'T'HH:mm:ss"), - YY_MM_DD_HH24_MM_SS_SSS_ISO("yy-MM-dd'T'HH:mm:ss.SSS"), - YYYY_MM_DD_HH24_MM_SS_ISO("yyyy-MM-dd'T'HH:mm:ss"), - YYYY_MM_DD_HH24_MM_SS_SSS_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS"), - // UTC - YY_MM_DD_HH24_MM_SS_UTC("yy-MM-dd'T'HH:mm:ss'Z'"), - YY_MM_DD_HH24_MM_SS_SSS_UTC("yy-MM-dd'T'HH:mm:ss.SSS'Z'"), - YYYY_MM_DD_HH24_MM_SS_UTC("yyyy-MM-dd'T'HH:mm:ss'Z'"), - YYYY_MM_DD_HH24_MM_SS_SSS_UTC("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - - /** - * 格式 - */ - private final String format; - /** - * 格式工具 - */ - private final DateTimeFormatter dateTimeFormatter; - - private DateTimeStyle(String format) { - this.format = format; - this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); - } - - } - - /** - * 时间 - * - * @author acgist - */ - @Getter - public static enum TimeStyle { - - HH24("HH"), - HH24MM("HHmm"), - HH24_MM("HH:mm"), - HH24MMSS("HHmmss"), - HH24_MM_SS("HH:mm:ss"), - HH24MMSSSSS("HHmmssSSS"), - HH24_MM_SS_SSS("HH:mm:ss.SSS"); - - /** - * 格式 - */ - private final String format; - /** - * 格式工具 - */ - private final DateTimeFormatter dateTimeFormatter; - - private TimeStyle(String format) { - this.format = format; - this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); - } - - } - - /** - * 日期 - * - * @author acgist - */ - @Getter - public static enum DateStyle { - - YYMMDD("yyMMdd"), - YYYYMMDD("yyyyMMdd"), - YY_MM_DD("yy-MM-dd"), - YYYY_MM_DD("yyyy-MM-dd"); - - /** - * 格式 - */ - private String format; - /** - * 格式工具 - */ - private final DateTimeFormatter dateTimeFormatter; - - private DateStyle(String format) { - this.format = format; - this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); - } - - } - -} diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/WebrtcProperties.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/WebrtcProperties.java index 898e258..25c3cd3 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/WebrtcProperties.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/config/WebrtcProperties.java @@ -18,14 +18,23 @@ import lombok.Setter; public class WebrtcProperties { /** - * 类型 + * 架构类型 * * @author acgist */ public enum Type { + /** + * SFU架构 + */ SFU, + /** + * MCU架构 + */ MCU, + /** + * MESH架构 + */ MESH; } @@ -33,7 +42,15 @@ public class WebrtcProperties { /** * 类型 */ - @Schema(name = "类型", description = "WebRTC媒体架构") + @Schema(name = "架构类型", description = "WebRTC架构类型") private Type type; + /** + * stun服务器 + */ + private String[] stun; + /** + * turn服务器 + */ + private String[] turn; } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoControllerAdvice.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoControllerAdvice.java index 5ad7448..29150fb 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoControllerAdvice.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoControllerAdvice.java @@ -18,7 +18,7 @@ import com.acgist.taoyao.boot.utils.ErrorUtils; public class TaoyaoControllerAdvice { @ExceptionHandler(Exception.class) - public Message exception(Exception e, HttpServletRequest request, HttpServletResponse response) { + public Message exception(Exception e, HttpServletRequest request, HttpServletResponse response) { return ErrorUtils.message(e, request, response); } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoErrorController.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoErrorController.java index d78981e..5b2a185 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoErrorController.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/controller/TaoyaoErrorController.java @@ -24,7 +24,7 @@ public class TaoyaoErrorController implements ErrorController { @Operation(summary = "统一错误地址", description = "全局统一错误地址") @RequestMapping(value = ErrorUtils.ERROR_PATH) - public Message index(HttpServletRequest request, HttpServletResponse response) { + public Message index(HttpServletRequest request, HttpServletResponse response) { return ErrorUtils.message(request, response); } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/interceptor/SecurityInterceptor.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/interceptor/SecurityInterceptor.java index bf12cae..e5987e3 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/interceptor/SecurityInterceptor.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/interceptor/SecurityInterceptor.java @@ -46,7 +46,7 @@ public class SecurityInterceptor implements HandlerInterceptor { /** * @param request 请求 * - * @return 是否公共请求 + * @return 是否许可请求 */ private boolean permit(HttpServletRequest request) { final String uri = request.getRequestURI(); @@ -54,8 +54,8 @@ public class SecurityInterceptor implements HandlerInterceptor { return false; } for (String permit : this.securityProperties.getPermit()) { - if(uri.startsWith(permit)) { - log.debug("授权成功(公共请求):{}-{}", uri, permit); + if(StringUtils.startsWith(uri, permit)) { + log.debug("授权成功(许可请求):{}-{}", uri, permit); return true; } } @@ -73,11 +73,10 @@ public class SecurityInterceptor implements HandlerInterceptor { if(StringUtils.isEmpty(authorization)) { return false; } - final int index = authorization.indexOf(' '); - if(index < 0 || !authorization.substring(0, index).equalsIgnoreCase(SecurityProperties.BASIC)) { + if(!StringUtils.startsWithIgnoreCase(authorization, SecurityProperties.BASIC)) { return false; } - authorization = authorization.substring(index).strip(); + authorization = authorization.substring(SecurityProperties.BASIC.length()).strip(); authorization = new String(Base64.getDecoder().decode(authorization)); if(!authorization.equals(this.securityProperties.getAuthorization())) { return false; diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Header.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java similarity index 73% rename from taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Header.java rename to taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java index 930b3c5..0ad4867 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Header.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Header.java @@ -1,4 +1,4 @@ -package com.acgist.taoyao.signal.message; +package com.acgist.taoyao.boot.model; import java.io.Serializable; @@ -23,16 +23,20 @@ public class Header implements Serializable { private static final long serialVersionUID = 1L; /** - * 信令来源 + * 信令版本 + */ + private String v; + /** + * 请求标识 + */ + private Long id; + /** + * 终端标识 */ private String sn; /** - * 事件标识 + * 协议标识 */ - private String event; - /** - * 信令版本 - */ - private String version; - + private Integer pid; + } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java index 9979df6..ceeefd4 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/model/Message.java @@ -7,61 +7,88 @@ import org.apache.commons.lang3.StringUtils; import com.acgist.taoyao.boot.utils.JSONUtils; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; /** - * 响应消息 + * 消息 * * @author acgist - * - * @param 消息类型 */ @Getter @Setter -@Schema(name = "响应消息", description = "HTTP响应消息") -public class Message implements Serializable { +@Schema(name = "消息", description = "请求响应消息") +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Message implements Serializable { private static final long serialVersionUID = 1L; /** * 响应编码 */ - @Schema(name = "响应编码", description = "0000表示成功其他都是失败") + @Schema(name = "响应编码", description = "响应消息标识响应状态") private String code; /** * 响应描述 */ - @Schema(name = "响应描述", description = "响应编码描述") + @Schema(name = "响应描述", description = "响应消息描述响应编码") private String message; /** - * 响应内容 + * 请求响应头部 */ - @Schema(name = "响应内容", description = "响应内容") - private T body; + @Schema(name = "请求响应头部", description = "请求响应头部") + private Header header; + /** + * 请求响应主体 + */ + @Schema(name = "请求响应主体", description = "请求响应主体") + private Object body; + + /** + * @param code 状态编码 + * + * @return this + */ + public Message setCode(MessageCode code) { + this.code = code.getCode(); + this.message = code.getMessage(); + return this; + } + + /** + * @param code 状态编码 + * @param message + * + * @return this + */ + public Message setCode(MessageCode code, String message) { + if(StringUtils.isEmpty(message)) { + message = code.getMessage(); + } + this.code = code.getCode(); + this.message = message; + return this; + } /** - * 成功消息 - * - * @param 消息类型 - * * @return 成功消息 */ - public static final Message success() { + public static final Message success() { return success(null); } /** - * 成功消息 - * - * @param 消息类型 - * - * @param body 消息内容 + * @param body 主体 * * @return 成功消息 */ - public static final Message success(T body) { - final Message message = new Message<>(); + public static final Message success(Object body) { + final Message message = new Message(); message.code = MessageCode.CODE_0000.getCode(); message.message = MessageCode.CODE_0000.getMessage(); message.body = body; @@ -69,94 +96,65 @@ public class Message implements Serializable { } /** - * 错误消息 - * - * @param 消息类型 - * * @return 错误消息 */ - public static final Message fail() { - return fail(MessageCode.CODE_9999); + public static final Message fail() { + return fail(null, null, null); } /** - * 错误消息 - * - * @param 消息类型 - * - * @param message 消息内容 + * @param message 主体 * * @return 错误消息 */ - public static final Message fail(String message) { - return fail(MessageCode.CODE_9999, message); + public static final Message fail(String message) { + return fail(null, message, null); } /** - * 错误消息 - * - * @param 消息类型 - * - * @param code 错误编码 + * @param code 响应编码 * * @return 错误消息 */ - public static final Message fail(MessageCode code) { - return fail(code, null); + public static final Message fail(MessageCode code) { + return fail(code, null, null); } /** - * 错误消息 - * - * @param 消息类型 - * - * @param code 错误编码 - * @param message 错误描述 + * @param code 响应编码 + * @param message 响应描述 * * @return 错误消息 */ - public static final Message fail(MessageCode code, String message) { - final Message failMessage = new Message<>(); - failMessage.code = code.getCode(); - if (StringUtils.isEmpty(message)) { - failMessage.message = code.getMessage(); - } else { - failMessage.message = message; + public static final Message fail(MessageCode code, String message) { + return fail(code, message, null); + } + + /** + * @param code 响应编码 + * @param body 主体 + * + * @return 错误消息 + */ + public static final Message fail(MessageCode code, Object body) { + return fail(code, null, body); + } + + /** + * @param code 响应编码 + * @param message 响应描述 + * @param body 主体 + * + * @return 错误消息 + */ + public static final Message fail(MessageCode code, String message, Object body) { + if(code == null) { + code = MessageCode.CODE_9999; } - return failMessage; - } - - /** - * 错误消息 - * - * @param 消息类型 - * - * @param code 错误编码 - * @param body 消息内容 - * - * @return 错误消息 - */ - public static final Message fail(MessageCode code, T body) { - final Message message = new Message<>(); - message.code = code.getCode(); - message.message = code.getMessage(); - message.body = body; - return message; - } - - /** - * 错误消息 - * - * @param 消息类型 - * - * @param code 错误编码 - * @param message 错误描述 - * @param body 消息内容 - * - * @return 错误消息 - */ - public static final Message fail(MessageCode code, String message, T body) { - final Message failMessage = new Message<>(); + if (StringUtils.isEmpty(message)) { + message = code.getMessage(); + } + final Message failMessage = new Message(); failMessage.code = code.getCode(); failMessage.message = message; failMessage.body = body; diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java index 4979457..f81a533 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/IdService.java @@ -1,17 +1,17 @@ package com.acgist.taoyao.boot.service; /** - * ID生成 + * ID * * @author acgist */ public interface IdService { /** - * 生成十八位的ID:YYMMddHHmmss + sn + xxxx + * 生成十九位的ID:YYMMddHHmmss(12) + sn(1) + xxxxxx(6) * * @return ID */ - Long id(); + long id(); } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java index 3a976f1..155e7ea 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/service/impl/IdServiceImpl.java @@ -1,5 +1,46 @@ package com.acgist.taoyao.boot.service.impl; -public class IdServiceImpl { +import java.time.LocalDateTime; +import org.springframework.beans.factory.annotation.Value; + +import com.acgist.taoyao.boot.service.IdService; + +public class IdServiceImpl implements IdService { + + /** + * 机器序号 + */ + @Value("${taoyao.sn:0}") + private int sn = 9; + /** + * 当前索引 + */ + private int index; + /** + * 最大索引 + */ + private static final int MAX_INDEX = 999999; + + @Override + public long id() { + synchronized (this) { + if (++this.index > MAX_INDEX) { + this.index = 0; + } + } + final LocalDateTime time = LocalDateTime.now(); + return + 100000000000000000L * (time.getYear() % 100) + + 1000000000000000L * time.getMonthValue() + + 10000000000000L * time.getDayOfMonth() + + 100000000000L * time.getHour() + + 1000000000L * time.getMinute() + + 10000000L * time.getSecond() + + // 机器序号一位 + 1000000L * this.sn + + // 每秒并发数量 + this.index; + } + } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/DateUtils.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/DateUtils.java index e610143..650e04c 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/DateUtils.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/DateUtils.java @@ -6,15 +6,13 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Objects; import org.apache.commons.lang3.StringUtils; -import com.acgist.taoyao.boot.config.FormatStyle.DateStyle; -import com.acgist.taoyao.boot.config.FormatStyle.DateTimeStyle; -import com.acgist.taoyao.boot.config.FormatStyle.TimeStyle; - +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -28,6 +26,125 @@ public final class DateUtils { private DateUtils() { } + /** + * 默认日期格式 + */ + public static final String YYYY_MM_DD_HH24_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + /** + * 日期时间 + * + * @author acgist + */ + @Getter + public static enum DateTimeStyle { + + // YYYY + YYYYMMDD_HH24_MM("yyyyMMdd HH:mm"), + YYYY_MM_DD_HH24_MM("yyyy-MM-dd HH:mm"), + YYYYMMDDHH24MMSS("yyyyMMddHHmmss"), + YYYYMMDDHH24MMSSSSS("yyyyMMddHHmmssSSS"), + YYYYMMDD_HH24_MM_SS("yyyyMMdd HH:mm:ss"), + YYYYMMDD_HH24_MM_SS_SSS("yyyyMMdd HH:mm:ss.SSS"), + YYYY_MM_DD_HH24_MM_SS("yyyy-MM-dd HH:mm:ss"), + YYYY_MM_DD_HH24_MM_SS_SSS("yyyy-MM-dd HH:mm:ss.SSS"), + // YY + YYMMDD_HH24_MM("yyMMdd HH:mm"), + YY_MM_DD_HH24_MM("yy-MM-dd HH:mm"), + YYMMDDHH24MMSS("yyMMddHHmmss"), + YYMMDDHH24MMSSSSS("yyMMddHHmmssSSS"), + YYMMDD_HH24_MM_SS("yyMMdd HH:mm:ss"), + YYMMDD_HH24_MM_SS_SSS("yyMMdd HH:mm:ss.SSS"), + YY_MM_DD_HH24_MM_SS("yy-MM-dd HH:mm:ss"), + YY_MM_DD_HH24_MM_SS_SSS("yy-MM-dd HH:mm:ss.SSS"), + // ISO + YY_MM_DD_HH24_MM_SS_ISO("yy-MM-dd'T'HH:mm:ss"), + YY_MM_DD_HH24_MM_SS_SSS_ISO("yy-MM-dd'T'HH:mm:ss.SSS"), + YYYY_MM_DD_HH24_MM_SS_ISO("yyyy-MM-dd'T'HH:mm:ss"), + YYYY_MM_DD_HH24_MM_SS_SSS_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS"), + // UTC + YY_MM_DD_HH24_MM_SS_UTC("yy-MM-dd'T'HH:mm:ss'Z'"), + YY_MM_DD_HH24_MM_SS_SSS_UTC("yy-MM-dd'T'HH:mm:ss.SSS'Z'"), + YYYY_MM_DD_HH24_MM_SS_UTC("yyyy-MM-dd'T'HH:mm:ss'Z'"), + YYYY_MM_DD_HH24_MM_SS_SSS_UTC("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + /** + * 格式 + */ + private final String format; + /** + * 格式工具 + */ + private final DateTimeFormatter dateTimeFormatter; + + private DateTimeStyle(String format) { + this.format = format; + this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); + } + + } + + /** + * 时间 + * + * @author acgist + */ + @Getter + public static enum TimeStyle { + + HH24("HH"), + HH24MM("HHmm"), + HH24_MM("HH:mm"), + HH24MMSS("HHmmss"), + HH24_MM_SS("HH:mm:ss"), + HH24MMSSSSS("HHmmssSSS"), + HH24_MM_SS_SSS("HH:mm:ss.SSS"); + + /** + * 格式 + */ + private final String format; + /** + * 格式工具 + */ + private final DateTimeFormatter dateTimeFormatter; + + private TimeStyle(String format) { + this.format = format; + this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); + } + + } + + /** + * 日期 + * + * @author acgist + */ + @Getter + public static enum DateStyle { + + YYMMDD("yyMMdd"), + YYYYMMDD("yyyyMMdd"), + YY_MM_DD("yy-MM-dd"), + YYYY_MM_DD("yyyy-MM-dd"); + + /** + * 格式 + */ + private String format; + /** + * 格式工具 + */ + private final DateTimeFormatter dateTimeFormatter; + + private DateStyle(String format) { + this.format = format; + this.dateTimeFormatter = DateTimeFormatter.ofPattern(format); + } + + } + /** * 生成时间戳 * diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java index 355972a..d66bf26 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ErrorUtils.java @@ -76,7 +76,7 @@ public final class ErrorUtils { * * @return 错误信息 */ - public static final Message message(HttpServletRequest request, HttpServletResponse response) { + public static final Message message(HttpServletRequest request, HttpServletResponse response) { return message(null, request, response); } @@ -87,11 +87,11 @@ public final class ErrorUtils { * * @return 错误信息 */ - public static final Message message(Throwable t, HttpServletRequest request, HttpServletResponse response) { - final Message message; + public static final Message message(Throwable t, HttpServletRequest request, HttpServletResponse response) { + final Message message; int status = globalStatus(request, response); final Object globalError = t == null ? globalError(request) : t; - final Object rootError = ExceptionUtils.root(globalError); + final Object rootError = rootException(globalError); if(rootError instanceof MessageCodeException) { // 自定义的异常 final MessageCodeException messageCodeException = (MessageCodeException) rootError; @@ -220,4 +220,35 @@ public final class ErrorUtils { return messageCode.getMessage(); } + /** + * @param t 异常 + * + * @return 原始异常 + * + * @see #rootException(Throwable) + */ + public static final Object rootException(Object t) { + if(t instanceof Throwable) { + return rootException((Throwable) t); + } + return t; + } + + /** + * @param t 异常 + * + * @return 原始异常 + */ + public static final Throwable rootException(Throwable t) { + Throwable cause = t; + do { + // 直接返回状态编码异常 + if(cause instanceof MessageCodeException) { + return cause; + } + } while(cause != null && (cause = cause.getCause()) != null); + // 返回原始异常 + return t; + } + } diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ExceptionUtils.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ExceptionUtils.java deleted file mode 100644 index 69e4f9b..0000000 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/ExceptionUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.acgist.taoyao.boot.utils; - -import com.acgist.taoyao.boot.model.MessageCodeException; - -/** - * 异常工具 - * - * @author acgist - */ -public class ExceptionUtils { - - /** - * @param t 异常 - * - * @return 原始异常 - * - * @see #root(Throwable) - */ - public static final Object root(Object t) { - if(t instanceof Throwable) { - return ExceptionUtils.root((Throwable) t); - } - return t; - } - - /** - * @param t 异常 - * - * @return 原始异常 - */ - public static final Throwable root(Throwable t) { - Throwable cause = t; - do { - // 直接返回状态编码异常 - if(cause instanceof MessageCodeException) { - return cause; - } - } while(cause != null && (cause = cause.getCause()) != null); - // 返回原始异常 - return t; - } - -} diff --git a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java index 3850417..0a3a49b 100644 --- a/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java +++ b/taoyao-boot/src/main/java/com/acgist/taoyao/boot/utils/JSONUtils.java @@ -10,10 +10,10 @@ import java.util.Map; import java.util.Objects; import java.util.TimeZone; -import com.acgist.taoyao.boot.config.FormatStyle.DateStyle; -import com.acgist.taoyao.boot.config.FormatStyle.DateTimeStyle; -import com.acgist.taoyao.boot.config.FormatStyle.TimeStyle; import com.acgist.taoyao.boot.model.MessageCodeException; +import com.acgist.taoyao.boot.utils.DateUtils.DateStyle; +import com.acgist.taoyao.boot.utils.DateUtils.DateTimeStyle; +import com.acgist.taoyao.boot.utils.DateUtils.TimeStyle; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; diff --git a/taoyao-client/pom.xml b/taoyao-client/pom.xml deleted file mode 100644 index 1c71edc..0000000 --- a/taoyao-client/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - 4.0.0 - - - com.acgist - taoyao - 1.0.0 - - - taoyao-client - jar - - taoyao-client - 终端:帐号(移动端|浏览器)、摄像头 - - - - com.acgist - taoyao-boot - - - org.springframework.boot - spring-boot-starter-web - - - - \ No newline at end of file diff --git a/taoyao-nat/README.md b/taoyao-nat/README.md new file mode 100644 index 0000000..6b4627f --- /dev/null +++ b/taoyao-nat/README.md @@ -0,0 +1,3 @@ +# 内网穿透 + +请用公共STUN/TURN服务或者自行搭建coturn服务。 diff --git a/taoyao-nat/pom.xml b/taoyao-nat/pom.xml index f7d5045..c489356 100644 --- a/taoyao-nat/pom.xml +++ b/taoyao-nat/pom.xml @@ -14,7 +14,7 @@ jar taoyao-net - 内网穿透:STUN/TURN暂不实现(请用公共服务或者搭建coturn服务) + 内网穿透:STUN/TURN diff --git a/taoyao-server/LICENSE b/taoyao-server/LICENSE new file mode 100644 index 0000000..6e6a6b0 --- /dev/null +++ b/taoyao-server/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2022] [acgist] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/taoyao-server/pom.xml b/taoyao-server/pom.xml index dd84c87..2027045 100644 --- a/taoyao-server/pom.xml +++ b/taoyao-server/pom.xml @@ -26,10 +26,6 @@ com.acgist taoyao-live - - com.acgist - taoyao-client - com.acgist taoyao-signal diff --git a/taoyao-server/src/main/resources/application.yml b/taoyao-server/src/main/resources/application.yml index 5a82ead..0ed7da7 100644 --- a/taoyao-server/src/main/resources/application.yml +++ b/taoyao-server/src/main/resources/application.yml @@ -48,7 +48,14 @@ taoyao: version: 1.0.0 description: WebRTC信令服务 webrtc: - type: + type: SFU + stun: + - stun:stun1.l.google.com:19302 + - stun:stun2.l.google.com:19302 + - stun:stun3.l.google.com:19302 + - stun:stun4.l.google.com:19302 + - stun:stun.stunprotocol.org:3478 + turn: security: realm: taoyao permit: /v3/api-docs/,/swagger-ui/,/error diff --git a/taoyao-signal/README.md b/taoyao-signal/README.md new file mode 100644 index 0000000..b89b012 --- /dev/null +++ b/taoyao-signal/README.md @@ -0,0 +1,43 @@ +# 信令 + +## 格式 + +``` +{ + "header": { + "v": "版本", + "id": 请求标识, + "sn": "设备标识" + "pid": 信令标识, + }, + "code": "响应编码", + "message": "响应描述", + "body": { + // 信令参数 + } +} +``` + +## 系统信令(1000~1999|9999) + +### 异常信令(9999) + +## 设备信令(2000~2999) + +### 注册信令(2000) + +``` +{ +"username": "username", +"password": "password" +} +``` + +### 关闭信令(2001) + +### 上线信令(2002) + +### 下线信令(2003) + +## 房间信令(3000~3999) + diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/config/SignalAutoConfiguration.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/config/SignalAutoConfiguration.java index de89481..090ff50 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/config/SignalAutoConfiguration.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/config/SignalAutoConfiguration.java @@ -1,14 +1,11 @@ package com.acgist.taoyao.signal.config; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; -import com.acgist.taoyao.signal.protocol.ProtocolManager; -import com.acgist.taoyao.signal.session.SessionManager; -import com.acgist.taoyao.signal.session.websocket.TaoyaoWebSocket; +import com.acgist.taoyao.signal.session.websocket.WebSocketSignal; /** * 信令配置 @@ -18,14 +15,10 @@ import com.acgist.taoyao.signal.session.websocket.TaoyaoWebSocket; @Configuration public class SignalAutoConfiguration { - @Autowired - private ProtocolManager eventManager; - @Autowired - private SessionManager sessionManager; - - @Autowired - public TaoyaoWebSocket taoyaoWebSocket() { - return new TaoyaoWebSocket(this.eventManager, this.sessionManager); + @Bean + @ConditionalOnMissingBean + public WebSocketSignal webSocketSignal() { + return new WebSocketSignal(); } @Bean diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/Event.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/Event.java deleted file mode 100644 index c831b01..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/Event.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.acgist.taoyao.signal.event; - -import com.acgist.taoyao.signal.protocol.Protocol; - -/** - * 事件 - * 事件主要负责执行信令 - * - * @author acgist - * - * @see Protocol - */ -public interface Event { - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/EventAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/EventAdapter.java deleted file mode 100644 index b2630d4..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/EventAdapter.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.acgist.taoyao.signal.event; - -/** - * 注册事件 - * - * @author acgist - */ -public class EventAdapter { - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/RegisterEvent.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/RegisterEvent.java index 9a2f7bd..fa2340b 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/RegisterEvent.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/event/client/RegisterEvent.java @@ -1,10 +1,31 @@ package com.acgist.taoyao.signal.event.client; +import org.springframework.context.ApplicationEvent; + +import com.acgist.taoyao.signal.session.ClientSession; + +import lombok.Getter; +import lombok.Setter; + /** - * 注册事件 + * 终端注册事件 * * @author acgist */ -public class RegisterEvent { +@Getter +@Setter +public class RegisterEvent extends ApplicationEvent { + + private static final long serialVersionUID = 1L; + + /** + * 会话 + */ + private ClientSession session; + + public RegisterEvent(ClientSession session) { + super(session); + this.session = session; + } } diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/ApplicationListenerAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/ApplicationListenerAdapter.java new file mode 100644 index 0000000..fc5002f --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/ApplicationListenerAdapter.java @@ -0,0 +1,21 @@ +package com.acgist.taoyao.signal.listener; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; + +import com.acgist.taoyao.signal.session.ClientSessionManager; + +/** + * 事件监听 + * + * @param 事件泛型 + * + * @author acgist + */ +public abstract class ApplicationListenerAdapter implements ApplicationListener { + + @Autowired + protected ClientSessionManager clientSessionManager; + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/RegisterListener.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/RegisterListener.java new file mode 100644 index 0000000..4f8d365 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/listener/client/RegisterListener.java @@ -0,0 +1,38 @@ +package com.acgist.taoyao.signal.listener.client; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.event.client.RegisterEvent; +import com.acgist.taoyao.signal.listener.ApplicationListenerAdapter; +import com.acgist.taoyao.signal.protocol.client.OnlineProtocol; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 终端注册监听 + * + * @author acgist + */ +@Component +public class RegisterListener extends ApplicationListenerAdapter { + + @Autowired + private OnlineProtocol onlineProtocol; + + @Async + @Override + public void onApplicationEvent(RegisterEvent event) { + final ClientSession session = event.getSession(); + if(!session.authorized()) { + return; + } + final Message message = this.onlineProtocol.build(); + message.setBody(Map.of("sn", session.sn())); + this.clientSessionManager.broadcast(message); + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Message.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Message.java deleted file mode 100644 index 8012bac..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/message/Message.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.acgist.taoyao.signal.message; - -import java.io.Serializable; - -import com.acgist.taoyao.boot.utils.JSONUtils; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -/** - * 信令消息 - * - * @author acgist - */ -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Message implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * 信令头部 - */ - private Header header; - /** - * 信令主体 - */ - private Object body; - - @Override - public String toString() { - return JSONUtils.toJSON(this); - } - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java index 2792575..d4ec31a 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/Protocol.java @@ -1,5 +1,10 @@ package com.acgist.taoyao.signal.protocol; +import org.springframework.context.ApplicationEvent; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.session.ClientSession; + /** * 信令协议 * @@ -7,9 +12,33 @@ package com.acgist.taoyao.signal.protocol; * 2000~2999:终端信令(注册、注销、终端列表) * 3000~3999:直播信令 * 4000~4999:会议信令 + * 9999:信令异常 * * @author acgist */ public interface Protocol { + + /** + * @return 信令协议标识 + */ + Integer protocol(); + /** + * 处理信令消息 + * + * @param sn 终端标识 + * @param message 信令消息 + * @param session 会话 + * + * @return 事件 + */ + ApplicationEvent execute(String sn, Message message, ClientSession session); + + /** + * 创建信令消息 + * + * @return 信令消息 + */ + Message build(); + } diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java index 1b862e5..8a1f839 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolAdapter.java @@ -1,5 +1,49 @@ package com.acgist.taoyao.signal.protocol; -public class ProtocolAdapter { +import org.springframework.beans.factory.annotation.Autowired; +import com.acgist.taoyao.boot.config.TaoyaoProperties; +import com.acgist.taoyao.boot.model.Header; +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.service.IdService; + +/** + * 信令协议适配器 + * + * @author acgist + */ +public abstract class ProtocolAdapter implements Protocol { + + @Autowired + private IdService idService; + @Autowired + protected TaoyaoProperties taoyaoProperties; + + /** + * 信令协议标识 + */ + protected final Integer protocol; + + protected ProtocolAdapter(Integer protocol) { + this.protocol = protocol; + } + + @Override + public Integer protocol() { + return this.protocol; + } + + @Override + public Message build() { + final Header header = Header.builder() + .v(this.taoyaoProperties.getVersion()) + .id(this.idService.id()) + .pid(this.protocol) + .build(); + final Message message = Message.builder() + .header(header) + .build(); + return message; + } + } diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyMapAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyMapAdapter.java new file mode 100644 index 0000000..22fb3e6 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyMapAdapter.java @@ -0,0 +1,44 @@ +package com.acgist.taoyao.signal.protocol; + +import java.util.Map; + +import org.springframework.context.ApplicationEvent; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.model.MessageCodeException; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 信令协议Map主体适配器 + * + * @author acgist + */ +public abstract class ProtocolBodyMapAdapter extends ProtocolAdapter { + + protected ProtocolBodyMapAdapter(Integer protocol) { + super(protocol); + } + + @Override + public ApplicationEvent execute(String sn, Message message, ClientSession session) { + final Object body = message.getBody(); + if(body instanceof Map map) { + return this.execute(sn, map, message, session); + } else { + throw MessageCodeException.of("信令主体类型错误:" + message); + } + } + + /** + * 处理信令消息 + * + * @param sn 终端标识 + * @param body 消息主体 + * @param message 信令消息 + * @param session 会话 + * + * @return 事件 + */ + public abstract ApplicationEvent execute(String sn, Map body, Message message, ClientSession session); + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyObjectAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyObjectAdapter.java new file mode 100644 index 0000000..c5388e5 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolBodyObjectAdapter.java @@ -0,0 +1,55 @@ +package com.acgist.taoyao.signal.protocol; + +import java.util.Map; + +import org.springframework.cglib.beans.BeanMap; +import org.springframework.context.ApplicationEvent; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.model.MessageCodeException; +import com.acgist.taoyao.boot.utils.BeanUtils; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 信令协议对象主体适配器 + * + * @author acgist + */ +public abstract class ProtocolBodyObjectAdapter extends ProtocolAdapter { + + /** + * 对象类型 + */ + private final Class clazz; + + protected ProtocolBodyObjectAdapter(Integer protocol, Class clazz) { + super(protocol); + this.clazz = clazz; + } + + @Override + public ApplicationEvent execute(String sn, Message message, ClientSession session) { + final Object body = message.getBody(); + if(body instanceof Map map) { + final T t = BeanUtils.newInstance(this.clazz); + final BeanMap beanMap = BeanMap.create(t); + beanMap.putAll(map); + return this.execute(sn, t, message, session); + } else { + throw MessageCodeException.of("信令主体类型错误:" + message); + } + } + + /** + * 处理信令消息 + * + * @param sn 终端标识 + * @param body 消息主体 + * @param message 信令消息 + * @param session 会话 + * + * @return 事件 + */ + public abstract ApplicationEvent execute(String sn, T body, Message message, ClientSession session); + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java index baccd61..661470f 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/ProtocolManager.java @@ -1,20 +1,98 @@ package com.acgist.taoyao.signal.protocol; -import javax.websocket.Session; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; import org.springframework.stereotype.Service; +import com.acgist.taoyao.boot.model.Header; +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.model.MessageCodeException; +import com.acgist.taoyao.boot.utils.JSONUtils; +import com.acgist.taoyao.signal.protocol.client.RegisterProtocol; +import com.acgist.taoyao.signal.session.ClientSession; +import com.acgist.taoyao.signal.session.ClientSessionManager; + +import lombok.extern.slf4j.Slf4j; + /** * 协议管理 * * @author acgist */ +@Slf4j @Service public class ProtocolManager { - public void execute(Session session, String message) { - // TODO Auto-generated method stub - + /** + * 协议映射 + */ + private Map protocolMapping = new ConcurrentHashMap<>(); + + @Autowired + private ApplicationContext context; + @Autowired + private ClientSessionManager clientSessionManager; + + @PostConstruct + public void init() { + final Map map = this.context.getBeansOfType(Protocol.class); + map.forEach((k, v) -> { + final Integer protocol = v.protocol(); + if(this.protocolMapping.containsKey(protocol)) { + throw MessageCodeException.of("存在重复信令协议:" + protocol); + } + log.info("注册信令协议:{}-{}", protocol, k); + this.protocolMapping.put(protocol, v); + }); + } + + /** + * 执行信令消息 + * + * @param message 信令消息 + * @param instance 会话实例 + */ + public void execute(String message, AutoCloseable instance) { + if(StringUtils.isEmpty(message)) { + log.warn("消息为空:{}", message); + return; + } + final Message value = JSONUtils.toJava(message, Message.class); + final Header header = value.getHeader(); + if(header == null) { + log.warn("消息格式错误(没有头部):{}", message); + return; + } + final String sn = header.getSn(); + final Integer pid = header.getPid(); + if(sn == null || pid == null) { + log.warn("消息格式错误(没有SN或者PID):{}", message); + return; + } + final Protocol protocol = this.protocolMapping.get(pid); + if(protocol == null) { + log.warn("不支持的信令协议:{}", message); + return; + } + ApplicationEvent event = null; + final ClientSession session = this.clientSessionManager.session(instance); + if(session != null && protocol instanceof RegisterProtocol) { + event = protocol.execute(sn, value, session); + } else if(session != null) { + event = protocol.execute(sn, value, session); + } else { + log.warn("会话没有权限:{}", message); + } + if(event != null) { + this.context.publishEvent(event); + } } } diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/CloseProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/CloseProtocol.java new file mode 100644 index 0000000..7899054 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/CloseProtocol.java @@ -0,0 +1,33 @@ +package com.acgist.taoyao.signal.protocol.client; + +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.protocol.ProtocolAdapter; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 关闭信令协议 + * + * @author acgist + */ +@Component +public class CloseProtocol extends ProtocolAdapter { + + /** + * 信令协议标识 + */ + public static final Integer PID = 2001; + + public CloseProtocol() { + super(PID); + } + + @Override + public ApplicationEvent execute(String sn, Message message, ClientSession session) { + // TODO + return null; + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OfflineProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OfflineProtocol.java new file mode 100644 index 0000000..16ab85c --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OfflineProtocol.java @@ -0,0 +1,10 @@ +package com.acgist.taoyao.signal.protocol.client; + +public class OfflineProtocol { + + /** + * 信令协议标识 + */ + public static final Integer PID = 2003; + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OnlineProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OnlineProtocol.java new file mode 100644 index 0000000..0e911d0 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/OnlineProtocol.java @@ -0,0 +1,32 @@ +package com.acgist.taoyao.signal.protocol.client; + +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.protocol.ProtocolAdapter; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 上线信令协议 + * + * @author acgist + */ +@Component +public class OnlineProtocol extends ProtocolAdapter { + + /** + * 信令协议标识 + */ + public static final Integer PID = 2002; + + public OnlineProtocol() { + super(PID); + } + + @Override + public ApplicationEvent execute(String sn, Message message, ClientSession session) { + return null; + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/RegisterProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/RegisterProtocol.java new file mode 100644 index 0000000..ace1987 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/client/RegisterProtocol.java @@ -0,0 +1,54 @@ +package com.acgist.taoyao.signal.protocol.client; + +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.config.SecurityProperties; +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.boot.model.MessageCode; +import com.acgist.taoyao.signal.event.client.RegisterEvent; +import com.acgist.taoyao.signal.protocol.ProtocolBodyMapAdapter; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 注册信令协议 + * + * @author acgist + */ +@Component +public class RegisterProtocol extends ProtocolBodyMapAdapter { + + /** + * 信令协议标识 + */ + public static final Integer PID = 2000; + + @Autowired + private SecurityProperties securityProperties; + + public RegisterProtocol() { + super(PID); + } + + @Override + public ApplicationEvent execute(String sn, Map body, Message message, ClientSession session) { + final String username = (String) body.get("username"); + final String password = (String) body.get("password"); + if( + StringUtils.equals(this.securityProperties.getUsername(), username) && + StringUtils.equals(this.securityProperties.getPassword(), password) + ) { + session.authorize(sn); + message.setCode(MessageCode.CODE_0000); + } else { + message.setCode(MessageCode.CODE_3401); + } + session.push(message); + return new RegisterEvent(session); + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/system/ErrorProtocol.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/system/ErrorProtocol.java new file mode 100644 index 0000000..6449219 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/protocol/system/ErrorProtocol.java @@ -0,0 +1,32 @@ +package com.acgist.taoyao.signal.protocol.system; + +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Component; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.protocol.ProtocolAdapter; +import com.acgist.taoyao.signal.session.ClientSession; + +/** + * 异常信令协议 + * + * @author acgist + */ +@Component +public class ErrorProtocol extends ProtocolAdapter { + + /** + * 信令协议标识 + */ + public static final Integer PID = 9999; + + public ErrorProtocol() { + super(PID); + } + + @Override + public ApplicationEvent execute(String sn, Message message, ClientSession session) { + return null; + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSession.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSession.java new file mode 100644 index 0000000..83a83c7 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSession.java @@ -0,0 +1,66 @@ +package com.acgist.taoyao.signal.session; + +import com.acgist.taoyao.boot.model.Message; + +/** + * 会话 + * + * @author acgist + * + * @param 会话类型 + */ +public interface ClientSession extends AutoCloseable { + + /** + * @return 终端标识 + */ + String sn(); + + /** + * 推送消息 + * + * @param message 消息 + */ + void push(Message message); + + /** + * @param timeout 超时时间 + * + * @return 是否超时会话 + */ + boolean timeout(long timeout); + + /** + * 设置授权 + * + * @param sn 重点标识 + */ + void authorize(String sn); + + /** + * @return 是否授权 + */ + boolean authorized(); + + /** + * @param sn 终端标识 + * + * @return 终端标识是否匹配 + */ + boolean matchSn(String sn); + + /** + * @param sn 终端标识 + * + * @return 终端标识是否匹配失败 + */ + boolean matchNoneSn(String sn); + + /** + * @param instance 会话实例 + * + * @return 会话实例是否匹配 + */ + boolean matchInstance(M instance); + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionAdapter.java new file mode 100644 index 0000000..c3e5824 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionAdapter.java @@ -0,0 +1,88 @@ +package com.acgist.taoyao.signal.session; + +import org.apache.commons.lang3.StringUtils; + +/** + * 会话适配器 + * + * @author acgist + */ +public abstract class ClientSessionAdapter implements ClientSession { + + /** + * 终端标识 + */ + protected String sn; + /** + * 进入时间 + */ + protected final long time; + /** + * 会话实例 + */ + protected final T instance; + /** + * 是否授权 + */ + protected boolean authorized; + + protected ClientSessionAdapter(T instance) { + this.time = System.currentTimeMillis(); + this.instance = instance; + this.authorized = false; + } + + @Override + public String sn() { + return this.sn; + } + + @Override + public boolean timeout(long timeout) { + return !(this.authorized && System.currentTimeMillis() - this.time <= timeout); + } + + @Override + public void authorize(String sn) { + this.sn = sn; + this.authorized = true; + } + + @Override + public boolean authorized() { + return this.authorized; + } + + @Override + public boolean matchSn(String sn) { + return StringUtils.equals(sn, this.sn); + } + + @Override + public boolean matchNoneSn(String sn) { + return !StringUtils.equals(sn, this.sn); + } + + @Override + public boolean matchInstance(I instance) { + return instance == this.instance; + } + + @Override + public void close() throws Exception { + try { + this.instance.close(); + } finally { + // TODO:退出房间 + // TODO:退出帐号 + } + } + + /** + * @return 会话实例 + */ + public T instance() { + return this.instance; + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionManager.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionManager.java new file mode 100644 index 0000000..24a39e4 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionManager.java @@ -0,0 +1,129 @@ +package com.acgist.taoyao.signal.session; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import com.acgist.taoyao.boot.config.TaoyaoProperties; +import com.acgist.taoyao.boot.model.Message; + +import lombok.extern.slf4j.Slf4j; + +/** + * 会话管理 + * + * @author acgist + */ +@Slf4j +@Service +public class ClientSessionManager { + + @Autowired + private TaoyaoProperties taoyaoProperties; + + /** + * 会话列表 + */ + private List sessions = new CopyOnWriteArrayList<>(); + + @Scheduled(cron = "${taoyao.scheduled.session:0 * * * * ?}") + public void scheduled() { + this.closeTimeoutSession(); + } + + /** + * @param session 会话 + */ + public void open(ClientSession session) { + this.sessions.add(session); + } + + /** + * @param instance 会话实例 + * + * @return 会话 + */ + public ClientSession session(AutoCloseable instance) { + return this.sessions.stream() + .filter(v -> v.matchInstance(instance)) + .findFirst() + .orElse(null); + } + + /** + * 单播消息 + * + * @param to 接收终端 + * @param message 消息 + */ + public void unicast(String to, Message message) { + this.sessions.stream().filter(v -> v.matchSn(to)).forEach(v -> { + message.getHeader().setSn(v.sn()); + v.push(message); + }); + } + + /** + * 广播消息 + * + * @param message 消息 + */ + public void broadcast(Message message) { + this.sessions.forEach(v -> { + message.getHeader().setSn(v.sn()); + v.push(message); + }); + } + + /** + * 广播消息 + * + * @param from 发送终端 + * @param message 消息 + */ + public void broadcast(String from, Message message) { + this.sessions.stream().filter(v -> v.matchNoneSn(from)).forEach(v -> { + message.getHeader().setSn(v.sn()); + v.push(message); + }); + } + + /** + * 关闭会话 + * + * @param instance 会话实例 + */ + public void close(AutoCloseable instance) { + final ClientSession session = this.session(instance); + try { + if(session != null) { + session.close(); + } else { + instance.close(); + } + } catch (Exception e) { + log.error("关闭会话异常", e); + } finally { + if(session != null) { + this.sessions.remove(session); + } + } + } + + /** + * 定时关闭超时会话 + */ + private void closeTimeoutSession() { + log.debug("定时关闭超时会话"); + this.sessions.stream() + .filter(v -> v.timeout(this.taoyaoProperties.getTimeout())) + .forEach(v -> { + log.debug("关闭超时会话:{}", v); + this.close(v); + }); + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionStatus.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionStatus.java new file mode 100644 index 0000000..e9f78a5 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/ClientSessionStatus.java @@ -0,0 +1,12 @@ +package com.acgist.taoyao.signal.session; + +/** + * 终端状态 + * + * @author acgist + */ +public class ClientSessionStatus { + + + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/Session.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/Session.java deleted file mode 100644 index 4133704..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/Session.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.acgist.taoyao.signal.session; - -public interface Session { - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionAdapter.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionAdapter.java deleted file mode 100644 index 2f514a2..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionAdapter.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.acgist.taoyao.signal.session; - -public class SessionAdapter { - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionManager.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionManager.java deleted file mode 100644 index 1cc52bf..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/SessionManager.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.acgist.taoyao.signal.session; - -import java.io.IOException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -import javax.websocket.Session; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; - -import com.acgist.taoyao.boot.config.TaoyaoProperties; -import com.acgist.taoyao.signal.message.Message; -import com.acgist.taoyao.signal.session.websocket.SessionWrapper; - -import lombok.extern.slf4j.Slf4j; - -/** - * 会话管理 - * - * @author acgist - */ -@Slf4j -@Service -public class SessionManager { - - @Autowired - private TaoyaoProperties taoyaoProperties; - - /** - * 没有授权会话 - */ - private Map unauthorized = new ConcurrentHashMap<>(); - /** - * 授权会话列表 - */ - private List sessions = new CopyOnWriteArrayList<>(); - - @Scheduled(cron = "${taoyao.scheduled.session:0 * * * * ?}") - public void scheduled() { - this.closeTimeoutSession(); - } - - /** - * 存入没有授权会话,定时清除没有授权会话。 - * - * @param session 会话 - */ - public void open(Session session) { - this.unauthorized.put(session, System.currentTimeMillis()); - } - - /** - * @param session 会话 - * - * @return 会话包装器 - */ - public SessionWrapper getWrapper(Session session) { - return this.sessions.stream() - .filter(v -> v.matchSession(session)) - .findFirst() - .orElse(null); - } - - /** - * 认证会话 - * - * @param sn 终端标识 - * @param session 会话 - */ - public void authorized(String sn, Session session) { - this.unauthorized.remove(session); - final SessionWrapper wrapper = new SessionWrapper(); - wrapper.setSn(sn); - wrapper.setSession(session); - this.sessions.add(wrapper); - } - - /** - * 单播消息 - * - * @param to 接收终端 - * @param message 消息 - */ - public void unicast(String to, Message message) { - this.sessions.stream().filter(v -> v.matchSn(to)).forEach(v -> { - message.getHeader().setSn(v.getSn()); - message.getHeader().setVersion(this.taoyaoProperties.getVersion()); - v.send(message); - }); - } - - /** - * 广播消息 - * - * @param message 消息 - */ - public void broadcast(Message message) { - this.sessions.forEach(v -> { - message.getHeader().setSn(v.getSn()); - message.getHeader().setVersion(this.taoyaoProperties.getVersion()); - v.send(message); - }); - } - - /** - * 广播消息 - * - * @param from 发送终端 - * @param message 消息 - */ - public void broadcast(String from, Message message) { - this.sessions.stream().filter(v -> v.matchNoneSn(from)).forEach(v -> { - message.getHeader().setSn(v.getSn()); - message.getHeader().setVersion(this.taoyaoProperties.getVersion()); - v.send(message); - }); - } - - /** - * 关闭会话 - * - * @param session 会话 - */ - public void close(Session session) { - final SessionWrapper wrapper = this.getWrapper(session); - if(wrapper != null) { - // TODO:退出房间 - // TODO:退出帐号 - // 移除 - this.sessions.remove(wrapper); - } - try { - session.close(); - } catch (IOException e) { - log.error("关闭会话异常", e); - } - } - - /** - * 定时关闭超时会话 - */ - private void closeTimeoutSession() { - log.debug("定时关闭超时会话"); - final Iterator> iterator = this.unauthorized.entrySet().iterator(); - while(iterator.hasNext()) { - final Entry next = iterator.next(); - final Long last = next.getValue(); - final Session session = next.getKey(); - if(System.currentTimeMillis() - last > this.taoyaoProperties.getTimeout()) { - log.debug("关闭超时会话:{}", session); - this.close(session); - } - } - } - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/TaoyaoSocket.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/SocketSignal.java similarity index 77% rename from taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/TaoyaoSocket.java rename to taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/SocketSignal.java index 10c2ec3..02f40d2 100644 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/TaoyaoSocket.java +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/socket/SocketSignal.java @@ -5,6 +5,6 @@ package com.acgist.taoyao.signal.session.socket; * * @author acgist */ -public class TaoyaoSocket { +public class SocketSignal { } diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/SessionWrapper.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/SessionWrapper.java deleted file mode 100644 index f347f3d..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/SessionWrapper.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.acgist.taoyao.signal.session.websocket; - -import java.io.IOException; - -import javax.websocket.Session; - -import com.acgist.taoyao.signal.message.Message; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -/** - * 会话包装器 - * - * @author acgist - */ -@Slf4j -@Getter -@Setter -public class SessionWrapper { - - /** - * 终端帐号 - */ - private String sn; - /** - * 会话 - */ - private Session session; - - /** - * 发送消息 - * - * @param message 消息 - */ - public void send(Message message) { - try { - this.session.getBasicRemote().sendText(message.toString()); - } catch (IOException e) { - log.error("WebSocket发送消息异常:{}", message, e); - } - } - - /** - * @param sn 终端编号 - * - * @return 是否匹配成功 - */ - public boolean matchSn(String sn) { - return this.sn != null && this.sn.equals(sn); - } - - /** - * @param sn 终端编号 - * - * @return 是否匹配失败 - */ - public boolean matchNoneSn(String sn) { - return this.sn != null && !this.sn.equals(sn); - } - - /** - * @param session 会话 - * - * @return 是否匹配成功 - */ - public boolean matchSession(Session session) { - return this.session != null && this.session.equals(session); - } - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/TaoyaoWebSocket.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/TaoyaoWebSocket.java deleted file mode 100644 index ce2ace5..0000000 --- a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/TaoyaoWebSocket.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.acgist.taoyao.signal.session.websocket; - -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; -import javax.websocket.server.ServerEndpoint; - -import com.acgist.taoyao.signal.protocol.ProtocolManager; -import com.acgist.taoyao.signal.session.SessionManager; - -import lombok.extern.slf4j.Slf4j; - -/** - * WebSocket信令 - * - * @author acgist - */ -@Slf4j -@ServerEndpoint(value = "/taoyao/websocket") -public class TaoyaoWebSocket { - - private ProtocolManager eventManager; - private SessionManager sessionManager; - - public TaoyaoWebSocket(ProtocolManager eventManager, SessionManager sessionManager) { - this.eventManager = eventManager; - this.sessionManager = sessionManager; - } - - @OnOpen - public void open(Session session) { - log.debug("会话连接:{}", session); - this.sessionManager.open(session); - } - - @OnMessage - public void message(Session session, String message) { - log.debug("会话消息:{}-{}", session, message); - try { - this.eventManager.execute(session, message); - } catch (Exception e) { - log.error("处理会话消息异常", e); - } - } - - @OnClose - public void close(Session session) { - log.debug("会话关闭:{}", session); - this.sessionManager.close(session); - } - - @OnError - public void error(Session session, Throwable e) { - log.error("会话异常:{}", session, e); - this.close(session); - } - -} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSession.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSession.java new file mode 100644 index 0000000..6212d29 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSession.java @@ -0,0 +1,37 @@ +package com.acgist.taoyao.signal.session.websocket; + +import java.io.IOException; + +import javax.websocket.Session; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.session.ClientSessionAdapter; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * WebSocket会话 + * + * @author acgist + */ +@Slf4j +@Getter +@Setter +public class WebSocketSession extends ClientSessionAdapter { + + public WebSocketSession(Session instance) { + super(instance); + } + + @Override + public void push(Message message) { + try { + this.instance.getBasicRemote().sendText(message.toString()); + } catch (IOException e) { + log.error("WebSocket发送消息异常:{}", message, e); + } + } + +} diff --git a/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSignal.java b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSignal.java new file mode 100644 index 0000000..3c9a2a9 --- /dev/null +++ b/taoyao-signal/src/main/java/com/acgist/taoyao/signal/session/websocket/WebSocketSignal.java @@ -0,0 +1,94 @@ +package com.acgist.taoyao.signal.session.websocket; + +import java.io.IOException; + +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.ServerEndpoint; + +import org.springframework.beans.factory.annotation.Autowired; + +import com.acgist.taoyao.boot.model.Message; +import com.acgist.taoyao.signal.protocol.ProtocolManager; +import com.acgist.taoyao.signal.protocol.system.ErrorProtocol; +import com.acgist.taoyao.signal.session.ClientSessionManager; + +import lombok.extern.slf4j.Slf4j; + +/** + * WebSocket信令 + * + * @author acgist + */ +@Slf4j +@ServerEndpoint(value = "/websocket.signal") +public class WebSocketSignal { + + private static ErrorProtocol errorProtocol; + private static ProtocolManager protocolManager; + private static ClientSessionManager clientSessionManager; + + @OnOpen + public void open(Session session) { + log.debug("会话连接:{}", session); + WebSocketSignal.clientSessionManager.open(new WebSocketSession(session)); + } + + @OnMessage + public void message(Session session, String message) { + log.debug("会话消息:{}-{}", session, message); + try { + WebSocketSignal.protocolManager.execute(message, session); + } catch (Exception e) { + log.error("处理会话消息异常", e); + final Message errorMessage = WebSocketSignal.errorProtocol.build(); + errorMessage.setBody(e.getMessage()); + this.push(session, errorMessage); + } + } + + @OnClose + public void close(Session session) { + log.debug("会话关闭:{}", session); + WebSocketSignal.clientSessionManager.close(session); + } + + @OnError + public void error(Session session, Throwable e) { + log.error("会话异常:{}", session, e); + this.close(session); + } + + /** + * 推送消息 + * + * @param session 会话 + * @param message 消息 + */ + private void push(Session session, Message message) { + try { + session.getBasicRemote().sendText(message.toString()); + } catch (IOException e) { + log.error("推送消息异常:{}", message, e); + } + } + + @Autowired + public void setErrorProtocol(ErrorProtocol errorProtocol) { + WebSocketSignal.errorProtocol = errorProtocol; + } + + @Autowired + public void setProtocolManager(ProtocolManager protocolManager) { + WebSocketSignal.protocolManager = protocolManager; + } + + @Autowired + public void setClientSessionManager(ClientSessionManager clientSessionManager) { + WebSocketSignal.clientSessionManager = clientSessionManager; + } + +} diff --git a/taoyao-webrtc/pom.xml b/taoyao-webrtc/pom.xml index a47ee55..7eb2e3d 100644 --- a/taoyao-webrtc/pom.xml +++ b/taoyao-webrtc/pom.xml @@ -17,9 +17,9 @@ WebRTC模块 + taoyao-webrtc-jni taoyao-webrtc-sfu taoyao-webrtc-mcu - taoyao-webrtc-mix taoyao-webrtc-mesh diff --git a/taoyao-webrtc/taoyao-webrtc-mix/pom.xml b/taoyao-webrtc/taoyao-webrtc-jni/pom.xml similarity index 65% rename from taoyao-webrtc/taoyao-webrtc-mix/pom.xml rename to taoyao-webrtc/taoyao-webrtc-jni/pom.xml index fabcb15..a81f0f8 100644 --- a/taoyao-webrtc/taoyao-webrtc-mix/pom.xml +++ b/taoyao-webrtc/taoyao-webrtc-jni/pom.xml @@ -10,19 +10,13 @@ 1.0.0 - taoyao-webrtc-mix + taoyao-webrtc-jni jar - taoyao-webrtc-mix - MCU/SFU混合媒体服务 + taoyao-webrtc-jni + WebRTC JNI:WebRTC本地接口 - \ No newline at end of file diff --git a/taoyao-webrtc/taoyao-webrtc-mcu/pom.xml b/taoyao-webrtc/taoyao-webrtc-mcu/pom.xml index aa188ec..47581d4 100644 --- a/taoyao-webrtc/taoyao-webrtc-mcu/pom.xml +++ b/taoyao-webrtc/taoyao-webrtc-mcu/pom.xml @@ -19,7 +19,7 @@ com.acgist - taoyao-webrtc-mix + taoyao-webrtc-jni diff --git a/taoyao-webrtc/taoyao-webrtc-sfu/pom.xml b/taoyao-webrtc/taoyao-webrtc-sfu/pom.xml index 6976fa9..a3ad050 100644 --- a/taoyao-webrtc/taoyao-webrtc-sfu/pom.xml +++ b/taoyao-webrtc/taoyao-webrtc-sfu/pom.xml @@ -19,7 +19,7 @@ com.acgist - taoyao-webrtc-mix + taoyao-webrtc-jni