如何为 Java 套接字正确实现阻塞、线程安全的写入方法?
How to properly implement a blocking, thread-safe write method for Java sockets?
我在 Java 中写了一个 WebSocket 服务器。
这是服务器用来向其客户端发送 WebSocket 数据包的方法:
private void sendFrame(boolean fin, boolean rsv1, boolean rsv2, boolean rsv3, WebSocketOpcode opcode, byte[] payloadData) throws IOException {
if (connection.isClosed() || webSocketConnectionClosing != null) return;
byte[] header = new byte[2];
if (fin) header[0] |= 1 << 7;
if (rsv1) header[0] |= 1 << 6;
if (rsv2) header[0] |= 1 << 5;
if (rsv3) header[0] |= 1 << 4;
header[0] |= opcode.get() & 0b1111;
header[1] |= payloadData.length < 126 ? payloadData.length : (payloadData.length <= 65535 ? 126 : 127);
out.write(header);
if (payloadData.length > 125) {
if (payloadData.length <= 65535) {
out.writeShort(payloadData.length);
} else {
out.writeLong(payloadData.length);
}
}
out.write(payloadData);
out.flush();
}
这就是我在客户端连接后声明输出流的方式:
out = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream()));
我对此有一些疑问:
上面的代码是线程安全的吗?我的意思是,多个线程可以同时调用 sendFrame() 而没有数据包数据交错的风险吗?这段代码貌似是错误的,不过我还没有遇到交错的情况。
如果它不是线程安全的,那么我如何在不使用队列的情况下以这种形式使其成为线程安全的? (我希望 sendFrame() 方法在数据实际发送之前一直阻塞)
如果我不将 OutputStream 包装在 BufferedOutputStream 中,而是仅包装在 DataOutputStream 中,这会使 .write() 方法成为原子方法吗?将整个数据包数据打包到一个字节数组中,然后使用该数组调用一次 .write() 是否是线程安全的?
Is the above code thread-safe? What I mean by that is, can multiple threads call sendFrame() at the same time without the risk of packets data interleaving?
不是thread-safe。
It looks like this code is wrong, but I haven't encountered any interleaving yet.
交织发生的时间window非常小。可能不到一微秒。这意味着它发生的可能性很小。但不为零。
If it isn't thread-safe, then how would I make it thread-safe in this form without the use of queues? (I want the sendFrame() method to be blocking until the data is actually sent)
这取决于 sendFrame
方法如何适应您的其余代码。
我将使用的方法是确保针对特定输出流对 sendFrame
的所有调用都是在同一目标对象上进行的。然后我会使用synchronized来锁定目标对象或者属于目标对象的私有日志。
另一种方法是使用 synchronized
并锁定 out
。然而,存在其他东西已经在做的风险,并且 sendFrame
呼叫将被不必要地阻止。
If I wouldn't wrap the OutputStream
in BufferedOutputStream
, but only in DataOutputStream
instead, would this make the .write()
method atomic?
(这不是重点。您有 3 个写入调用需要应对。但是....)
Would it be thread-safe to pack the entire packet data into a single byte array and then call .write()
once with that array?
类 中的 None 已记录 1 作为 thread-safe,或作为保证write
操作是原子的。但是,在 OpenJDK Java 11(至少)中,相关的 write
方法在 BufferedOutputStream
和 DataOutputStream
.
中实现为 synchronized
1 - 如果 javadocs 不 指定 thread-safety 等特性,那么这些特性 可以 因 Java 版本等而异
我在 Java 中写了一个 WebSocket 服务器。 这是服务器用来向其客户端发送 WebSocket 数据包的方法:
private void sendFrame(boolean fin, boolean rsv1, boolean rsv2, boolean rsv3, WebSocketOpcode opcode, byte[] payloadData) throws IOException {
if (connection.isClosed() || webSocketConnectionClosing != null) return;
byte[] header = new byte[2];
if (fin) header[0] |= 1 << 7;
if (rsv1) header[0] |= 1 << 6;
if (rsv2) header[0] |= 1 << 5;
if (rsv3) header[0] |= 1 << 4;
header[0] |= opcode.get() & 0b1111;
header[1] |= payloadData.length < 126 ? payloadData.length : (payloadData.length <= 65535 ? 126 : 127);
out.write(header);
if (payloadData.length > 125) {
if (payloadData.length <= 65535) {
out.writeShort(payloadData.length);
} else {
out.writeLong(payloadData.length);
}
}
out.write(payloadData);
out.flush();
}
这就是我在客户端连接后声明输出流的方式:
out = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream()));
我对此有一些疑问:
上面的代码是线程安全的吗?我的意思是,多个线程可以同时调用 sendFrame() 而没有数据包数据交错的风险吗?这段代码貌似是错误的,不过我还没有遇到交错的情况。
如果它不是线程安全的,那么我如何在不使用队列的情况下以这种形式使其成为线程安全的? (我希望 sendFrame() 方法在数据实际发送之前一直阻塞)
如果我不将 OutputStream 包装在 BufferedOutputStream 中,而是仅包装在 DataOutputStream 中,这会使 .write() 方法成为原子方法吗?将整个数据包数据打包到一个字节数组中,然后使用该数组调用一次 .write() 是否是线程安全的?
Is the above code thread-safe? What I mean by that is, can multiple threads call sendFrame() at the same time without the risk of packets data interleaving?
不是thread-safe。
It looks like this code is wrong, but I haven't encountered any interleaving yet.
交织发生的时间window非常小。可能不到一微秒。这意味着它发生的可能性很小。但不为零。
If it isn't thread-safe, then how would I make it thread-safe in this form without the use of queues? (I want the sendFrame() method to be blocking until the data is actually sent)
这取决于 sendFrame
方法如何适应您的其余代码。
我将使用的方法是确保针对特定输出流对 sendFrame
的所有调用都是在同一目标对象上进行的。然后我会使用synchronized来锁定目标对象或者属于目标对象的私有日志。
另一种方法是使用 synchronized
并锁定 out
。然而,存在其他东西已经在做的风险,并且 sendFrame
呼叫将被不必要地阻止。
If I wouldn't wrap the
OutputStream
inBufferedOutputStream
, but only inDataOutputStream
instead, would this make the.write()
method atomic?
(这不是重点。您有 3 个写入调用需要应对。但是....)
类 中的Would it be thread-safe to pack the entire packet data into a single byte array and then call
.write()
once with that array?
None 已记录 1 作为 thread-safe,或作为保证write
操作是原子的。但是,在 OpenJDK Java 11(至少)中,相关的 write
方法在 BufferedOutputStream
和 DataOutputStream
.
synchronized
1 - 如果 javadocs 不 指定 thread-safety 等特性,那么这些特性 可以 因 Java 版本等而异