前言
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通信比较好理解。