Flutter 交易所

815 阅读2分钟

Flutter 交易所

交易所的项目核心还是socket的数据推送,所以重点来说说socket的东西吧.

  1. 最开始的设计是客户端和服务端之间建立一次连接,然后通过发送不同的方法来获取不同的数据.客户端这边就每秒钟调用发送一次socket方法.不过这里有个问题是客户短用户一旦过多,服务端就会出现消息排队的问题导致数据延迟.
  2. 所以优化了一下方案是客户端和服务端建立一次连接.然后客户端发送一次消息.服务端保存这个消息.然后开始主动推送给这条消息数据.如果不需要发送消息了.发送一个remove的方法让服务端停止发送数据.

代码实现

  1. 用单例来统一处理socket连接,消息体的封装, 消息数据返回回调,以及断线重连等等.
  2. 消息数据返回回调是用这个创建了一个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(() {});
});