public class TLSSocketChannel extends AbstractSocketChannel {
private final SSLEngine sslEngine;
private ByteBuffer outboundNetData;
private ByteBuffer inboundNetData;
private ByteBuffer inboundAppData;
public TLSSocketChannel(SocketChannel socketChannel, SSLEngine sslEngine) {
super(socketChannel);
this.sslEngine = sslEngine;
this.outboundNetData = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
this.inboundNetData = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
this.inboundAppData = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize());
this.inboundAppData.flip();
}
public void startHandshake() throws IOException {
sslEngine.beginHandshake();
_MAIN:
while (true) {
var handshakeStatus = sslEngine.getHandshakeStatus();
switch (handshakeStatus) {
case NEED_UNWRAP -> {
_NU:
while (true) {
if (socketChannel.read(inboundNetData) == -1) {
throw new SSLHandshakeException("Channel closed during handshake");
}
inboundNetData.flip();
var result = sslEngine.unwrap(inboundNetData, ByteBuffer.allocate(0));
switch (result.getStatus()) {
case OK -> {
break _NU;
}
case BUFFER_OVERFLOW -> {
throw new SSLHandshakeException("Unexpected buffer overflow");
}
case BUFFER_UNDERFLOW -> {
int remainingSpace = inboundNetData.capacity() - inboundNetData.limit();
if (remainingSpace == 0) {
var newInboundNetData = ByteBuffer.allocate(inboundNetData.capacity() * 2);
newInboundNetData.put(inboundNetData);
inboundNetData = newInboundNetData;
}
}
case CLOSED -> {
throw new SSLHandshakeException("closed on handshake wrap");
}
}
}
inboundNetData.compact();
}
case NEED_WRAP -> {
outboundNetData.clear();
_NW:
while (true) {
var result = sslEngine.wrap(ByteBuffer.allocate(0), outboundNetData);
switch (result.getStatus()) {
case OK -> {
outboundNetData.flip();
while (outboundNetData.hasRemaining()) {
socketChannel.write(outboundNetData);
}
break _NW;
}
case BUFFER_OVERFLOW -> {
outboundNetData = ByteBuffer.allocate(outboundNetData.capacity() * 2);
}
case BUFFER_UNDERFLOW -> {
throw new SSLHandshakeException("buffer underflow on handshake wrap");
}
case CLOSED -> {
throw new SSLHandshakeException("closed on handshake wrap");
}
}
}
}
case NEED_TASK -> {
while (true) {
var task = sslEngine.getDelegatedTask();
if (task == null) {
break;
}
task.run();
}
}
case FINISHED -> {
break _MAIN;
}
case NOT_HANDSHAKING -> {
break _MAIN;
}
}
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
if (inboundAppData.hasRemaining()) {
return transferByteBuffer(inboundAppData, dst);
}
inboundAppData.clear();
var totalBytesConsumed = 0;
var totalBytesProduced = 0;
_R:
while (true) {
var bytesRead = socketChannel.read(inboundNetData);
if (bytesRead == -1) {
return -1;
}
inboundNetData.flip();
_UW:
while (inboundNetData.hasRemaining()) {
var result = sslEngine.unwrap(inboundNetData, inboundAppData);
totalBytesConsumed += result.bytesConsumed();
totalBytesProduced += result.bytesProduced();
switch (result.getStatus()) {
case OK -> {
}
case BUFFER_OVERFLOW -> {
var newAppBuffer = ByteBuffer.allocate(inboundAppData.capacity() * 2);
newAppBuffer.put(inboundAppData.flip());
inboundAppData = newAppBuffer;
}
case BUFFER_UNDERFLOW -> {
if (totalBytesProduced > 0) {
break _UW;
}
var newNetBuffer = ByteBuffer.allocate(inboundNetData.capacity() * 2);
newNetBuffer.put(inboundNetData);
inboundNetData = newNetBuffer;
continue _R;
}
case CLOSED -> {
break _R;
}
}
}
break;
}
inboundNetData.compact();
inboundAppData.flip();
return transferByteBuffer(inboundAppData, dst);
}
@Override
public int write(ByteBuffer src) throws IOException {
int n = 0;
while (src.hasRemaining()) {
outboundNetData.clear();
var result = sslEngine.wrap(src, outboundNetData);
switch (result.getStatus()) {
case OK -> {
outboundNetData.flip();
while (outboundNetData.hasRemaining()) {
socketChannel.write(outboundNetData);
}
n += result.bytesConsumed();
}
case BUFFER_OVERFLOW -> {
outboundNetData = ByteBuffer.allocate(outboundNetData.capacity() * 2);
}
case BUFFER_UNDERFLOW -> {
throw new IOException("SSLEngine wrap BUFFER_UNDERFLOW");
}
case CLOSED -> {
throw new IOException("SSLEngine wrap CLOSED");
}
}
}
return n;
}
@Override
protected void implCloseSelectableChannel() throws IOException {
sslEngine.closeOutbound();
socketChannel.close();
}
public static int transferByteBuffer(ByteBuffer source, ByteBuffer dest) {
int sourceRemaining = source.remaining();
int destRemaining = dest.remaining();
if (sourceRemaining > destRemaining) {
int originalLimit = source.limit();
source.limit(source.position() + destRemaining);
dest.put(source);
source.limit(originalLimit);
return destRemaining;
} else {
dest.put(source);
return sourceRemaining;
}
}
}
AbstractSocketChannel просто делегирует некоторые менее важные методы. Поэтому я не стал размещать это здесь.
Я пишу TLSSocketChannel.
Но когда я запускаю клиент в виртуальном потоке, startHandshake блокируется, почему ?
Когда я пытаюсь выполнить System.out.println("xxx") на TLSSocketChannel.startHandshake, иногда не происходит блокировка?
В потоке платформы никогда не блокируется .
public static void startClient() throws IOException {
var socketChannel = SocketChannel.open(); var sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(true);
var tlsSocketChannel = new TLSSocketChannel(socketChannel, sslEngine); tlsSocketChannel.configureBlocking(true); tlsSocketChannel.connect(new InetSocketAddress(8080));
public void startHandshake() throws IOException { sslEngine.beginHandshake();
_MAIN: while (true) { var handshakeStatus = sslEngine.getHandshakeStatus(); switch (handshakeStatus) { case NEED_UNWRAP -> {
_NU: while (true) {
if (socketChannel.read(inboundNetData) == -1) { throw new SSLHandshakeException("Channel closed during handshake"); }
inboundNetData.flip();
var result = sslEngine.unwrap(inboundNetData, ByteBuffer.allocate(0)); switch (result.getStatus()) { case OK -> { break _NU; } case BUFFER_OVERFLOW -> { throw new SSLHandshakeException("Unexpected buffer overflow"); } case BUFFER_UNDERFLOW -> { int remainingSpace = inboundNetData.capacity() - inboundNetData.limit(); if (remainingSpace == 0) { var newInboundNetData = ByteBuffer.allocate(inboundNetData.capacity() * 2); newInboundNetData.put(inboundNetData); inboundNetData = newInboundNetData; } } case CLOSED -> { throw new SSLHandshakeException("closed on handshake wrap"); } } }
inboundNetData.compact(); } case NEED_WRAP -> {
outboundNetData.clear();
_NW: while (true) {
var result = sslEngine.wrap(ByteBuffer.allocate(0), outboundNetData); switch (result.getStatus()) { case OK -> {
outboundNetData.flip();
while (outboundNetData.hasRemaining()) { socketChannel.write(outboundNetData); } break _NW; } case BUFFER_OVERFLOW -> { outboundNetData = ByteBuffer.allocate(outboundNetData.capacity() * 2); } case BUFFER_UNDERFLOW -> { throw new SSLHandshakeException("buffer underflow on handshake wrap"); } case CLOSED -> { throw new SSLHandshakeException("closed on handshake wrap"); } } } } case NEED_TASK -> { while (true) { var task = sslEngine.getDelegatedTask(); if (task == null) { break; } task.run(); } } case FINISHED -> { break _MAIN; } case NOT_HANDSHAKING -> { break _MAIN; } } } }
@Override public int read(ByteBuffer dst) throws IOException {
if (inboundAppData.hasRemaining()) { return transferByteBuffer(inboundAppData, dst); }
inboundAppData.clear();
var totalBytesConsumed = 0; var totalBytesProduced = 0;
_R: while (true) {
var bytesRead = socketChannel.read(inboundNetData);
if (bytesRead == -1) { return -1; }
inboundNetData.flip();
_UW: while (inboundNetData.hasRemaining()) {
var result = sslEngine.unwrap(inboundNetData, inboundAppData);
public static int transferByteBuffer(ByteBuffer source, ByteBuffer dest) { int sourceRemaining = source.remaining(); int destRemaining = dest.remaining(); if (sourceRemaining > destRemaining) { int originalLimit = source.limit(); source.limit(source.position() + destRemaining); dest.put(source); source.limit(originalLimit); return destRemaining; } else { dest.put(source); return sourceRemaining; } }
} [/code] AbstractSocketChannel просто делегирует некоторые менее важные методы. Поэтому я не стал размещать это здесь. Я пишу TLSSocketChannel. Но когда я запускаю клиент в виртуальном потоке, startHandshake блокируется, почему ? Когда я пытаюсь выполнить System.out.println("xxx") на TLSSocketChannel.startHandshake, иногда не происходит блокировка? В потоке платформы никогда не блокируется .