ZetCode

Dart SocketMessage

最后修改于 2025 年 4 月 4 日

Dart 中的 SocketMessage 类提供了一种通过套接字处理网络通信的方式。它对于构建客户端-服务器应用程序和实时系统非常有用。

SocketMessage 管理消息帧、序列化和网络传输。它通常与 Dart 的 dart:io 库一起用于套接字编程。

基本定义

SocketMessage 代表套接字上传递的离散通信单元。它通常包含用于结构化数据的头部和有效载荷。

主要功能包括消息帧、二进制/字符串转换和错误处理。它有助于管理网络通信的复杂性。

基本的 SocketMessage 用法

此示例展示了通过套接字创建和发送基本消息。

main.dart
import 'dart:io';
import 'dart:convert';

class SocketMessage {
  final Map<String, String> headers;
  final String body;

  SocketMessage(this.headers, this.body);

  List<int> toBytes() {
    var headerStr = headers.entries
      .map((e) => '${e.key}:${e.value}')
      .join(';');
    return utf8.encode('$headerStr|$body');
  }
}

void main() async {
  var server = await ServerSocket.bind('127.0.0.1', 4040);
  print('Server listening on ${server.address}:${server.port}');

  server.listen((client) {
    client.transform(utf8.decoder).listen((data) {
      print('Received: $data');
    });
  });

  var client = await Socket.connect('127.0.0.1', 4040);
  var message = SocketMessage(
    {'type': 'greeting', 'length': '5'},
    'Hello'
  );
  client.add(message.toBytes());
}

我们创建了一个带有头部和主体的简单 SocketMessage 类。服务器监听连接,而客户端发送格式化消息。消息被转换为字节进行传输。

$ dart main.dart
Server listening on 127.0.0.1:4040
Received: type:greeting;length:5|Hello

处理二进制数据

此示例演示了 SocketMessage 中处理二进制数据。

main.dart
import 'dart:io';
import 'dart:typed_data';

class BinarySocketMessage {
  final int type;
  final Uint8List data;

  BinarySocketMessage(this.type, this.data);

  Uint8List toBytes() {
    var buffer = ByteData(4 + data.length);
    buffer.setUint32(0, type);
    buffer.buffer.asUint8List().setAll(4, data);
    return buffer.buffer.asUint8List();
  }
}

void main() async {
  var server = await ServerSocket.bind('127.0.0.1', 4041);
  server.listen((client) {
    client.listen((data) {
      var bytes = Uint8List.fromList(data);
      var type = ByteData.sublistView(bytes, 0, 4).getUint32(0);
      print('Received message type: $type');
    });
  });

  var client = await Socket.connect('127.0.0.1', 4041);
  var message = BinarySocketMessage(
    42,
    Uint8List.fromList([1, 2, 3, 4, 5])
  );
  client.add(message.toBytes());
}

我们创建了一个二进制消息格式,其头部是 4 字节的类型,后面跟着有效载荷。服务器从二进制数据中提取消息类型。这对于二进制协议来说效率很高。

$ dart main.dart
Received message type: 42

消息帧

此示例展示了如何实现带长度前缀的消息帧。

main.dart
import 'dart:io';
import 'dart:typed_data';
import 'dart:convert';

class FramedSocketMessage {
  final String content;

  FramedSocketMessage(this.content);

  Uint8List toBytes() {
    var contentBytes = utf8.encode(content);
    var buffer = ByteData(4 + contentBytes.length);
    buffer.setUint32(0, contentBytes.length);
    buffer.buffer.asUint8List().setAll(4, contentBytes);
    return buffer.buffer.asUint8List();
  }
}

void main() async {
  var server = await ServerSocket.bind('127.0.0.1', 4042);
  server.listen((client) {
    var buffer = <int>[];
    client.listen((data) {
      buffer.addAll(data);
      if (buffer.length >= 4) {
        var length = ByteData.sublistView(Uint8List.fromList(buffer), 0, 4)
          .getUint32(0);
        if (buffer.length >= 4 + length) {
          var message = utf8.decode(buffer.sublist(4, 4 + length));
          print('Framed message: $message');
          buffer = buffer.sublist(4 + length);
        }
      }
    });
  });

  var client = await Socket.connect('127.0.0.1', 4042);
  var message = FramedSocketMessage('Hello, framed world!');
  client.add(message.toBytes());
}

我们实现了带长度前缀的消息帧。消息以 4 字节的长度开头,后跟有效载荷。服务器首先读取长度,然后读取精确的有效载荷字节。这可以正确处理消息边界。

$ dart main.dart
Framed message: Hello, framed world!

JSON 消息格式

此示例演示了使用 JSON 进行结构化消息内容。

main.dart
import 'dart:io';
import 'dart:convert';

class JsonSocketMessage {
  final String type;
  final Map<String, dynamic> payload;

  JsonSocketMessage(this.type, this.payload);

  List<int> toBytes() {
    var message = {
      'type': type,
      'payload': payload,
      'timestamp': DateTime.now().toIso8601String()
    };
    return utf8.encode(json.encode(message));
  }
}

void main() async {
  var server = await ServerSocket.bind('127.0.0.1', 4043);
  server.listen((client) {
    client.transform(utf8.decoder).listen((data) {
      var message = json.decode(data);
      print('${message['type']}: ${message['payload']}');
    });
  });

  var client = await Socket.connect('127.0.0.1', 4043);
  var message = JsonSocketMessage(
    'user.login',
    {'username': 'johndoe', 'password': 'secret'}
  );
  client.add(message.toBytes());
}

我们创建了带有类型和有效载荷字段的 JSON 格式消息。服务器解码 JSON 并提取消息组件。这对于 Web API 和结构化通信很有用。

$ dart main.dart
user.login: {username: johndoe, password: secret}

错误处理

此示例展示了套接字消息处理中的强大错误处理。

main.dart
import 'dart:io';
import 'dart:convert';

class SafeSocketMessage {
  final String command;
  final List<String> args;

  SafeSocketMessage(this.command, this.args);

  List<int> toBytes() {
    try {
      return utf8.encode('$command:${args.join(',')}');
    } catch (e) {
      return utf8.encode('error:Failed to encode message');
    }
  }
}

void main() async {
  var server = await ServerSocket.bind('127.0.0.1', 4044);
  server.listen((client) {
    client.transform(utf8.decoder).listen(
      (data) {
        try {
          var parts = data.split(':');
          if (parts.length != 2) throw FormatException('Invalid format');
          print('Command: ${parts[0]}, Args: ${parts[1].split(',')}');
        } catch (e) {
          print('Error processing message: $e');
          client.write('error:Invalid message format');
        }
      },
      onError: (e) => print('Connection error: $e'),
      onDone: () => print('Client disconnected')
    );
  });

  var client = await Socket.connect('127.0.0.1', 4044);
  var message = SafeSocketMessage('calculate', ['add', '5', '3']);
  client.add(message.toBytes());
  
  client.listen((response) {
    print('Server response: ${utf8.decode(response)}');
    client.close();
  });
}

我们在消息创建和处理阶段都实现了错误处理。客户端发送结构化的命令消息,而服务器对其进行验证。当出现问题时,会发送错误响应。

$ dart main.dart
Command: calculate, Args: [add, 5, 3]
Client disconnected

最佳实践

来源

Dart Socket 文档

本教程介绍了 Dart 的 SocketMessage 模式,并提供了实际示例,展示了文本/二进制格式、消息帧、JSON 和错误处理,以实现健壮的网络通信。

作者

我叫 Jan Bodnar,是一名热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教学编程方面拥有超过十年的经验。

列出 所有 Dart 教程