Flutter网络通信插件whimstech_socket_dart的使用

Flutter网络通信插件whimstech_socket_dart的使用

版本信息

whimstech-socket-dart Socket.io Server
v0.9.* ~ v1.* v2.*
v2.* v3.* ~ v4.6.*
v3.* v4.7.* ~ v4.*

使用方法

Dart服务器
import 'package:socket_io/socket_io.dart';

void main() {
  // Dart服务器
  var io = Server();
  var nsp = io.of('/some');
  nsp.on('connection', (client) {
    print('连接 /some');
    client.on('msg', (data) {
      print('来自 /some 的数据 => $data');
      client.emit('fromServer', "ok 2");
    });
  });
  io.on('connection', (client) {
    print('默认命名空间的连接');
    client.on('msg', (data) {
      print('来自默认的 数据 => $data');
      client.emit('fromServer', "ok");
    });
  });
  io.listen(3000);
}
Dart客户端
import 'package:socket_io_client/socket_io_client.dart' as IO;

void main() {
  // Dart客户端
  IO.Socket socket = IO.io('http://localhost:3000');
  socket.onConnect((_) {
    print('连接成功');
    socket.emit('msg', 'test');
  });
  socket.on('event', (data) => print(data));
  socket.onDisconnect((_) => print('断开连接'));
  socket.on('fromServer', (_) => print(_));
}
手动连接

为了手动连接套接字,设置选项 autoConnect: false 并调用 .connect()

例如:

Socket socket = IO.io('http://localhost:3000',
    OptionBuilder()
      .setTransports(['websocket']) // 为 Flutter 或 Dart VM
      .disableAutoConnect()  // 禁用自动连接
      .setExtraHeaders({'foo': 'bar'}) // 可选
      .build()
  );
socket.connect();

请注意,如果 autoConnect: true(默认情况下启用),则不应调用 .connect(),否则会导致所有事件处理程序被注册/触发两次。参见Issue #33

更新额外的头部
Socket socket = ... // 创建套接字。
socket.io.options['extraHeaders'] = {'foo': 'bar'}; // 更新额外的头部。
socket.io..disconnect()..connect(); // 手动重新连接套接字。
带确认的发送
Socket socket = ... // 创建套接字。
socket.onConnect((_) {
    print('连接成功');
    socket.emitWithAck('msg', 'init', ack: (data) {
        print('确认 $data') ;
        if (data != null) {
          print('来自服务器 $data');
        } else {
          print("空");
        }
    });
});
套接字连接事件

这些事件可以监听。

const List EVENTS = [
  'connect',
  'connect_error',
  'disconnect',
  'error',
  'reconnect',
  'reconnect_attempt',
  'reconnect_failed',
  'reconnect_error',
  'ping',
  'pong'
];

// 替换 'onConnect' 为上述任意一个事件。
socket.onConnect((_) {
    print('连接成功');
});
确认已收到套接字服务器的事件
socket.on('eventName', (data) {
    final dataList = data as List;
    final ack = dataList.last as Function;
    ack(null);
});

在Flutter中的使用

在Flutter环境中(非Flutter Web环境),它仅支持 dart:io WebSocket, 而不是 dart:html WebSocket 或 Ajax (XHR),因此在这种情况下 创建套接字实例时需要添加 setTransports(['websocket'])

例如:

IO.Socket socket = IO.io('http://localhost:3000',
  OptionBuilder()
      .setTransports(['websocket']) // 为 Flutter 或 Dart VM
      .setExtraHeaders({'foo': 'bar'}) // 可选
      .build());

在Flutter中使用流和StreamBuilder

import 'dart:async';

// 步骤1: 流设置
class StreamSocket {
  final _socketResponse = StreamController<String>();

  void Function(String) get addResponse => _socketResponse.sink.add;

  Stream<String> get getResponse => _socketResponse.stream;

  void dispose() {
    _socketResponse.close();
  }
}

StreamSocket streamSocket = StreamSocket();

// 步骤2: 在 main.dart 文件的主函数中添加此函数,并将传入的数据添加到流中
void connectAndListen() {
  IO.Socket socket = IO.io('http://localhost:3000',
      OptionBuilder()
       .setTransports(['websocket']).build());

    socket.onConnect((_) {
     print('连接成功');
     socket.emit('msg', 'test');
    });

    // 当从服务器接收到事件时,数据会被添加到流中
    socket.on('event', (data) => streamSocket.addResponse);
    socket.onDisconnect((_) => print('断开连接'));
}

// 步骤3: 使用StreamBuilder构建小部件

class BuildWithSocketStream extends StatelessWidget {
  const BuildWithSocketStream({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: StreamBuilder(
        stream: streamSocket.getResponse,
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          return Container(
            child: snapshot.data,
          );
        },
      ),
    );
  }
}

处理套接字缓存和后续查找的重要注意事项

查找协议

我们基于相同的方案/端口/主机重用现有实例。

重要的是要明确,dispose()destroy() 不会从已知连接的缓存中删除主机。如果不正确地处理,这可能会导致后续连接忽略某些配置部分。必须通过在第一次创建实例时使用OptionBuilder中的可用选项来处理这种情况。

示例

以下示例中,为该连接设置了用户ID和用户名作为额外的头部。 如果你尝试以更新后的值创建相同的连接,如果主机相同,则不会更新额外的头部,而返回旧的连接。

_socket = IO.io(
        host,
        IO.OptionBuilder()
            .setTransports(['websocket'])
            .setExtraHeaders({'id': userId, 'name': username})
            .disableAutoConnect()
            .enableReconnection()
            .build());

为了避免这种情况,根据你的应用程序需求,你可以使用 enableForceNew()disableMultiplex() 到选项构建器。这些选项将修改使用相同主机的所有连接,所以请务必注意并计划好。

另一种选择,如果你的应用场景适合,可以在上面提到的更新额外的头部中遵循使用方式。 通过在构建选项之外更新额外的头部,你可以保证你的连接将具有你期望的值。

故障排除

无法连接HTTPS服务器或自签名证书服务器
  • 参考此处问题。 解决方法是使用由@lehno提供的以下代码:
class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

void main() {
  HttpOverrides.global = MyHttpOverrides();
  runApp(MyApp());
}
iOS关闭套接字时出现内存泄漏问题
  • 参考此处问题。 请使用 socket.dispose() 而不是 socket.close()socket.disconnect() 来解决iOS上的内存泄漏问题。
macOS上连接错误:SocketException: 连接失败

在文件 <*.entitlements> 中的目录 <macos/Runner/> 下添加以下键:

<key>com.apple.security.network.client</key>
<true/>

更多关于Flutter网络通信插件whimstech_socket_dart的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络通信插件whimstech_socket_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


whimstech_socket_dart 是一个用于 Flutter 的 Dart 插件,提供了基于 WebSocket 的网络通信功能。它允许你在 Flutter 应用中轻松地建立 WebSocket 连接,并发送和接收消息。

安装

首先,你需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  whimstech_socket_dart: ^1.0.0

然后运行 flutter pub get 来安装依赖。

基本用法

  1. 导入包
import 'package:whimstech_socket_dart/whimstech_socket_dart.dart';
  1. 创建 WebSocket 连接
final socket = WebSocket('wss://your.websocket.url');
  1. 监听连接状态
socket.onOpen.listen((event) {
  print('WebSocket connection opened');
});

socket.onClose.listen((event) {
  print('WebSocket connection closed');
});

socket.onError.listen((error) {
  print('WebSocket error: $error');
});
  1. 发送消息
socket.send('Hello, WebSocket!');
  1. 接收消息
socket.onMessage.listen((message) {
  print('Received message: $message');
});
  1. 关闭连接
socket.close();

完整示例

以下是一个完整的示例,展示了如何使用 whimstech_socket_dart 进行基本的 WebSocket 通信:

import 'package:flutter/material.dart';
import 'package:whimstech_socket_dart/whimstech_socket_dart.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WebSocketExample(),
    );
  }
}

class WebSocketExample extends StatefulWidget {
  [@override](/user/override)
  _WebSocketExampleState createState() => _WebSocketExampleState();
}

class _WebSocketExampleState extends State<WebSocketExample> {
  late WebSocket socket;
  final TextEditingController _controller = TextEditingController();
  List<String> messages = [];

  [@override](/user/override)
  void initState() {
    super.initState();
    _initWebSocket();
  }

  void _initWebSocket() {
    socket = WebSocket('wss://your.websocket.url');

    socket.onOpen.listen((event) {
      print('WebSocket connection opened');
    });

    socket.onClose.listen((event) {
      print('WebSocket connection closed');
    });

    socket.onError.listen((error) {
      print('WebSocket error: $error');
    });

    socket.onMessage.listen((message) {
      setState(() {
        messages.add('Received: $message');
      });
    });
  }

  void _sendMessage() {
    if (_controller.text.isNotEmpty) {
      socket.send(_controller.text);
      setState(() {
        messages.add('Sent: ${_controller.text}');
      });
      _controller.clear();
    }
  }

  [@override](/user/override)
  void dispose() {
    socket.close();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebSocket Example'),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: messages.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(messages[index]),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(
                      hintText: 'Enter a message',
                    ),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.send),
                  onPressed: _sendMessage,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
回到顶部