我想我已经观察了一段时间内ChannelId的变化。这是正常行为吗?
这就是为什么我认为:
我正在编写一个连接持续很长时间的游戏服务器。我想引用Channels,所以我将它们包装在Player对象中,并将那些带有ChannelId.asLongText()的对象作为HashMap中的键。这是HashMap:
private HashMap<String, Player> players = new HashMap<String, Player>();
public void registerPlayer(Channel c, String name, String password) {
Player p = new Player(c, name, password);
players.put(c.id().asLongText(), p);
try {
Future<CredentialCheckResult> future = workerTasks.submit(new AuthenticateCredentialsTask(context.getDatabase().getConnection(), c.id().asLongText(), name, password));
loginFutures.add(future);
} catch (SQLException e) {
System.out.println("["+Thread.currentThread().getName()+"] "+e.getMessage());
return;
}
System.out.println("["+Thread.currentThread().getName()+"] Registered and added worker for logging in "+name+" ("+c.id().asLongText()+") \t\t total players = "+players.size());
}
public void unregisterPlayer(String id) {
System.out.println("["+Thread.currentThread().getName()+"] Unregistering player ("+id+") \t\t total players = "+(players.size()-1));
Player p = players.get(id);
players.remove(id);
if (p == null) {
System.out.println("["+Thread.currentThread().getName()+"] P WAS NULL ("+id+") \t\t total players = "+players.size());
}
if (p.getChannel().isActive()) {
p.getChannel().close();
}
}
register和unregister方法从另一个名为GameEngine的线程运行。只有这个线程才能触及HashMap。
这是我的ServerHandler的样子,记住这一点:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
GameEngine game = context.getGameService();
Packet p = (Packet) msg;
if (p instanceof NewConnectionPacket) {
NewConnectionPacket packet = (NewConnectionPacket) msg;
game.queueLogic(new Runnable() {
@Override
public void run() {
game.registerPlayer(ctx.channel(), packet.getUsername(), packet.getPassword());
}
});
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
context.getGameService().queueLogic(new Runnable() {
@Override
public void run() {
context.getGameService().unregisterPlayer(ctx.channel().id().asLongText());
}
});
System.out.println("["+Thread.currentThread().getName()+"] "+" Kicked for inactive channel: "+ctx.channel().id().asLongText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("["+Thread.currentThread().getName()+"] "+cause.getMessage());
ctx.close();
}
GameEngine.queueLogic(Runnable)是线程安全的,只对GameEngine线程的工作进行排队。
当我运行服务器并登录(对所有用户使用相同的信誉)少数用户,并在客户端快速断开连接时,服务器似乎无法正确地从HashMap注销Player ,因为players.get(ChannelId.asLongText())将返回null。
以下是发生这种情况的会话中的日志:
May 21, 2018 10:33:23 PM com.mchange.v2.log.MLog
INFO: MLog clients using java 1.4+ standard logging.
May 21, 2018 10:33:23 PM com.mchange.v2.c3p0.C3P0Registry
INFO: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
[main] GameEngine starting up...
May 21, 2018 10:33:44 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hge0wf9v10qklm1j8rb36|3b94d659, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge0wf9v10qklm1j8rb36|3b94d659, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/onlinerp, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
Mon May 21 22:33:44 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Mon May 21 22:33:44 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Mon May 21 22:33:44 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
[GameEngine] Registered and added worker for logging in mads (cc2f71fffe2d38f4-0000287c-00000001-4b49379c0cc5629d-2b8fd125) total players = 1
[pool-1-thread-1] Looking up SQL data on player mads
[pool-1-thread-1] Finished looking up SQL data on player mads, result was VERIFIED
[GameEngine] Logged in succesfully mads
[GameEngine] Registered and added worker for logging in mads (cc2f71fffe2d38f4-0000287c-00000002-777e5a8cccc572e5-65acb968) total players = 2
[pool-1-thread-1] Looking up SQL data on player mads
[pool-1-thread-1] Finished looking up SQL data on player mads, result was VERIFIED
[GameEngine] Logged in succesfully mads
[GameEngine] Registered and added worker for logging in mads (cc2f71fffe2d38f4-0000287c-00000003-22a191d14cc5787b-f58eab6f) total players = 3
[pool-1-thread-1] Looking up SQL data on player mads
[pool-1-thread-1] Finished looking up SQL data on player mads, result was VERIFIED
[nioEventLoopGroup-3-3] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-3] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000003-22a191d14cc5787b-f58eab6f
[GameEngine] Unregistering player (cc2f71fffe2d38f4-0000287c-00000003-22a191d14cc5787b-f58eab6f) total players = 2
Mon May 21 22:33:51 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Mon May 21 22:33:51 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Mon May 21 22:33:51 CEST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
[GameEngine] Registered and added worker for logging in mads (cc2f71fffe2d38f4-0000287c-00000004-edfb71804cc58079-6c340605) total players = 3
[pool-1-thread-1] Looking up SQL data on player mads
[pool-1-thread-1] Finished looking up SQL data on player mads, result was VERIFIED
[nioEventLoopGroup-3-5] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-5] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000005-50468ea44cc5825e-52f26a37
[GameEngine] Unregistering player (cc2f71fffe2d38f4-0000287c-00000005-50468ea44cc5825e-52f26a37) total players = 2
[GameEngine] P WAS NULL (cc2f71fffe2d38f4-0000287c-00000005-50468ea44cc5825e-52f26a37) total players = 3
Exception in thread "GameEngine" java.lang.NullPointerException
at com.hydrozoa.onlinerp_server.game.GameEngine.unregisterPlayer(GameEngine.java:89)
at com.hydrozoa.onlinerp_server.net.RPServerHandler$2.run(RPServerHandler.java:49)
at com.hydrozoa.onlinerp_server.game.GameEngine.run(GameEngine.java:128)
at java.base/java.lang.Thread.run(Unknown Source)
[nioEventLoopGroup-3-4] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-4] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000004-edfb71804cc58079-6c340605
[nioEventLoopGroup-3-6] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-6] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000006-c3bd154facc587a0-3ef4f4b5
[nioEventLoopGroup-3-1] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-1] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000009-066e3818acc592cb-5618df97
[nioEventLoopGroup-3-8] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-8] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000008-65235126acc58ed4-10dc8b8f
[nioEventLoopGroup-3-2] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-2] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-0000000a-6034ea9b2cc59711-5b48e9f0
[nioEventLoopGroup-3-7] An existing connection was forcibly closed by the remote host
[nioEventLoopGroup-3-7] Kicked for inactive channel: cc2f71fffe2d38f4-0000287c-00000007-693ab465acc58a8a-8251ba2a
我也试过使用ChannelId对象作为键。我觉得我的智慧结束了。
我使用NioServerSocketChannel.class作为服务器,使用NioSocketChannel.class作为客户端。
如何以不随时间变化的方式引用频道?
顺便说一句,我使用的是Netty 4.1.25,这是我今天能找到的最新版本。
答案 0 :(得分:0)
事实证明我在所有情况下尝试从HashMap中删除Player对象时犯了一个错误。
客户端可能连接,然后空闲(导致它们不被注册,因为登录包发生了注册),最后断开连接。多数民众赞成在发生什么。
我的坏。
总而言之,ChannelId似乎可以安全使用,并且据我所知,它不会随着时间的推移而改变。