【发布时间】:2020-08-29 03:38:25
【问题描述】:
我已经使用 SSLSockets 开发了自己的 MMORPG 游戏客户端和服务器。新连接在它们自己的线程中获得数据输出/输入流。在我的主线程循环中,我有一个方法通过连接映射并关闭连接(不活动启动、请求注销等)。
我的游戏服务器随机挂起 15 分钟,因此每隔 60 秒,在另一个线程中,我打印出堆栈跟踪以及主线程运行了多少循环。
main: [sun.misc.Unsafe.park(Native Method),
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175),
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836),
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870),
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199),
java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209),
java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285),
sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:863),
sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:735),
sun.security.ssl.SSLSocketImpl.sendAlert(SSLSocketImpl.java:2087),
sun.security.ssl.SSLSocketImpl.warning(SSLSocketImpl.java:1914),
sun.security.ssl.SSLSocketImpl.closeInternal(SSLSocketImpl.java:1677),
sun.security.ssl.SSLSocketImpl.close(SSLSocketImpl.java:1615),
com.jayavon.game.server.MyServer.processClientConnectionMapV2(MyServer.java:6427),
com.jayavon.game.server.MyServer.main(MyServer.java:708)]
由此我可以看出我的服务器在尝试关闭连接时卡住了:
clientConnectionEntry.getValue().getSocket().close();
这是它卡在运行中的浓缩方法(挂起整个服务器):
private void processClientConnectionMapV2(boolean disconnectAll) {
try {
Iterator<Entry<String, ClientConnectionV2>> it = MyServer.ClientConnectionMapV2.entrySet().iterator();
while (it.hasNext()) {
Entry<String, ClientConnectionV2> clientConnectionEntry = it.next();
if (clientConnectionEntry.getValue().isToDisconnect()
|| disconnectAll){ //this connection should be disconnected
if (!hasOutstandingBacklogMessagesV2(MyServer.ClientConnectionMapV2.get(clientConnectionEntry.getKey()))
|| clientConnectionEntry.getValue().getDisconnectLoopsSkipped() > 4
|| disconnectAll) {
MyServer.LOGGER.warn("----REMOVE CONNECTION----<processClientConnectionMapV2>---- " + clientConnectionEntry);
//close the actual connection here
try {
clientConnectionEntry.getValue().getSocket().close();
clientConnectionEntry.getValue().getDataInputStream().close();
clientConnectionEntry.getValue().getDataOutputStream().close();
clientConnectionEntry.getValue().getCommandHandlerV2().loggedOut = true;
serverConnectionNumber--;
} catch (Exception e1) {
LOGGER.warn("unable to close in Server processClientConnectionsV2", e1);
LOGGER.warn("connection was already closed on client side");
}
//remove from iterator and ClientConnectionMapV2
it.remove();
} else {
clientConnectionEntry.getValue().setDisconnectLoopsSkipped(clientConnectionEntry.getValue().getDisconnectLoopsSkipped() + 1);
MyServer.LOGGER.warn("----SKIP REMOVE CONNECTION----<processClientConnectionMapV2>---- " + clientConnectionEntry);
}
}
}
} catch (Exception e) {
LOGGER.fatal("MAIN LOOP: processClientConnectionsV2: ", e);
}
}
我最近重写了我的连接代码以试图解决挂起问题,我终于找到了根本原因 - 但我不确定如何解决它。
【问题讨论】:
-
“我终于找到了根本原因” - 如果你分享它可能会有所帮助
-
我做到了 - socket.close 是它挂起的地方 - 我还共享了堆栈跟踪。
-
根本原因是您正在关闭套接字,而另一个线程正在读取或写入它,并且该线程可能在套接字 I/O 本身上被阻塞。而不是这个闲置循环线程,您应该只在套接字上使用读取超时,并在处理该客户端的线程中读取
SocketTimeoutException时关闭它。 -
@MarquisofLorne 谢谢。我在看 setSoTimeout,但它似乎只有助于读取而不是写入。有什么想法吗?也不知道要设置多长时间。我来回读取 UTF 和 writeUTF 字符串,所以只有文本数据。
-
将它设置为与您当前的“不活动踢”使用相同的超时。没有什么能帮助你解决线程卡在写中的问题,除非同行赶上他的阅读。
标签: java sockets sslsocketfactory