Flutter WebSocket通信插件pusher_websockets_client的使用
Flutter WebSocket通信插件pusher_websockets_client
的使用
简介
dart_pusher_channels
是一个实现纯 Dart 的 Pusher Channels 客户端库。该库的结构根据 Pusher Channels 协议的官方文档进行构建。
从版本 1.0.0
开始,该库提供了更规范的 Pusher Channels 客户端 API。如果您有使用旧版本的项目,可以在 pubspec.yaml
中设置依赖项:
dart_pusher_channels: 0.3.1+1
主要里程碑
从版本 1.1.0
开始,实现了以下主要功能:
- 存在通道 ✓
- 私有加密通道 ✓
- 触发客户端事件 ✓
- 在所有六个平台上进行了测试 ✓
贡献者
贡献者:
- Sameh Doush - 开发了一个测试环境,促进了私有加密通道特性的发布。
- Nicolas Britos - 贡献了拉取请求 #5, #6, #8.
使用说明
选项
为了通过 Pusher Channels 协议连接到服务器,客户端必须提供一些元数据作为 URL。可以使用 PusherChannelsOptions
的一个构造函数来满足不同的用例需求。
PusherChannelsOptions.fromCluster
如果您的 URL 模式类似于 {scheme}://ws-{cluster_name}.{host}:{port}/app/{key}
,可以使用此构造函数:
const clusterOptions = PusherChannelsOptions.fromCluster(
scheme: 'wss', // 通常为 ws 或 wss
cluster: 'mt1', // 应用程序的集群
key: 'a0173cd5499b34d93109', // 应用程序的密钥
host: 'pusher.com', // 自定义主机名
shouldSupplyMetadataQueries: true, // 是否发送额外的元数据查询参数
metadata: PusherChannelsOptionsMetadata.byDefault(), // 应用自定义元数据
port: 443,
);
print(clusterOptions.uri.toString()); // 输出: wss://ws-mt1.pusher.com:443/app/a0173cd5499b34d93109?client=dart&version=0.8.0&protocol=7
PusherChannelsOptions.fromHost
如果您的 Pusher Channels 安装在自己的域名主机上,且 URL 模式类似于 {scheme}://{host}:{port}/app/{key}
,可以使用此构造函数:
const hostOptions = PusherChannelsOptions.fromHost(
scheme: 'wss',
host: 'my.domain.com',
key: 'my_key',
shouldSupplyMetadataQueries: true,
metadata: PusherChannelsOptionsMetadata.byDefault(),
port: 443,
);
print(hostOptions.uri.toString()); // 输出: wss://my.domain.com:443/app/my_key?client=dart&version=0.8.0&protocol=7
PusherChannelsOptions.custom
如果上述构造函数都不符合您的用例需求,可以使用此构造函数:
final customOptions = PusherChannelsOptions.custom(
uriResolver: (metadata) => Uri.parse('wss://my.custom.domain/my/custom/path'),
);
print(customOptions.uri.toString()); // 输出: wss://my.custom.domain/my/custom/path
客户端
当您已经决定使用哪种选项时,可以将其应用到 PusherChannelsClient
的实例中。
PusherChannelsClient.websocket
使用此构造函数可以构建支持 WebSocket 连接的客户端。该库依赖于 web_socket_channel
来支持此功能:
const testOptions = PusherChannelsOptions.fromCluster(
scheme: 'wss',
cluster: 'mt1',
key: 'a0173cd5499b34d93109',
host: 'pusher.com',
shouldSupplyMetadataQueries: true,
metadata: PusherChannelsOptionsMetadata.byDefault(),
port: 443,
);
final client = PusherChannelsClient.websocket(
options: testOptions,
connectionErrorHandler: (exception, trace, refresh) {
// 处理连接错误
refresh(); // 重新连接客户端
},
minimumReconnectDelayDuration: const Duration(seconds: 1),
defaultActivityDuration: const Duration(seconds: 120),
activityDurationOverride: const Duration(seconds: 120),
waitForPongDuration: const Duration(seconds: 30),
);
自定义连接实现
如前所述,该库支持通过 WebSocket 使用 PusherChannelsClient.websocket
进行连接。您可以实现自己的 PusherChannelsConnection
。可以查看 PusherChannelsWebSocketConnection
类的实现作为示例:
final myClient = PusherChannelsClient.custom(
connectionDelegate: () => MyConnection(),
connectionErrorHandler: ((exception, trace, refresh) {
refresh();
}),
);
公共频道
公共频道应用于公开访问的数据,无需任何形式的授权即可订阅。创建公共频道的方法如下:
client.publicChannel('public-MyChannel');
私有频道
私有频道应用于需要限制访问的频道。为了订阅私有频道,用户必须被授权。授权通过在调用 subscribe
方法时向可配置的授权 URL 发送 HTTP 请求来完成:
final myPrivateChannel = client.privateChannel(
'private-channel',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('https://test.pusher.com/pusher/auth'),
headers: const {},
),
);
私有加密频道
端到端加密频道提供了与私有频道相同的订阅限制,并且在发布到这些频道的事件数据字段在离开服务器之前会使用 NaCl 中定义的 Secretbox 加密标准进行加密。只有经过授权的订阅者才能访问特定频道的解密密钥。
PrivateEncryptedChannel myEncryptedChannel = client.privateEncryptedChannel(
'private-encrypted-channel',
eventDataEncodeDelegate: (bytes) => utf8.decode(bytes),
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate
.forPrivateEncryptedChannel(
authorizationEndpoint: Uri.parse('https://test.pusher.com/pusher/auth'),
headers: const {},
),
);
存在频道
存在频道基于私有频道的安全性,并提供了额外的功能,即了解谁订阅了该频道。这使得构建聊天室和“谁在线”类型的功能变得非常容易。同样需要一个 EndpointAuthorizableChannelAuthorizationDelegate
实例来进行授权:
final myPresenceChannel = client.presenceChannel(
'presence-channel',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPresenceChannel(
authorizationEndpoint: Uri.parse('https://test.pusher.com/pusher/auth'),
headers: const {},
),
);
订阅、取消订阅和连接
以下是连接客户端并订阅频道的示例:
// 组织所有频道以提高可读性
final allChannels = <Channel>[
myPresenceChannel,
myPrivateChannel,
myPublicChannel,
];
// 高度推荐在客户端的 .onConnectionEstablished Stream 发出事件时订阅频道,
// 因为这样可以在客户端因连接错误而重新连接时重新订阅
final StreamSubscription connectionSubs = client.onConnectionEstablished.listen((_) {
for (final channel in allChannels) {
// 如果没有故意取消订阅,则订阅频道
channel.subscribeIfNotUnsubscribed();
}
});
// 使用客户端连接
client.connect();
// 如果不再需要客户端 - 取消连接订阅并销毁它
// 在未来某个时间点
await Future.delayed(const Duration(seconds: 5));
connectionSubs.cancel();
client.dispose();
取消订阅频道
// 如果需要再次订阅,也可以取消订阅
myChannel.unsubscribe();
绑定到事件
与其他 SDK 不同,dart_pusher_channels
提供了通过 Dart 流绑定到事件的功能,因此建议为每个要订阅的事件创建 StreamSubscription
。记住:除非取消订阅或取消 StreamSubscription
,否则这些流将继续接收事件。
// 为了绑定到事件,使用 Channel 实例的 .bind 方法
StreamSubscription<ChannelReadEvent> somePrivateChannelEventSubs = myPrivateChannel.bind('private-MyEvent').listen((event) {
print('Event from the private channel fired!');
});
// 如果想取消绑定 - 只需取消订阅
somePrivateChannelEventSubs.cancel();
使用扩展快捷方式绑定
您可以使用 Channel
实例上的扩展快捷方式方法来绑定到预定义的事件(特别是存在频道):
.whenSubscriptionSucceeded()
绑定到 pusher:subscription_succeeded
.whenSubscriptionCount()
绑定到 pusher:subscription_count
.whenMemberAdded()
绑定到 pusher:member_added
.whenMemberRemoved()
绑定到 pusher:member_removed
.onSubscriptionError({String? errorType})
绑定到 pusher:subscription_error
并通过 errorType
进行过滤
.onAuthenticationSubscriptionFailed()
绑定到 pusher:subscription_error
并通过 errorType
为 AuthError
进行过滤
监听频道的所有事件
StreamSubscription<ChannelReadEvent> allEventsSubs = myChannel.bindToAll().listen((event) {
// 做一些操作
});
监听来自客户端的所有事件
StreamSubscription<PusherChannelsReadEvent> allEventsSubs = client.eventStream.listen((event) {
// 做一些操作
});
触发事件
私有频道和存在频道支持触发客户端事件:
myPresenceChannel.trigger(
eventName: 'client-event',
data: {'hello': 'Hello'},
);
示例代码
import 'dart:async';
import 'package:pusher_websockets_client/dart_pusher_channels.dart';
void connectToPusher() async {
// 启用或禁用日志
PusherChannelsPackageLogger.enableLogs();
// 创建 PusherChannelsOptions 实例
const testOptions = PusherChannelsOptions.fromCluster(
scheme: 'wss',
cluster: 'mt1',
key: 'a0173cd5499b34d93109',
port: 443,
);
// 创建 PusherChannelsClient 实例
final client = PusherChannelsClient.websocket(
options: testOptions,
// 连接异常在这里处理
connectionErrorHandler: (exception, trace, refresh) async {
// 如果发生任何错误,允许重新连接
refresh();
},
);
// 创建 Channel 实例
PresenceChannel myPresenceChannel = client.presenceChannel(
'presence-channel',
// 私有和存在频道需要用户授权
// 使用 EndpointAuthorizableChannelTokenAuthorizationDelegate 通过 HTTP 端点授权
// 或创建您自己的 EndpointAuthorizableChannelAuthorizationDelegate 实现
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate
.forPresenceChannel(
authorizationEndpoint: Uri.parse('https://test.pusher.com/pusher/auth'),
headers: const {},
),
);
PrivateChannel myPrivateChannel = client.privateChannel(
'private-channel',
authorizationDelegate:
EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('https://test.pusher.com/pusher/auth'),
headers: const {},
),
);
PublicChannel myPublicChannel = client.publicChannel(
'public-channel',
);
// 与其他 SDK 不同,dart_pusher_channels 提供了通过 Dart 流绑定到事件的功能
// 因此建议为每个要订阅的事件创建 StreamSubscription
// 记住:除非取消订阅或取消 StreamSubscription,否则这些流将继续接收事件
// 这意味着:如果取消 StreamSubscription 实例 - 事件将不会被接收
// 如果取消频道订阅 - 流不会关闭,但会被阻止接收事件,除非再次订阅频道
// 使用 .bind 方法监听频道事件
StreamSubscription<ChannelReadEvent> somePrivateChannelEventSubs =
myPrivateChannel.bind('private-MyEvent').listen((event) {
print('Event from the private channel fired!');
});
StreamSubscription<ChannelReadEvent> somePublicChannelEventSubs =
myPublicChannel.bind('public-MyEvent').listen((event) {
print('Event from the public channel fired!');
});
// 您可以使用一些有用的扩展快捷方式方法来绑定预定义的频道事件
// 例如,此方法绑定了名为 'pusher:member_added' 的事件
StreamSubscription<ChannelReadEvent> presenceMembersAddedSubs =
myPresenceChannel.whenMemberAdded().listen((event) {
print(
'Member added, now members count is ${myPresenceChannel.state?.members?.membersCount}',
);
});
// 将所有订阅组织到一个列表中以便阅读
final allEventSubs = <StreamSubscription?>[
presenceMembersAddedSubs,
somePrivateChannelEventSubs,
somePublicChannelEventSubs,
];
// 将所有频道组织到一个列表中以便阅读
final allChannels = <Channel>[
myPresenceChannel,
myPrivateChannel,
myPublicChannel,
];
// 高度推荐在客户端的 .onConnectionEstablished Stream 发出事件时订阅频道
// 因为这样可以在客户端因连接错误而重新连接时重新订阅
final StreamSubscription connectionSubs =
client.onConnectionEstablished.listen((_) {
for (final channel in allChannels) {
// 如果没有故意取消订阅,则订阅频道
channel.subscribeIfNotUnsubscribed();
}
});
// 使用客户端连接
unawaited(client.connect());
// 您可以从私有和存在频道触发事件
// 在未来的某个时间点
await Future.delayed(
const Duration(seconds: 5),
);
myPresenceChannel.trigger(
eventName: 'client-event',
data: {'hello': 'Hello'},
);
// 如果不再需要频道 - 取消订阅。频道实例是可重用的,
// 所以如果需要,可以在以后再次订阅
// 在未来的某个时间点
await Future.delayed(const Duration(seconds: 5));
myPresenceChannel.unsubscribe();
// 在未来的某个时间点
await Future.delayed(const Duration(seconds: 5));
myPresenceChannel.subscribe();
// 如果想取消事件绑定 - 只需取消事件订阅
// 在未来的某个时间点
await Future.delayed(const Duration(seconds: 5));
await presenceMembersAddedSubs.cancel();
// 如果不再需要客户端 - 取消连接订阅并销毁它
// 在未来的某个时间点
await Future.delayed(const Duration(seconds: 5));
await connectionSubs.cancel();
// 考虑取消事件订阅
for (final subscription in allEventSubs) {
subscription?.cancel();
}
client.dispose();
}
更多关于Flutter WebSocket通信插件pusher_websockets_client的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter WebSocket通信插件pusher_websockets_client的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个使用 pusher_websockets_client
插件在 Flutter 中实现 WebSocket 通信的示例代码。这个插件允许你与 Pusher Channels 或任何兼容的 WebSocket 服务器进行通信。
首先,确保你已经在 pubspec.yaml
文件中添加了 pusher_websockets_client
依赖:
dependencies:
flutter:
sdk: flutter
pusher_websockets_client: ^x.y.z # 请替换为最新版本号
然后,运行 flutter pub get
来安装依赖。
接下来是一个简单的 Flutter 应用示例,展示了如何使用 pusher_websockets_client
进行 WebSocket 通信:
import 'package:flutter/material.dart';
import 'package:pusher_websockets_client/pusher_websockets_client.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
PusherClient? _pusherClient;
String _channelMessage = "";
@override
void initState() {
super.initState();
// 初始化 Pusher 客户端
_pusherClient = PusherClient(
key: 'YOUR_PUSHER_APP_KEY', // 替换为你的 Pusher 应用密钥
cluster: 'YOUR_PUSHER_CLUSTER', // 替换为你的 Pusher 集群
useTLS: true,
);
// 订阅频道并监听事件
_pusherClient?.subscribe('test_channel')
.bind('my_event', (data) {
// 收到事件时更新状态
setState(() {
_channelMessage = data.message;
});
})
.catchError((error) {
print('Error subscribing to channel: $error');
});
// 连接 Pusher
_pusherClient?.connect()
.then((_) {
print('Connected to Pusher');
})
.catchError((error) {
print('Error connecting to Pusher: $error');
});
}
@override
void dispose() {
// 断开连接并释放资源
_pusherClient?.disconnect();
_pusherClient = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Pusher WebSocket Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Channel Message:',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 8),
Text(
_channelMessage,
style: TextStyle(fontSize: 16),
),
],
),
),
),
);
}
}
在这个示例中:
- 初始化 Pusher 客户端:在
initState
方法中,我们创建了一个PusherClient
实例,并传入 Pusher 应用的密钥和集群信息。 - 订阅频道:使用
_pusherClient?.subscribe('test_channel')
方法订阅了一个名为test_channel
的频道,并绑定了一个名为my_event
的事件。当该事件发生时,会更新_channelMessage
状态。 - 连接 Pusher:调用
_pusherClient?.connect()
方法连接到 Pusher 服务器。 - UI 显示:在
build
方法中,我们构建了一个简单的 UI,用于显示从频道接收到的消息。 - 资源释放:在
dispose
方法中,我们断开了与 Pusher 的连接并释放了资源。
请确保将 YOUR_PUSHER_APP_KEY
和 YOUR_PUSHER_CLUSTER
替换为你自己的 Pusher 应用密钥和集群信息。
这个示例展示了如何使用 pusher_websockets_client
插件进行基本的 WebSocket 通信。根据实际需求,你可以扩展这个示例,比如处理更多的事件、发送消息到频道等。