SocketException: OS Error: Broken pipe, errno = 32
这个错误其实有点迷惑性,很多时候调用栈显示是在socket.connect
的时机出的错,但实际是因为socket已经被对端关闭,而关闭的原因可能是因为发送了错误的数据。
解决:
发送数据时机检查socket连接状态及数据正确性。
Bad state: StreamSink is bound to a stream
这个错误容易发生在调用socket.flush
之后,这是因为flush操作是一个future, 如果在这个操作结束之前就向socket中写入数据就会报这个错误。实际上调用socket.close
也有可能遇到这种错误。在io_sink.dart
里源码如下:
Future flush() {
if (_isBound) {
throw new StateError("StreamSink is bound to a stream");
}
...
}
Future close() {
if (_isBound) {
throw new StateError("StreamSink is bound to a stream");
}
...
return done;
}
解决:
或者写入时机在flush完成之后,需要外部关心写入时机;或者封装socket并持有一个缓冲数据对象,外部写入时判断当前socket状态,如果flush完成则直接socket.add
否则写入到缓冲区,flush完成时再发送缓冲的数据, 外部就不用再关心写入时机了。
后者实现显然更复杂,需要一揽子的状态判断和处理操作, 但是把这个复杂留给外部逻辑会让工程整体更复杂。
StreamSink is closed
这个错误虽然直白但需要明确什么时机哪个stream是关闭的。一种情况是发生在和socket进行关联的stream的关闭操作上,如上socket.close
是一个future, future结束之前还能够接收数据,如果我们的关联stream的close
是和socket.close
一个时机,那么当socket.close的future还没有结束这时又有数据从远端过来,调用关联stream的处理操作就会出这个错误。
形如:
void close() {
_socket.close();
your_stream.close();
}
void handleData() {
_socket.listen((data) {
your_stream.add(yourData(data)); // your_stream可能已经关闭
});
}
解决:
关联的stream的操作应当在socket.close
的future结束之后再关闭。
形如:
void close() async {
await _socket.close();
your_stream.close();
}
http/2 connection is no longer active
因为在package:http
中发送多个请求到同一服务器有如下的示例:
var client = http.Client();
try {
var response = await client.post(
Uri.https('example.com', 'whatsit/create'),
body: {'name': 'doodle', 'color': 'blue'});
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
var uri = Uri.parse(decodedResponse['uri'] as String);
print(await client.get(uri));
} finally {
client.close();
}
很自然地想到,能不能在package:http2
中也做类似的复用,比如有如下的场景:
@override
Stream<String> bind(Stream<String> stream) async* {
await for (final text in stream) {
yield* _doHTTP2Request(text);
}
}
Stream<String> _doHTTP2Request(String text) async* {
final transport = ClientTransportConnection.viaSocket(
await SecureSocket.connect(
uri.host,
uri.port,
supportedProtocols: ['h2'],
),
);
}
改成:
@override
Stream<String> bind(Stream<String> stream) async* {
final transport = ClientTransportConnection.viaSocket(
await SecureSocket.connect(
uri.host,
uri.port,
supportedProtocols: ['h2'],
),
);
await for (final text in stream) {
transport.makeRequest..
}
这时候就会发生这个错误!
解决:
保持原来的写法,每次请求都生成连接的实例,也就是说不能复用http2包中的ClientTransportConnection
实例。