Flutter 交易所
交易所的项目核心还是socket的数据推送,所以重点来说说socket的东西吧.
- 最开始的设计是客户端和服务端之间建立一次连接,然后通过发送不同的方法来获取不同的数据.客户端这边就每秒钟调用发送一次socket方法.不过这里有个问题是客户短用户一旦过多,服务端就会出现消息排队的问题导致数据延迟.
- 所以优化了一下方案是客户端和服务端建立一次连接.然后客户端发送一次消息.服务端保存这个消息.然后开始主动推送给这条消息数据.如果不需要发送消息了.发送一个remove的方法让服务端停止发送数据.
代码实现
- 用单例来统一处理socket连接,消息体的封装, 消息数据返回回调,以及断线重连等等.
- 消息数据返回回调是用这个创建了一个map,里面装了方法对应的回调函数.然后socket拿到回调的时候服务端返回消息方法名称,通过返回的消息方法名称找到当前方法对应的回调函数进行回调.
typedef void WebSocketCallback(arg);
const String _Host = "ws://xxxxx/ws";
const String _Host_test = "ws://xxxxx/ws";
class ShareSocket {
// 单例公开访问点
factory ShareSocket() =>_sharedInstance();
// 静态私有成员,没有初始化
static ShareSocket _instance;
static ShareSocket get instance => _sharedInstance();
IOWebSocketChannel _socket;
static Map<String, WebSocketCallback> funcs = {};
// 私有构造函数
ShareSocket._() {
// 具体初始化代码
}
// 静态、同步、私有访问点
static ShareSocket _sharedInstance() {
if (_instance == null) {
_instance = ShareSocket._();
}
_instance._initsocket();
return _instance;
}
/// send Message
static sendMessage(ChannelMessage msg, {Function(dynamic) onFinish}) {
funcs[msg.channel] = onFinish;
if(_instance._socket == null) return;
String res = json.encode(msg.toJson());
_instance._socket.sink.add(res);
}
static Future stop() {
return _instance._socket.sink.close();
}
/// 初始化socket 进行监听
void _initsocket() {
String url = AppConst.APP_IS_RELEASE ? _Host : _Host_test;
_socket = IOWebSocketChannel.connect(url);
_socket.stream.listen(
_instance._onData,
onDone: _instance._onDone,
onError: _instance._onError
);
EventBus.instance.commit(EventKeys.JumpCloseKlineReConnect, null);
EventBus.instance.commit(EventKeys.JumpOpenKlineReConnect, null);
EventBus.instance.commit(EventKeys.HomeSocketReConnect, null);
}
void _onDone(){
print("websocket=> is closed");
_socket.sink.close();
_socket = null;
// socket 断线每两秒重连一次
Future.delayed(const Duration(seconds: 2)).then((value) => _initsocket());
}
void _onError(err){
print("websocket => "+err.runtimeType.toString());
WebSocketChannelException ex = err;
print("websocket => " + ex.message);
}
void _onData(event){
// print("======>$event");
Map temp = json.decode(event);
// 获取消息方法名称回调数据
String channel = temp["channel"];
if(funcs[channel] != null) {
funcs[channel](temp);
}
}
}
/// 消息体
class ChannelMessage {
/// 消息名称
String channel;
String marketId;
String marketName;
String minType;
String userId;
String event;
///type =1 开多开空 type=2 是平多 3平空
String type;
ChannelMessage({@required this.channel, this.marketName, this.event, this.type, this.marketId, this.minType, this.userId});
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['channel'] = this.channel;
if(this.marketId != null) data['marketId'] = this.marketId;
if(this.minType != null) data['minType'] = this.minType;
if(this.userId != null) data['userId'] = this.userId;
if(this.type != null) data['type'] = this.type;
if(this.event != null) data["event"] = this.event;
if(this.marketName != null) data["marketName"] = this.marketName;
// data["time"] = DateTime.now().millisecondsSinceEpoch;
return data;
}
}
使用
String channel = "marketAll";// 获取交易对
markbyall = ChannelMessage(channel: channel);
ShareSocket.sendMessage(markbyall, onFinish: (data) {
final response = MarketAllResponse.fromJson(data);
if (mounted) setState(() {});
});