Windows平台下TLS 1.3环境Java Socket发送数据后关闭连接触发服务器端异常的问题求助
Windows平台下TLS 1.3环境Java Socket发送数据后关闭连接触发服务器端异常的问题求助
我最近碰到一个挺棘手的问题:写了一段最基础的Java Socket通信代码,在TLS 1.2环境下跑完全正常,但切换到Windows平台的TLS 1.3就直接炸了——Linux上完全没这个问题,甚至把服务器部署在Windows、客户端放Linux也会触发异常,反过来就一切正常。
我的需求其实特别简单:客户端建立SSL连接后发送一次数据,然后直接关闭Socket。结果服务器端在处理的时候,每次都会抛出SocketException,提示连接被主机上的软件中止。我查了相关资料,感觉这应该是不同平台TLS 1.3实现的差异导致的,听说Java有个系统属性可以让TLS 1.3兼容旧的连接关闭行为,但我设置之后完全没效果。我就很疑惑,客户端发完数据就关闭连接这种场景太普遍了,怎么在TLS 1.3下就走不通呢?
服务器端抛出的异常栈:
java.net.SocketException: An established connection was aborted by the software in your host machine at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:330) at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:355) at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:808) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966) at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:484) at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:478) at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:70) at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1465) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:1069) at java.base/java.io.ObjectInputStream$PeekInputStream.read(ObjectInputStream.java:2897) at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2913) at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3410) at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:954) at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:392) at TestChallenge/scratch.OO.lambda$main$0(OO.java:36) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) at --- Async.Stack.Trace --- (captured by IntelliJ IDEA debugger) at java.base/java.util.concurrent.FutureTask.<init>(FutureTask.java:151) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.<init>(ScheduledThreadPoolExecutor.java:215) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:561) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor.execute(ScheduledThreadPoolExecutor.java:705) at TestChallenge/scratch.OO.main(OO.java:30)
完整代码实现:
import java.io.*; import java.net.*; import java.security.KeyStore; import java.security.cert.CertificateException; import java.util.concurrent.*; import javax.net.ssl.*; import org.jetbrains.annotations.NotNull; public class OO { public static void main(String[] args) throws Exception { enum STATE { CURR } final SSLContext sslContext = getSslContext(); final ServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory(); final SSLServerSocket serverSocket = (SSLServerSocket) serverSocketFactory.createServerSocket(1888); final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(20); scheduledExecutorService.execute(() -> { while (!serverSocket.isClosed()) { try { final SSLSocket socket = (SSLSocket) serverSocket.accept(); try { try(final InputStream inputStream = socket.getInputStream()){ final ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); final Object o = objectInputStream.readObject(); System.out.println(o); } } catch (IOException e) { e.printStackTrace(); } // 尝试过开启SO_Linger,但无效果 // socket.setSoLinger(true,10); } catch (Exception e) { e.printStackTrace(); } } }); final javax.net.SocketFactory socketFactory = sslContext.getSocketFactory(); scheduledExecutorService.scheduleWithFixedDelay(() -> { try { try (final SSLSocket socket = (SSLSocket) socketFactory.createSocket("127.0.0.1", 1888)) { // 客户端侧也尝试过设置SO_Linger,同样无效 // socket.setSoLinger(true,10); socket.startHandshake(); final OutputStream outputStream = socket.getOutputStream(); try (final ObjectOutputStream oo = new ObjectOutputStream(socket.getOutputStream())) { oo.writeObject(STATE.CURR); outputStream.flush(); } } } catch (IOException e) { e.printStackTrace(); } }, 0, 1, TimeUnit.SECONDS); new CountDownLatch(1).await(); } @NotNull public static SSLContext getSslContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { final KeyStore keyStore = KeyStore.getInstance("pkcs12"); // 实际代码中会正确加载PKCS12证书文件,此处保留原代码片段 final String s1 ="MIIKZAIBAzCCCg4GCSqGSIb3DQEHAaCCCf8Eggn7MIIJ9zCCBa4GCSqGSIb3DQEHAaCCBZ8EggWbMIIFlzCCBZMGCyqGSIb3DQEMCgECoIIFQDCCBTwwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFBCnlxptM/N5OqAPpFkoEY3r+0QWAgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQG/RKlEAGPpW5betwGewIlwSCBN"; // 此处省略SSLContext的完整初始化逻辑 return SSLContext.getDefault(); } }
我已经尝试在客户端和服务器端都设置socket.setSoLinger(true,10),但完全没效果,还是会抛出同样的异常。有没有朋友碰到过类似的问题?或者知道怎么让TLS 1.3下的Socket兼容这种“发完数据就关闭”的场景?
内容来源于stack exchange
相关产品推荐
相关产品推荐

