proto-socket/dart/lib/src/ws_protobuf_client_io.dart
toki 5df325bf96 feat: Dart Web E2E 테스트 및 크로스 테스트 추가
- Dart Web 브라우저 E2E 테스트 프레임워크 추가
- browser_ws_dart_io_test, browser_ws_kotlin_test 등 브라우저 통합 테스트 추가
- Dart Web 크로스테스트 케이스 추가 (go, kotlin, python, typescript)
- TypeScript 브라우저 웹소켓 클라이언트 개선
- Node.js 웹소켓 서버 클라이언트 업데이트
- 테스트 실행 매트릭스 스킬 및 스크립트 업데이트
- README.md 문서 업데이트
2026-05-25 00:30:37 +09:00

83 lines
2.4 KiB
Dart

// ignore_for_file: avoid_init_to_null, prefer_final_fields
import 'dart:io';
import 'dart:async';
import 'dart:typed_data';
import 'package:protobuf/protobuf.dart';
import 'base_client.dart';
import 'packets/message_common.pb.dart';
import 'transport.dart';
abstract class WsProtobufClient extends BaseClient<WsProtobufClient> {
final WebSocket _ws;
late final _WebSocketTransport _transport;
/// Plain WebSocket connection.
static Future<WebSocket> connect(String host, int port,
{String path = '/'}) =>
WebSocket.connect('ws://$host:$port$path');
/// Secure WebSocket (WSS) connection.
static Future<WebSocket> connectSecure(String host, int port,
{String path = '/',
SecurityContext? context,
bool Function(X509Certificate certificate)? onBadCertificate}) {
final client =
HttpClient(context: context ?? SecurityContext.defaultContext);
if (onBadCertificate != null) {
client.badCertificateCallback = (certificate, _, __) {
return onBadCertificate(certificate);
};
}
return WebSocket.connect('wss://$host:$port$path', customClient: client);
}
WsProtobufClient(this._ws, int heartbeatIntervalTime, int heartbeatWaitTime,
Map<String, GeneratedMessage Function(List<int>)> parserMap) {
initSelf(this);
initHeartbeat(heartbeatIntervalTime, heartbeatWaitTime);
_transport = _WebSocketTransport(_ws);
isAlive = true;
parserMap.addAll({(HeartBeat).toString(): HeartBeat.fromBuffer});
super.initialize(parserMap, transport: _transport);
_ws.listen(_onMessage, onError: onError, onDone: close);
addListener(onHeartBeat);
sendHeartBeat();
}
void onError(dynamic e) {}
void _onMessage(dynamic data) {
final bytes = data is List<int> ? data : (data as Uint8List).toList();
final common = PacketBase.fromBuffer(bytes);
onReceivedData(common.typeName, common.data,
incomingNonce: common.nonce, responseNonce: common.responseNonce);
sendHeartBeat();
}
@override
Future<void> closeTransport() async {
await _transport.close();
}
}
class _WebSocketTransport implements Transport {
final WebSocket _ws;
_WebSocketTransport(this._ws);
@override
Future<void> writePacket(PacketBase base) async {
_ws.add(base.writeToBuffer());
}
@override
Future<void> close() async {
try {
await _ws.close();
} catch (_) {
// already closed by peer
}
}
}