1

我正在为多人游戏实现简单的网络服务器。我只是想弄清楚Netty。

我通过 telnet 测试服务器。我所做的是将消息广播到所有频道。它工作顺利。我还可以在关闭事件时从地图中删除频道,这很好。

但问题是,如果其中一个客户端意外断开,在关闭回调之前,调用发送方断开通道的 messageReceived 回调。

如何正确忽略来自断开连接的客户端的消息?我使用StringBufferinmessagedReceived但对于这种情况StringBuffer.toString也不是正确的字符串。最后断开的通道向其他通道和自身广播无意义的消息,当接收通道本身抛出异常时Connection reset by peer ,这是正常的,因为通道本身目前不可用。

这是代码;

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {

            System.out.println();
            System.out.println("------------------");


            Channel current = e.getChannel();
            System.out.println("SenderChannel:"+current.getId());

            if(!current.isOpen())
                System.out.println("Not Open");

            ChannelBuffer buf = (ChannelBuffer) e.getMessage();

            StringBuffer sbs = new StringBuffer();

            while(buf.readable()) {
                sbs.append((char) buf.readByte());
            }

            String s = sbs.toString();


            System.out.println(s);

            String you = "You:" + s;
            String other = "Other:" + s;



            byte [] uResponse = you.getBytes();
            byte [] otherResponse = other.getBytes();


            Iterator iterator = channelList.entrySet().iterator();
            while(iterator.hasNext()){
                  Map.Entry pairs = (Map.Entry)iterator.next();

                  Integer key = (Integer)pairs.getKey();
                  Channel c = (Channel)pairs.getValue();

                  System.out.println("ReceiverChannel:"+c.getId());

                  if(key != current.getId())
                      c.write(ChannelBuffers.wrappedBuffer(otherResponse));
                  else
                      c.write(ChannelBuffers.wrappedBuffer(uResponse));

            }

     }


    @Override
    public void channelDisconnected(ChannelHandlerContext ctx,
            ChannelStateEvent e){

        Channel ch = e.getChannel();


            channelList.remove(ch.getId());
            System.out.println();
            System.out.println("*****************");
            System.out.println("DisconnectEvent:"+ch.getId());
            System.out.println("*****************");
            System.out.println();
            ch.close();

    }
4

4 回答 4

2

你无法以你想要的方式解决问题。如果存在网络问题,那么从技术上讲,发件人可以随时断开连接,例如

  • 一旦线程进入 messageReceived
  • 当您遍历 channelList
  • 在您遍历 channelList 但回显给发送者之后
  • 在您广播消息后

当 messageReceived 正在处理时,Netty 无法引发断开连接的事件,因为您正在引发事件的线程中运行(除非您的管道中有一个无序执行处理程序)。正确的解决方案实际上取决于您的应用程序。如果广播导致所有其他接收器响应,那么让服务器抑制任何发往不再连接的客户端的消息可能会更好/更容易。

此外,如果您真的要使用字符串,请查看 StringEncoder / StringDecoder。您的代码不能保证消息事件缓冲区包含完整的字符串。

于 2013-02-01T15:06:36.730 回答
1

只需在每次发送时尝试/捕获即可。如果其中一个失败,则关闭相应的通道。

于 2013-01-31T22:08:51.777 回答
1

如果这是用于多人游戏服务器,最好使用现有的 Netty 游戏服务器解决方案,如java game server。断开连接成为发送到会话的事件,并且由于它是事件驱动的,因此您可以编写自己的处理程序来决定是否在同一会话上接收更多事件。由于事件按 FIFO 顺序排队,因此如果发生断开连接,则无需继续进行后续广播。

于 2013-02-01T07:59:17.267 回答
0

我不是 Java 开发人员。但从套接字的角度来看,这些数据在缓冲区中或在用户断开连接之前发送。因此,当您处于接收阶段时,用户仍处于连接状态,并且恰好在接收用户完成时已断开连接。所以我认为防止这种事情的最好方法是在每次接收数据后检查用户是否仍然连接。

在 C# 中,我个人使用此代码来检查用户是否仍处于连接状态:

if (client.Poll(0, SelectMode.SelectRead))
{
    byte[] checkConn = new byte[1];
    if (client.Receive(checkConn, SocketFlags.Peek) == 0)
        return false;
}
return true;

我不确定Java和Netty(如果你的连接是TCP),但这是我使用的,这可以很容易地将它转换为Java。

于 2013-01-31T14:51:26.077 回答