Flutter SignalR客户端通信插件signalr_client_core的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter SignalR客户端通信插件signalr_client_core的使用

signalr_client

pub package

一个用于 ASP.NET Core 的 Flutter SignalR 客户端。

ASP.NET Core SignalR 是一个开源库,它简化了向应用程序添加实时 Web 功能的过程。实时 Web 功能使服务器端代码能够即时将内容推送到客户端。

测试环境:ASP.NET Core 3.1 和 ASP.NET Core 6

该客户端能够调用服务器端的 Hub 函数(包括流式函数)并接收由服务器发出的方法调用。它还支持自动重新连接功能。

该客户端支持以下传输协议:

  • WebSocket
  • 服务端事件
  • 长轮询

该客户端支持以下 Hub 协议:

  • Json
  • MessagePack

示例

开始使用

pubspec.yaml 中添加 signalr_netcore 依赖:

dependencies:
  flutter:
    sdk: flutter

  signalr_netcore:

重要提示:如果你遇到问题(例如未接收到消息回调),请尝试使用旧版本,如 signalr_netcore: 0.1.7+2-nullsafety.3,但请注意,此版本不支持自动重新连接功能,需要手动处理。

使用方法

让我们演示一些基本用法:

1. 创建一个 Hub 连接

// 导入库。
import 'package:signalr_netcore/signalr_client.dart';

// SignalR 服务器的位置。
final serverUrl = "192.168.10.50:51001";
// 使用 HubConnectionBuilder 创建连接。
final hubConnection = HubConnectionBuilder().withUrl(serverUrl).build();
// 当连接关闭时,在控制台打印一条消息。
hubConnection.onclose((error) => print("Connection Closed"));

日志记录通过 dart 的 logging 包 支持:

// 导入库。
import 'package:logging/logger.dart';
import 'package:signalr_netcore/signalr_client.dart';

// 配置日志记录。
Logger.root.level = Level.ALL;
// 将日志消息写入控制台。
Logger.root.onRecord.listen((LogRecord rec) {
  print('${rec.level.name}: ${rec.time}: ${rec.message}');
});

// 如果你只想记录更高级别的 Hub 协议消息:
final hubProtLogger = Logger("SignalR - hub");
// 如果你还想记录传输消息:
final transportProtLogger = Logger("SignalR - transport");

// SignalR 服务器的位置。
final serverUrl = "192.168.10.50:51001";
final httpOptions = HttpConnectionOptions(logger: transportProtLogger);
//final httpOptions = new HttpConnectionOptions(logger: transportProtLogger, transport: HttpTransportType.WebSockets); // 默认传输类型。
//final httpOptions = new HttpConnectionOptions(logger: transportProtLogger, transport: HttpTransportType.ServerSentEvents);
//final httpOptions = new HttpConnectionOptions(logger: transportProtLogger, transport: HttpTransportType.LongPolling);

// 如果需要对 Hub 连接进行授权,则提供一个返回令牌字符串的异步回调函数(见 AccessTokenFactory 类型定义)并将其分配给 accessTokenFactory 参数:
// final httpOptions = new HttpConnectionOptions( .... accessTokenFactory: () async => await getAccessToken() );

// 使用 HubConnectionBuilder 创建连接。
final hubConnection = HubConnectionBuilder().withUrl(serverUrl, options: httpOptions).configureLogging(hubProtLogger).build();
// 当连接关闭时,在控制台打印一条消息。
hubConnection.onclose((error) => print("Connection Closed"));

2. 连接到一个 Hub

调用以下方法开始握手并连接到 SignalR 服务器:

await hubConnection.start();

3. 调用一个 Hub 函数

假设有一个这样的 Hub 函数:

public string MethodOneSimpleParameterSimpleReturnValue(string p1)
{
  Console.WriteLine($"'MethodOneSimpleParameterSimpleReturnValue' invoked. Parameter value: '{p1}");
  return p1;
}

客户端可以通过以下方式调用该函数:

final result = await hubConnection.invoke("MethodOneSimpleParameterSimpleReturnValue", args: <Object>["ParameterValue"]);
logger.log(LogLevel.Information, "Result: '$result'");

4. 调用一个客户端函数

假设服务器调用了一个名为 “aClientProvidedFunction” 的函数:

await Clients.Caller.SendAsync("aClientProvidedFunction", null);

客户端可以这样提供该函数:

hubConnection.on("aClientProvidedFunction", _handleAClientProvidedFunction);

// 若要注销该函数,可以使用:
// a) 注销特定实现:
// hubConnection.off("aClientProvidedFunction", method: _handleServerInvokeMethodNoParametersNoReturnValue);
// b) 注销所有实现:
// hubConnection.off("aClientProvidedFunction");

...

void _handleAClientProvidedFunction(List<Object> parameters) {
  logger.log(LogLevel.Information, "Server invoked the method");
}

5. 使用 MessagePack 进行序列化

Hub 应在客户端和服务器上配置为使用 MessagePack 协议

客户端
import 'package:signalr_netcore/msgpack_hub_protocol.dart';
_hubConnection = HubConnectionBuilder()
          .withUrl(_serverUrl, options: httpOptions)
          /* 配置 Hub 使用 MessagePack 协议 */
          .withHubProtocol(MessagePackHubProtocol())
          .withAutomaticReconnect()
          .configureLogging(logger)
          .build();
服务器

在你的 ASP.NET Core 项目中添加以下包

Microsoft.AspNetCore.SignalR.Protocols.MessagePack
public void ConfigureServices(IServiceCollection services)
{
    // 配置 Hub 使用 MessagePack 协议
    services.AddSignalR().AddMessagePackProtocol();
}

关于参数类型的注意事项

所有函数参数和返回值都使用 dart:convert 包(json.encode/json.decode)进行序列化/反序列化。确保你:

  • 只使用简单的参数类型
  • 或者
  • 使用实现了 toJson() 方法的对象,因为该方法被 dart:convert 包用来序列化对象。

Flutter JSON 101:

MSGPACK

所有函数参数和返回值都使用 msgpack_dart 包进行序列化/反序列化。确保你:

  • 只使用简单的参数类型
  • 或者
  • 使用 Json encode/decode 将类转换为 map 然后传递给 MessagePack
  • 或者
  • 使用自定义编码器和解码器将消息序列化为字节然后传递给 SignalR

MSGPACK - 它像 JSON 一样,但更快更小

Msgpack - 它像 JSON 一样,但更快更小。

如何暴露一个 MessageHeaders 对象以便客户端可以发送默认头

代码示例:

final defaultHeaders = MessageHeaders();
defaultHeaders.setHeaderValue("HEADER_MOCK_1", "HEADER_VALUE_1");
defaultHeaders.setHeaderValue("HEADER_MOCK_2", "HEADER_VALUE_2");

final httpConnectionOptions = new HttpConnectionOptions(
          httpClient: WebSupportingHttpClient(logger,
              httpClientCreateCallback: _httpClientCreateCallback),
          accessTokenFactory: () => Future.value('JWT_TOKEN'),
          logger: logger,
          logMessageContent: true,
          headers: defaultHeaders);

final _hubConnection = HubConnectionBuilder()
          .withUrl(_serverUrl, options: httpConnectionOptions)
          .withAutomaticReconnect(retryDelays: [2000, 5000, 10000, 20000, null])
          .configureLogging(logger)
          .build();

HTTP 请求日志:

I/flutter ( 5248): Starting connection with transfer format 'TransferFormat.Text'.
I/flutter ( 5248): Sending negotiation request: https://localhost:5000/negotiate?negotiateVersion=1
I/flutter ( 5248): HTTP send: url 'https://localhost:5000/negotiate?negotiateVersion=1', method: 'POST' content: '' content length = '0' 
headers: '{ content-type: text/plain;charset=UTF-8 }, { HEADER_MOCK_1: HEADER_VALUE_1 }, { X-Requested-With: FlutterHttpClient }, { HEADER_MOCK_2: HEADER_VALUE_2 }, { Authorization: Bearer JWT_TOKEN }'

示例代码

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

const kChatServerUrl = "http://192.168.0.4:5000";

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

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

1 回复

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


当然,以下是一个关于如何使用Flutter中的signalr_client_core插件与SignalR服务器进行通信的代码示例。这个示例将展示如何连接到SignalR服务器、订阅服务器发送的消息以及发送消息到服务器。

首先,确保你的Flutter项目中已经添加了signalr_client_core依赖。在你的pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  signalr_client_core: ^最新版本号  # 替换为实际的最新版本号

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

接下来,是一个简单的Flutter应用示例,它展示了如何使用signalr_client_core插件:

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  HubConnection? _hubConnection;
  TextEditingController _messageController = TextEditingController();
  List<String> _messages = [];

  @override
  void initState() {
    super.initState();
    initSignalR();
  }

  void initSignalR() async {
    var options = HttpHubConnectionBuilderOptions()
      ..withUrl("https://你的signalr服务器地址/yourHubEndpoint");

    _hubConnection = await HubConnectionBuilder()
      .withUrl("https://你的signalr服务器地址/yourHubEndpoint", options)
      .build();

    _hubConnection!.onclose(() {
      print("Connection closed. Trying to reconnect...");
      startConnection();
    });

    await startConnection();

    _hubConnection!.on("ReceiveMessage", (message) {
      setState(() {
        _messages.add(message);
      });
    });
  }

  Future<void> startConnection() async {
    try {
      await _hubConnection!.start();
      print("Connected to SignalR server");
    } catch (e) {
      print("Error connecting to SignalR server: $e");
    }
  }

  void sendMessage() async {
    if (_hubConnection!.state == ConnectionState.connected) {
      await _hubConnection!.invoke("SendMessage", _messageController.text);
      _messageController.clear();
    } else {
      print("Not connected to SignalR server");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter SignalR Client'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            children: [
              Expanded(
                child: ListView.builder(
                  itemCount: _messages.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text(_messages[index]),
                    );
                  },
                ),
              ),
              TextField(
                controller: _messageController,
                decoration: InputDecoration(
                  labelText: 'Send Message',
                  border: OutlineInputBorder(),
                ),
                onSubmitted: (value) {
                  sendMessage();
                },
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: sendMessage,
          tooltip: 'Send message',
          child: Icon(Icons.send),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _hubConnection?.stop();
    _messageController.dispose();
    super.dispose();
  }
}

代码说明:

  1. 依赖添加:在pubspec.yaml文件中添加signalr_client_core依赖。
  2. 状态管理:使用StatefulWidget来管理SignalR连接的状态和消息列表。
  3. 初始化SignalR连接:在initState方法中初始化SignalR连接,并设置重连逻辑。
  4. 处理消息:使用_hubConnection!.on方法来订阅服务器发送的消息,并更新UI。
  5. 发送消息:定义一个sendMessage方法来发送消息到服务器。
  6. UI布局:使用ListView显示接收到的消息,并使用TextFieldFloatingActionButton来发送消息。

请注意,你需要将"https://你的signalr服务器地址/yourHubEndpoint"替换为你实际的SignalR服务器地址和Hub端点。此外,确保你的SignalR服务器已经正确配置并运行。

这个示例提供了一个基本的框架,你可以根据需要进行扩展和修改。

回到顶部