[+] 信令调整

This commit is contained in:
acgist
2023-03-03 08:09:35 +08:00
parent b53f50adf7
commit c3dbe52d5c
36 changed files with 588 additions and 306 deletions

View File

@@ -13,3 +13,7 @@
[信令格式](https://localhost:8888/protocol/list)
## TODO
标识 -> ID
所有字段获取 -> get

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.2</version>
<version>3.0.3</version>
<relativePath />
</parent>

View File

@@ -2,19 +2,18 @@ package com.acgist.taoyao.boot.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* 信令描述
*
* @author acgist
*/
@Target(ElementType.TYPE)
@Component
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Description {
@@ -22,7 +21,7 @@ public @interface Description {
/**
* @return 消息主体
*/
String[] body() default { "{}" };
String[] body() default { "" };
/**
* @return 数据流向

View File

@@ -51,6 +51,10 @@ public interface Constant {
* 电池电量0~100
*/
String BATTERY = "battery";
/**
* 是否发生告警
*/
String ALARMING = "alarming";
/**
* 是否正在充电
*/

View File

@@ -23,6 +23,8 @@ import lombok.Setter;
@ConfigurationProperties(prefix = "taoyao.webrtc")
public class WebrtcProperties {
@Schema(title = "是否加密", description = "是否加密")
private Boolean encrypt;
@Schema(title = "STUN服务器", description = "STUN服务器")
private WebrtcStunProperties[] stun;
@Schema(title = "TURN服务器", description = "TURN服务器")

View File

@@ -58,8 +58,8 @@ public class SecurityInterceptor extends InterceptorAdapter {
if(this.permit(request) || this.authorization(request)) {
return true;
}
if(log.isInfoEnabled()) {
log.info("授权失败:{}", request.getRequestURL());
if(log.isDebugEnabled()) {
log.debug("授权失败:{}", request.getRequestURL());
}
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic Realm=\"" + this.securityProperties.getRealm() + "\"");

View File

@@ -17,9 +17,11 @@ server:
port-header: X-Forwarded-Port
protocol-header: X-Forwarded-Proto
remote-ip-header: X-Forwarded-For
# 服务前缀
# servlet:
# context-path: /taoyao
spring:
# 快速启动
# main:
# lazy-initialization: true
profiles:
@@ -54,10 +56,13 @@ taoyao:
name: 桃夭信令服务
version: 1.0.0
description: 桃夭WebRTC信令服务
# 全局超时时间
timeout: 5000
id:
# 服务端
max-index: 999999
server-index: 0
# 终端
min-client-index: 10000
max-client-index: 99999
# 媒体配置
@@ -117,6 +122,8 @@ taoyao:
buffer-size: 2048
# WebRTC配置
webrtc:
# 是否加密
encrypt: false
# STUN服务
stun:
- host: 192.168.1.110
@@ -146,7 +153,7 @@ taoyao:
password: taoyao
# 定时任务
scheduled:
media: 0 * * * * ?
# 清理无效终端连接
client: 0 * * * * ?
# 地址重写
ip-rewrite:

View File

@@ -25,7 +25,7 @@ public class ClientStatus {
private String ip;
@Schema(title = "终端名称", description = "终端名称")
private String name;
@Schema(title = "终端标识", description = "终端标识")
@Schema(title = "终端ID", description = "终端ID")
private String clientId;
@Schema(title = "终端类型", description = "终端类型")
private ClientType clientType;
@@ -41,6 +41,8 @@ public class ClientStatus {
private Integer signal;
@Schema(title = "电池电量0~100", description = "电池电量0~100")
private Integer battery;
@Schema(title = "是否发生告警", description = "是否发生告警")
private Boolean alarming;
@Schema(title = "是否正在充电", description = "是否正在充电")
private Boolean charging;
@Schema(title = "是否正在录像", description = "是否正在录像")
@@ -64,11 +66,12 @@ public class ClientStatus {
this.setTemperature(MapUtils.get(body, Constant.TEMPERATURE));
this.setSignal(MapUtils.get(body, Constant.SIGNAL));
this.setBattery(MapUtils.get(body, Constant.BATTERY));
this.setCharging(MapUtils.get(body, Constant.CHARGING));
this.setRecording(MapUtils.get(body, Constant.RECORDING));
this.setLastHeartbeat(LocalDateTime.now());
this.setAlarming(MapUtils.getBoolean(body, Constant.ALARMING));
this.setCharging(MapUtils.getBoolean(body, Constant.CHARGING));
this.setRecording(MapUtils.getBoolean(body, Constant.RECORDING));
this.status(MapUtils.get(body, Constant.STATUS));
this.config(MapUtils.get(body, Constant.CONFIG));
this.setLastHeartbeat(LocalDateTime.now());
}
/**

View File

@@ -3,7 +3,9 @@ package com.acgist.taoyao.signal.controller;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -74,27 +76,39 @@ public class ProtocolController {
final Protocol protocol = e.getValue();
final String name = protocol.name();
final String signal = protocol.signal();
final Description annotation = protocol.getClass().getDeclaredAnnotation(Description.class);
if(annotation == null) {
log.info("信令没有注解:{}-{}", key, name);
final Class<?> clazz;
final Description description;
if(AopUtils.isAopProxy(e) || AopUtils.isCglibProxy(protocol) || AopUtils.isJdkDynamicProxy(protocol)) {
// 代理获取
clazz = AopUtils.getTargetClass(protocol);
description = AnnotationUtils.findAnnotation(clazz, Description.class);
} else {
// 直接获取
clazz = protocol.getClass();
description = AnnotationUtils.findAnnotation(clazz, Description.class);
}
if(description == null) {
log.info("信令没有注解:{} - {}", key, name);
return;
}
// 信令名称
builder.append("### ").append(name).append("").append(signal).append("")
.append(Constant.LINE).append(Constant.LINE)
.append("```").append(Constant.LINE);
// 消息主体
builder.append("# 消息主体").append(Constant.LINE);
Stream.of(annotation.body()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
// 数据流向
builder.append("# 数据流向").append(Constant.LINE);
Stream.of(annotation.flow()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
builder.append("```").append(Constant.LINE).append(Constant.LINE);
builder.append("### ").append(name)
.append("").append(signal).append("")
.append(Constant.LINE).append(Constant.LINE);
// 描述信息
final String memo = annotation.memo().strip();
final String memo = description.memo().strip();
if(StringUtils.isNotEmpty(memo)) {
builder.append(memo).append(Constant.LINE).append(Constant.LINE);
}
// 消息主体
builder
.append("```").append(Constant.LINE)
.append("# 消息主体").append(Constant.LINE);
Stream.of(description.body()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
// 数据流向
builder.append("# 数据流向").append(Constant.LINE);
Stream.of(description.flow()).forEach(line -> builder.append(line.strip()).append(Constant.LINE));
builder.append("```").append(Constant.LINE).append(Constant.LINE);
});
return builder.toString();
}

View File

@@ -0,0 +1,23 @@
package com.acgist.taoyao.signal.event;
import com.acgist.taoyao.signal.client.Client;
import lombok.Getter;
import lombok.Setter;
/**
* 终端配置事件
*
* @author acgist
*/
@Getter
@Setter
public class ClientConfigEvent extends ClientEventAdapter {
private static final long serialVersionUID = 1L;
public ClientConfigEvent(Client client) {
super(client);
}
}

View File

@@ -0,0 +1,23 @@
package com.acgist.taoyao.signal.event;
import com.acgist.taoyao.signal.client.Client;
import lombok.Getter;
import lombok.Setter;
/**
* 终端下线事件
*
* @author acgist
*/
@Getter
@Setter
public class ClientOfflineEvent extends ClientEventAdapter {
private static final long serialVersionUID = 1L;
public ClientOfflineEvent(Client client) {
super(client);
}
}

View File

@@ -0,0 +1,23 @@
package com.acgist.taoyao.signal.event;
import com.acgist.taoyao.signal.client.Client;
import lombok.Getter;
import lombok.Setter;
/**
* 终端上线事件
*
* @author acgist
*/
@Getter
@Setter
public class ClientOnlineEvent extends ClientEventAdapter {
private static final long serialVersionUID = 1L;
public ClientOnlineEvent(Client client) {
super(client);
}
}

View File

@@ -0,0 +1,30 @@
package com.acgist.taoyao.signal.event;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.party.media.Room;
import lombok.Getter;
import lombok.Setter;
/**
* 离开房间事件
*
* @author acgist
*/
@Getter
@Setter
public class RoomLeaveEvent extends RoomEventAdapter {
private static final long serialVersionUID = 1L;
/**
* 离开终端
*/
private final Client client;
public RoomLeaveEvent(Room room, Client client) {
super(room);
this.client = client;
}
}

View File

@@ -16,7 +16,7 @@ import lombok.Setter;
*/
@Getter
@Setter
public class ClientWrapper {
public class ClientWrapper implements AutoCloseable {
/**
* 媒体订阅类型
@@ -131,5 +131,9 @@ public class ClientWrapper {
return this.producers.values().stream()
.anyMatch(v -> v.getConsumers().values().stream().anyMatch(c -> c.getProducer() == producer));
}
@Override
public void close() throws Exception {
}
}

View File

@@ -87,14 +87,21 @@ public class Room implements Closeable {
/**
* 终端离开
* 立即释放所有资源
*
* @param client 终端
*/
public void leave(Client client) {
synchronized (this.clients) {
if(this.clients.remove(client) != null) {
final ClientWrapper wrapper = this.clients.remove(client);
if(wrapper != null) {
try {
wrapper.close();
} catch (Exception e) {
log.error("终端关闭异常", e);
}
this.roomStatus.setClientSize(this.roomStatus.getClientSize() - 1);
// TODO资源释放
// TODOleave事件
}
}
}

View File

@@ -81,7 +81,6 @@ public class ProtocolManager {
* @param instance 终端实例
*/
public void execute(String content, AutoCloseable instance) {
log.debug("执行信令消息:{}", content);
final Client client = this.clientManager.clients(instance);
if(client == null) {
log.warn("信令终端无效:{}-{}", instance, content);
@@ -117,6 +116,9 @@ public class ProtocolManager {
client.push(this.platformErrorProtocol.build("不支持的信令协议:" + signal));
return;
}
if(log.isDebugEnabled()) {
log.debug("执行信令消息:{} - {}", client.clientId(), content);
}
if(protocol instanceof ClientRegisterProtocol) {
protocol.execute(client, message);
} else if(this.securityService.authenticate(client, message, protocol)) {

View File

@@ -8,6 +8,7 @@ import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientStatus;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
@@ -53,6 +54,8 @@ public class ClientAlarmProtocol extends ProtocolClientAdapter {
alarmMessage,
alarmDatetime
);
final ClientStatus status = client.status();
status.setAlarming(Boolean.TRUE);
// 业务逻辑
}

View File

@@ -20,16 +20,16 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
*/
@Protocol
@Description(
memo = "没有指定终端类型时广播所有类型终端",
body = {
"""
{
"clientType": "终端类型(可选)"
...
}
}
"""
},
flow = "终端->信令服务-)终端",
memo = "没有指定终端类型时广播所有类型终端"
flow = "终端->信令服务-)终端"
)
public class ClientBroadcastProtocol extends ProtocolClientAdapter {

View File

@@ -7,23 +7,24 @@ import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.event.ClientCloseEvent;
import com.acgist.taoyao.signal.event.ClientOfflineEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
import lombok.extern.slf4j.Slf4j;
/**
* 终端关闭信令
* 关闭终端信令
*
* @author acgist
*/
@Slf4j
@Protocol
@Description(
memo = "同时释放所有资源,所以如果终端意外掉线重连,需要终端实现音视频重连逻辑。",
flow = {
"终端->信令服务->终端",
"终端->信令服务-[终端下线])终端"
@@ -33,11 +34,8 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
public static final String SIGNAL = "client::close";
private final ClientOfflineProtocol clientOfflineProtocol;
public ClientCloseProtocol(ClientOfflineProtocol clientOfflineProtocol) {
super("终端关闭信令", SIGNAL);
this.clientOfflineProtocol = clientOfflineProtocol;
public ClientCloseProtocol() {
super("关闭终端信令", SIGNAL);
}
@Async
@@ -48,13 +46,12 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
@Override
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
// 响应消息
client.push(message.cloneWithoutBody());
// 关闭连接后会发布事件
try {
// 关闭连接后会发布事件
client.close();
} catch (Exception e) {
log.error("关闭终端异常", e);
log.error("关闭终端异常{}", clientId, e);
}
}
@@ -63,24 +60,17 @@ public class ClientCloseProtocol extends ProtocolClientAdapter implements Applic
*
* @param client 终端
*/
public void close(Client client) {
if(!client.authorized()) {
private void close(Client client) {
if(client == null || !client.authorized()) {
// 没有授权终端
return;
}
final String clientId = client.clientId();
log.info("关闭终端:{}", clientId);
// 房间释放
// 释放房间终端
this.roomManager.leave(client);
// 广播下线事件
final Message message = this.clientOfflineProtocol.build(
Map.of(Constant.CLIENT_ID, clientId)
);
this.clientManager.broadcast(clientId, message);
// TODO释放连接
// TODO释放房间
// TODO退出帐号
// TODO注意释放是否考虑没有message非正常的关闭不要立即释放
// 终端下线事件
this.publishEvent(new ClientOfflineEvent(client));
}
}

View File

@@ -4,6 +4,9 @@ import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
@@ -14,6 +17,7 @@ import com.acgist.taoyao.boot.utils.DateUtils;
import com.acgist.taoyao.boot.utils.DateUtils.DateTimeStyle;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.event.ClientConfigEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -30,9 +34,9 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
"datetime": "日期时间yyyyMMddHHmmss"
}
""",
flow = "终端-[终端注册]>信令服务->终端"
flow = "终端=[终端注册]>信令服务->终端"
)
public class ClientConfigProtocol extends ProtocolClientAdapter {
public class ClientConfigProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientConfigEvent> {
public static final String SIGNAL = "client::config";
@@ -45,6 +49,14 @@ public class ClientConfigProtocol extends ProtocolClientAdapter {
this.webrtcProperties = webrtcProperties;
}
@Async
@Override
public void onApplicationEvent(ClientConfigEvent event) {
final Client client = event.getClient();
final ClientType clientType = client.clientType();
client.push(this.build(clientType));
}
@Override
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
client.push(this.build(clientType));

View File

@@ -25,6 +25,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
"temperature": 温度,
"signal": 信号强度0~100,
"battery": 电池电量0~100,
"alarming": 是否发生告警true|false,
"charging": 是否正在充电true|false,
"recording": 是否正在录像true|false,
"status": {更多状态},
@@ -43,9 +44,7 @@ public class ClientHeartbeatProtocol extends ProtocolClientAdapter {
@Override
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
// 响应心跳
client.push(message.cloneWithoutBody());
// 设置状态
final ClientStatus status = client.status();
status.copy(body);
}

View File

@@ -20,6 +20,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
*/
@Protocol
@Description(
memo = "没有选择终端类型时返回所有类型终端状态列表",
body = """
{
"clientType": "终端类型(可选)"
@@ -36,6 +37,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
"temperature": 温度,
"signal": 信号强度0~100,
"battery": 电池电量0~100,
"alarming": 是否发生告警true|false,
"charging": 是否正在充电true|false,
"recording": 是否正在录像true|false,
"status": {更多状态},

View File

@@ -1,7 +1,14 @@
package com.acgist.taoyao.signal.protocol.client;
import java.util.Map;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.signal.event.ClientOfflineEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -18,7 +25,7 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
""",
flow = "终端-[终端关闭]>信令服务-)终端"
)
public class ClientOfflineProtocol extends ProtocolClientAdapter {
public class ClientOfflineProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientOfflineEvent> {
public static final String SIGNAL = "client::offline";
@@ -26,4 +33,13 @@ public class ClientOfflineProtocol extends ProtocolClientAdapter {
super("终端下线信令", SIGNAL);
}
@Async
@Override
public void onApplicationEvent(ClientOfflineEvent event) {
final String clientId = event.getClientId();
this.clientManager.broadcast(clientId, this.build(
Map.of(Constant.CLIENT_ID, clientId)
));
}
}

View File

@@ -1,7 +1,12 @@
package com.acgist.taoyao.signal.protocol.client;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.event.ClientOnlineEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
/**
@@ -23,20 +28,32 @@ import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
"temperature": 温度,
"signal": 信号强度0~100,
"battery": 电池电量0~100,
"alarming": 是否发生告警true|false,
"charging": 是否正在充电true|false,
"recording": 是否正在录像true|false,
"status": {更多状态},
"config": {更多配置}
}
""",
flow = "终端-[终端注册]>信令服务-)终端"
flow = "终端=[终端注册]>信令服务-)终端"
)
public class ClientOnlineProtocol extends ProtocolClientAdapter {
public class ClientOnlineProtocol extends ProtocolClientAdapter implements ApplicationListener<ClientOnlineEvent> {
public static final String SIGNAL = "client::online";
public ClientOnlineProtocol() {
super("终端上线信令", SIGNAL);
}
@Async
@Override
public void onApplicationEvent(ClientOnlineEvent event) {
final Client client = event.getClient();
final String clientId = event.getClientId();
this.clientManager.broadcast(
clientId,
this.build(client.status())
);
}
}

View File

@@ -8,10 +8,13 @@ import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.model.MessageCodeException;
import com.acgist.taoyao.boot.utils.CloseableUtils;
import com.acgist.taoyao.boot.utils.MapUtils;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientStatus;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.event.ClientConfigEvent;
import com.acgist.taoyao.signal.event.ClientOnlineEvent;
import com.acgist.taoyao.signal.event.MediaClientRegisterEvent;
import com.acgist.taoyao.signal.protocol.ProtocolClientAdapter;
import com.acgist.taoyao.signal.service.SecurityService;
@@ -20,7 +23,6 @@ import lombok.extern.slf4j.Slf4j;
/**
* 终端注册信令
* 如果需要验证终端授权自行实现
*
* @author acgist
*/
@@ -40,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
"temperature": 温度,
"signal": 信号强度0~100,
"battery": 电池电量0~100,
"alarming": 是否发生告警true|false,
"charging": 是否正在充电true|false,
"recording": 是否正在录像true|false,
"status": {更多状态},
@@ -47,8 +50,9 @@ import lombok.extern.slf4j.Slf4j;
}
""",
flow = {
"终端->信令服务->终端",
"终端->信令服务-[终端上线])终端"
"终端=>信令服务->终端",
"终端=>信令服务-[终端配置]>终端",
"终端=>信令服务-[终端上线])终端"
}
)
public class ClientRegisterProtocol extends ProtocolClientAdapter {
@@ -56,14 +60,10 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
public static final String SIGNAL = "client::register";
private final SecurityService securityService;
private final ClientConfigProtocol clientConfigProtocol;
private final ClientOnlineProtocol clientOnlineProtocol;
public ClientRegisterProtocol(SecurityService securityService, ClientConfigProtocol clientConfigProtocol, ClientOnlineProtocol clientOnlineProtocol) {
public ClientRegisterProtocol(SecurityService securityService) {
super("终端注册信令", SIGNAL);
this.securityService = securityService;
this.clientConfigProtocol = clientConfigProtocol;
this.clientOnlineProtocol = clientOnlineProtocol;
}
@Override
@@ -71,12 +71,11 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
final String clientId = MapUtils.get(body, Constant.CLIENT_ID);
final String username = MapUtils.get(body, Constant.USERNAME);
final String password = MapUtils.get(body, Constant.PASSWORD);
// 如果需要终端鉴权在此实现
if(this.securityService.authenticate(username, password)) {
final Client oldClient = this.clientManager.clients(clientId);
if(oldClient != null) {
log.debug("终端已经存在(注销旧的终端):{}", clientId);
oldClient.clientId();
CloseableUtils.close(oldClient);
}
log.info("终端注册:{}", clientId);
client.authorize(clientId);
@@ -85,20 +84,17 @@ public class ClientRegisterProtocol extends ProtocolClientAdapter {
throw MessageCodeException.of(MessageCode.CODE_3401, "注册失败");
}
final ClientType clientType = ClientType.of(MapUtils.get(body, Constant.CLIENT_TYPE));
// 推送消息
final Message registerResponse = message.cloneWithoutBody();
registerResponse.setBody(Map.of(Constant.INDEX, this.idService.buildClientIndex()));
client.push(registerResponse);
// 下发配置
client.push(this.clientConfigProtocol.build(clientType));
// 终端状态
final ClientStatus status = this.buildStatus(clientId, clientType, client, body);
// 上线事件
this.clientManager.broadcast(
clientId,
this.clientOnlineProtocol.build(status)
);
// 媒体服务终端注册
// 注册响应消息
final Message response = message.cloneWithoutBody();
response.setBody(Map.of(Constant.INDEX, this.idService.buildClientIndex()));
client.push(response);
// 设置终端状态
this.buildStatus(clientId, clientType, client, body);
// 终端配置事件
this.publishEvent(new ClientConfigEvent(client));
// 终端上线事件
this.publishEvent(new ClientOnlineEvent(client));
// 媒体服务注册事件
if(clientType == ClientType.MEDIA) {
this.publishEvent(new MediaClientRegisterEvent(client));
}

View File

@@ -16,11 +16,11 @@ import com.acgist.taoyao.signal.protocol.ProtocolControlAdapter;
*/
@Protocol
@Description(
memo = "状态通过心跳回传",
flow = {
"信令服务->终端",
"终端->信令服务->终端"
},
memo = "状态通过心跳回传"
}
)
public class ControlRecordProtocol extends ProtocolControlAdapter {

View File

@@ -0,0 +1,40 @@
package com.acgist.taoyao.signal.protocol.room;
import java.util.Map;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.party.media.Room;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
/**
* 房间广播信令
*
* @author acgist
*/
@Protocol
@Description(
body = """
{
...
}
""",
flow = "终端->信令服务->终端"
)
public class RoomBroadcastProtocol extends ProtocolRoomAdapter {
public static final String SIGNAL = "room::broadcast";
protected RoomBroadcastProtocol() {
super("房间广播信令", SIGNAL);
}
@Override
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
room.broadcast(client, message);
}
}

View File

@@ -31,6 +31,7 @@ public class RoomCloseProtocol extends ProtocolRoomAdapter {
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
room.close();
this.clientManager.broadcast(message);
// TODO释放房间
}
}

View File

@@ -1,10 +1,66 @@
package com.acgist.taoyao.signal.protocol.room;
import java.util.Map;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import com.acgist.taoyao.boot.annotation.Description;
import com.acgist.taoyao.boot.annotation.Protocol;
import com.acgist.taoyao.boot.config.Constant;
import com.acgist.taoyao.boot.model.Message;
import com.acgist.taoyao.signal.client.Client;
import com.acgist.taoyao.signal.client.ClientType;
import com.acgist.taoyao.signal.event.RoomLeaveEvent;
import com.acgist.taoyao.signal.party.media.Room;
import com.acgist.taoyao.signal.protocol.ProtocolRoomAdapter;
/**
* 离开房间信令
*
* @author acgist
*/
public class RoomLeaveProtocol {
@Protocol
@Description(
body = """
{
"clientId": "离开终端ID"
}
""",
flow = {
"终端->信令服务-)终端",
"终端-[关闭终端]>信令服务-)终端",
}
)
public class RoomLeaveProtocol extends ProtocolRoomAdapter implements ApplicationListener<RoomLeaveEvent> {
public static final String SIGNAL = "room::leave";
public RoomLeaveProtocol() {
super("离开房间信令", SIGNAL);
}
@Async
@Override
public void onApplicationEvent(RoomLeaveEvent event) {
this.leave(event.getRoom(), event.getClient());
}
@Override
public void execute(String clientId, ClientType clientType, Room room, Client client, Client mediaClient, Message message, Map<String, Object> body) {
// 离开房间后会发布事件
room.leave(client);
}
/**
* 离开房间
*
* @param room 房间
* @param client 终端
*/
private void leave(Room room, Client client) {
final Message leaveMessage = this.build(Map.of(Constant.CLIENT_ID, client.clientId()));
room.broadcast(client, leaveMessage);
}
}