1
This commit is contained in:
103
lib/request/websocket/room_protocol.dart
Normal file
103
lib/request/websocket/room_protocol.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
enum RoomCommand {
|
||||
///ping服务器,用于心跳
|
||||
ping("ping"),
|
||||
|
||||
///加入房间
|
||||
joinRoom("into_room"),
|
||||
|
||||
///获取房间信息(没啥用)
|
||||
getRoomInfo("room_data"),
|
||||
|
||||
///学生开关扬声器、摄像头、麦克风
|
||||
switchCamera("mute_self"),
|
||||
|
||||
///学生上传文件
|
||||
uploadFile("upload_file"),
|
||||
|
||||
///学生举手
|
||||
handUp("handup"),
|
||||
|
||||
///老师开启自习室
|
||||
openRoom("start_study_room"),
|
||||
|
||||
///老师关闭自习室
|
||||
closeRoom("close_study_room"),
|
||||
|
||||
///老师开关学生的扬声器、关闭摄像头、关闭麦克风
|
||||
switchStudentCamera("mute_user"),
|
||||
|
||||
///老师清除学生的举手
|
||||
clearHandUp("clear_handup"),
|
||||
|
||||
///邀请学生进入白板
|
||||
inviteStudent("invite_whiteboard");
|
||||
|
||||
final String value;
|
||||
|
||||
const RoomCommand(this.value);
|
||||
}
|
||||
|
||||
enum RoomEvent {
|
||||
///人员变化事件
|
||||
changeUser("sys_room_user_changed"),
|
||||
|
||||
///学生端开启扬声器
|
||||
openSpeaker("user_unmute_self_speeker"),
|
||||
|
||||
///学生端关闭扬声器
|
||||
closeSpeaker("user_mute_self_speeker"),
|
||||
|
||||
///学生开启麦克风
|
||||
openMic("user_unmute_self_microphone"),
|
||||
|
||||
///学生关闭麦克风
|
||||
closeMic("user_mute_self_microphone"),
|
||||
|
||||
///学生开启摄像头
|
||||
openCamera("user_unmute_self_camera"),
|
||||
|
||||
///学生关闭摄像头
|
||||
closeCamera("user_mute_self_camera"),
|
||||
|
||||
///学生文件上传完毕
|
||||
fileUploadComplete("sys_user_file_uploaded"),
|
||||
|
||||
///学生举手事件
|
||||
handUp("sys_user_handup"),
|
||||
|
||||
///自习室以开启,进入自习室(学生用)
|
||||
openRoom("sys_start_study_room"),
|
||||
|
||||
///自习室以关闭,退出自习室(学生用)
|
||||
closeRoom("sys_close_study_room"),
|
||||
|
||||
///老师关闭学生的扬声器
|
||||
closeStudentSpeaker("sys_control_mute_speeker"),
|
||||
|
||||
///老师打开学生的扬声器
|
||||
openStudentSpeaker("sys_control_unmute_speeker"),
|
||||
|
||||
///老师关闭学生的麦克风
|
||||
closeStudentMic("sys_control_mute_microphone"),
|
||||
|
||||
///老师关闭学生的摄像头
|
||||
closeStudentCamera("sys_control_mute_camera"),
|
||||
|
||||
///老师清除学生的举手(学生用)
|
||||
clearHandUp("sys_clear_handup"),
|
||||
|
||||
///学生收到白板邀请(学生用)
|
||||
inviteWhiteboard("sys_invite_whiteboard");
|
||||
|
||||
final String value;
|
||||
|
||||
const RoomEvent(this.value);
|
||||
|
||||
/// 根据 值获取枚举
|
||||
static RoomEvent fromStr(String value) {
|
||||
return RoomEvent.values.firstWhere(
|
||||
(e) => e.value == value,
|
||||
orElse: () => throw ArgumentError('Invalid weather type value: $value'),
|
||||
);
|
||||
}
|
||||
}
|
||||
122
lib/request/websocket/room_websocket.dart
Normal file
122
lib/request/websocket/room_websocket.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:app/config/config.dart';
|
||||
import 'package:app/request/api/room_api.dart';
|
||||
import 'package:app/request/websocket/room_protocol.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
import '../dto/room/rtc_token_dto.dart';
|
||||
|
||||
Logger logger = Logger();
|
||||
|
||||
class RoomWebSocket {
|
||||
///单例设计模式
|
||||
RoomWebSocket._();
|
||||
|
||||
static final RoomWebSocket _instance = RoomWebSocket._();
|
||||
|
||||
factory RoomWebSocket() => _instance;
|
||||
|
||||
/// WebSocket和心跳定时器
|
||||
String url = "";
|
||||
WebSocket? _socket;
|
||||
Timer? _heartbeatTimer;
|
||||
Timer? _reconnectTimer; //错误重连的定时器
|
||||
|
||||
///令牌
|
||||
String wsToken = ""; //自习室的websocket令牌
|
||||
int roomId = 0; //房间号
|
||||
RtcTokenDto? rtcToken; // rtc的令牌
|
||||
|
||||
///用 StreamController 分化消息给订阅者
|
||||
final StreamController<RoomMessage> _msgController = StreamController.broadcast();
|
||||
|
||||
Stream<RoomMessage> get stream => _msgController.stream;
|
||||
|
||||
///初始化令牌
|
||||
/// -[id] 房间id
|
||||
Future<void> initToken(int id) async {
|
||||
roomId = id;
|
||||
final rtcFuture = getRtcTokenApi(id);
|
||||
final wsFuture = getWsTokenApi(id);
|
||||
|
||||
rtcToken = await rtcFuture;
|
||||
wsToken = await wsFuture;
|
||||
}
|
||||
|
||||
///开始连接
|
||||
Future<void> connect() async {
|
||||
try {
|
||||
_socket = await WebSocket.connect(
|
||||
"${Config.wsUrl()}?token=$wsToken&study_room_id=$roomId",
|
||||
);
|
||||
logger.i("连接成功");
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = null;
|
||||
|
||||
//监听消息
|
||||
_socket!.listen(
|
||||
(data) {
|
||||
//监听事件
|
||||
final jsonMap = jsonDecode(data);
|
||||
RoomMessage msg = RoomMessage(RoomEvent.fromStr(jsonMap['action']), jsonMap['data']);
|
||||
_msgController.add(msg);
|
||||
},
|
||||
onDone: () {},
|
||||
onError: (_) {
|
||||
logger.e("连接异常断开");
|
||||
},
|
||||
);
|
||||
//心跳
|
||||
_heartbeatTimer?.cancel();
|
||||
_heartbeatTimer = Timer.periodic(Duration(seconds: 15), (_) {
|
||||
logger.i("发送心跳");
|
||||
send(RoomCommand.ping);
|
||||
});
|
||||
} catch (e) {
|
||||
logger.e("连接失败");
|
||||
_reconnect();
|
||||
}
|
||||
}
|
||||
|
||||
///发送指令
|
||||
void send(RoomCommand action, [Map<String, dynamic>? params]) {
|
||||
final msg = {
|
||||
"action": action.value,
|
||||
"data": params,
|
||||
};
|
||||
_socket!.add(jsonEncode(msg));
|
||||
}
|
||||
|
||||
///连接错误事件
|
||||
void _reconnect() {
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = Timer.periodic(Duration(seconds: 3), (timer) {
|
||||
logger.e("正在重连");
|
||||
connect();
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
//心跳取消
|
||||
_heartbeatTimer?.cancel();
|
||||
_heartbeatTimer = null;
|
||||
//socket取消
|
||||
_socket?.close();
|
||||
// 销毁事件流
|
||||
_msgController.close();
|
||||
// 错误重连取消
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = null;
|
||||
logger.i("websocket销毁成功");
|
||||
}
|
||||
}
|
||||
|
||||
///websocket服务器发过来的事件和数据
|
||||
class RoomMessage {
|
||||
final RoomEvent event; //事件名称
|
||||
final dynamic data; //事件数据
|
||||
|
||||
RoomMessage(this.event, this.data);
|
||||
}
|
||||
Reference in New Issue
Block a user