如何使用 Dart 中的 Socket

前言

Dart也支持Socket长链接。接下来, 我们一起学习。

dart:io 库中提供了两个类,第一个是 Socket,我们可以用它作为客户端与服务器建立连接。第二个是 ServerSocket,我们将使用它创建一个服务器,并与客户端进行连接。

Socket 客户端

源码如下:

abstract class Socket implements Stream<Uint8List>, IOSink {
  
  static Future<Socket> connect(host, int port,
      {sourceAddress, Duration? timeout}) {
    final IOOverrides? overrides = IOOverrides.current;
    if (overrides == null) {
      return Socket._connect(host, port,
          sourceAddress: sourceAddress, timeout: timeout);
    }
    return overrides.socketConnect(host, port,
        sourceAddress: sourceAddress, timeout: timeout);
  }

  static Future<ConnectionTask<Socket>> startConnect(host, int port,
      {sourceAddress}) {
    final IOOverrides? overrides = IOOverrides.current;
    if (overrides == null) {
      return Socket._startConnect(host, port, sourceAddress: sourceAddress);
    }
    return overrides.socketStartConnect(host, port,
        sourceAddress: sourceAddress);
  }

  external static Future<Socket> _connect(host, int port,
      {sourceAddress, Duration? timeout});

  external static Future<ConnectionTask<Socket>> _startConnect(host, int port,
      {sourceAddress});

  void destroy();

  bool setOption(SocketOption option, bool enabled);

  Uint8List getRawOption(RawSocketOption option);

  void setRawOption(RawSocketOption option);

  int get remotePort;

  InternetAddress get address;

  InternetAddress get remoteAddress;

  Future close();

  Future get done;
}
复制代码

由源码可知:

Socket 类中有一个静态方法 connect(host, int port) 。第一个参数 host 可以是一个域名或者 IP 的 String,也可以是 InternetAddress 对象。

connect 返回一个 Future<Socket> 对象,当 socket 与 host 完成连接时 Future 对象回调。

// socket_pratice1.dart
void main() {
 Socket.connect("www.baidu.com", 80).then((socket) {
   print('Connected to: '
       '${socket.remoteAddress.address}:${socket.remotePort}');
   socket.destroy();
 });
}
复制代码

这个 case 中,我们通过 80 端口(为 HTTP 协议开放)与 www.baidu.com 连接。连接到服务器之后,打印出连接的 IP 地址和端口,最后通过 socket.destroy() 关闭连接。在命令行中 执行 dart socket_pratice1.dart可以看到如下输出:

➜  socket_study dart socket_pratice1.dart \
socket_pratice2.dart: Warning: Interpreting this as package URI, 'package:io_pratice/socket_study/socket_pratice2.dart'.\
Connected to: 220.181.38.149:80
复制代码

通过简单的函数调用,Dart 为我们完成了 www.baidu.com 的 IP 查找与 TCP 建立连接,我们只需要等待即可。在连接建立之后,我们可以和服务端进行数据交互,为此我们需要做两件事。

1、发起请求 2、响应接受数据 对应 Socket 中提供的两个方法 Socket.write(String data) 和 Socket.listen(void onData(data)) 。

// socket_pratice2.dart
void main() {
  String indexRequest = 'GET / HTTP/1.1\nConnection: close\n\n';\

  //与百度通过 80 端口连接\
  Socket.connect("www.baidu.com", 80).then((socket) {\
    print('Connected to: '\
        '${socket.remoteAddress.address}:${socket.remotePort}');\

    //监听 socket 的数据返回\
    socket.listen((data) {\
      print(new String.fromCharCodes(data).trim());\
    }, onDone: () {
      print("Done");
      socket.destroy();
    });

    //发送数据
    socket.write(indexRequest);
  });
}
复制代码

ServerSocket 服务端

源码如下:

abstract class ServerSocket implements Stream<Socket> {
  static Future<ServerSocket> bind(address, int port,
      {int backlog = 0, bool v6Only = false, bool shared = false}) {
    final IOOverrides? overrides = IOOverrides.current;
    if (overrides == null) {
      return ServerSocket._bind(address, port,
          backlog: backlog, v6Only: v6Only, shared: shared);
    }
    return overrides.serverSocketBind(address, port,
        backlog: backlog, v6Only: v6Only, shared: shared);
  }

  external static Future<ServerSocket> _bind(address, int port,
      {int backlog = 0, bool v6Only = false, bool shared = false});

  int get port;

  InternetAddress get address;

  Future<ServerSocket> close();
}
复制代码

使用 Socket 可以很容易的与服务器连接,同样我们可以使用 ServerSocket 对象创建一个可以处理客户端请求的服务器。首先我们需要绑定到一个特定的端口并进行监听,使用 ServerSocket.bind(address,int port)方法即可。这个方法会返回 Future<ServerSocket> 对象,在绑定成功后返回 ServerSocket 对象。之后 ServerSocket.listen(void onData(Socket event)) 方法注册回调,便可以得到客户端连接的 Socket 对象。注意,端口号需要大于 1024 (保留范围)。

// serversocket_pratice1.dart
void main() {
  ServerSocket.bind(InternetAddress.anyIPv4, 4567)
      .then((ServerSocket server) {
    server.listen(handleClient);
  });
}

void handleClient(Socket client) {
  print('Connection from '
      '${client.remoteAddress.address}:${client.remotePort}');
  client.write("Hello from simple server!\n");
  client.close();
}
复制代码

与客户端不同的是,在 ServerSocket.listen 中我们监听的不是二进制数据,而是客户端连接。当客户端发起连接时,我们可以得到一个表示客户端连接的 Socket 对象。作为参数调用 handleClient(Socket client) 函数。通过这个 Socket 对象,我们可以获取到客户端的 IP 端口等信息,并且可以与其通信。运行这个程序后,我们需要一个客户端连接服务器。可以将上一个案例中 conect 的地址改为 127.0.0.0.1,端口改为 4567,或者使用 telnet 作为客户端发起。

可以看见, Dart中的Socket通信比较好理解。

参考资料

Flutter接入阿里云公共DNS Android/iOS SDK实践方案

猜你喜欢

转载自juejin.im/post/7035951717774524452
今日推荐