问题描述
TL;DR:
我明白,java HttpsServer 更多的是服务于常规的 HTTPS 请求,但是当客户端做一些疯狂的事情时它不应该崩溃或挂起。特别是,当基于 HttpsServer 的项目使用 JDK v11-v15 编译,并且 FireFox Dev 87.0b3 与 JS WebSocket 对象连接时 - 池线程挂起并且每隔一个 websocket 连接将导致积压填充我的 PC 上的 cpu 负载增加约 6%每次 F5 页面刷新。
使用 Chrome 或 Edge 访问时,情况并非如此。使用 jdk v1.8.0_251、v16 或 v17 编译的同一个项目在所有 3 个浏览器上都可以正常工作,因此它是 v11 到 v15 版本中的一个错误,只能用 FF 重现。
问题:
如何复制:
我拉皮条 this example 总是返回 404 错误。我还记录了一个线程名称并使用了 2 个池线程来证明只有一个具有 websocket 连接的线程挂起。
package test;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadPoolExecutor;
import java.security.KeyStore;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.io.FileInputStream;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.util.concurrent.SynchronousQueue;
import java.lang.Runnable;
import java.net.HttpURLConnection;
import java.io.IOException;
public class MyHttpsServer implements HttpHandler {
public static void main(String[] args) {
MyHttpsServer app = new MyHttpsServer();
}
public MyHttpsServer() {
System.out.println("Starting server...");
try {
InetSocketAddress address = new InetSocketAddress("192.168.1.2",8080);
HttpsServer httpsServer = HttpsServer.create(address,0);
SSLContext sslContext = SSLContext.getInstance("TLS");
char[] password = "qwerty".tochararray();
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("my.keystore");
ks.load(fis,password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks,password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sslContext.init(kmf.getKeyManagers(),tmf.getTrustManagers(),null);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
SSLContext c = SSLContext.getDefault();
SSLEngine engine = c.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
params.setSSLParameters(defaultSSLParameters);
} catch (Throwable t) {
System.out.println("Failed to create HTTPS port: " + t.getLocalizedMessage());
}
}
});
httpsServer.createContext("/",this);
ThreadPoolExecutor httpsServerExecutors = new ThreadPoolExecutor(2,2,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
httpsServer.setExecutor(httpsServerExecutors);
httpsServer.start();
System.out.println("Server is running!");
} catch (Throwable t) {
System.out.println("Failed to create HTTPS server on port: " + t.getLocalizedMessage());
}
}
@Override
public void handle(HttpExchange httpsExchange) throws IOException {
System.out.println("in " + Thread.currentThread().getName());
OutputStream os = null;
try {
Thread.sleep(1000);
httpsExchange.getResponseHeaders().add("Access-Control-Allow-Origin","*");
httpsExchange.sendResponseHeaders(HttpURLConnection.HTTP_NOT_FOUND,0);
os = httpsExchange.getResponseBody();
os.write("Page Not Found".getBytes(java.nio.charset.StandardCharsets.UTF_8));
} catch (Throwable t) {
System.out.println("Failed to handle: " + t.getLocalizedMessage());
} finally {
os.close();
}
}
}
这与 Java 8 和 11 完美配合,可以从 FF、Edge 和 Chrome 定期访问,无论您刷新页面多少次。
<html><head><script>
let socket = new WebSocket("wss://192.168.1.2:8080/WebSocket");
</script></head><body>OK</body></html>
将其放入 FireFox 并刷新页面两次。第三个不会出现在服务器日志中,坐在积压队列中。
可能的原因:
如果您在“do 循环”中的 sun.net.httpserver.SSLStreams::doClosure() 中放置断点并检查线程名称“Thread.currentThread().getName()”,它将与日志匹配。它旋转轮子,直到“\r\n”出现,这永远不会发生,因为 WebSocket 不会发送它。更糟糕的是,因为这个循环不检查中断,我什至无法停止这个线程。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)