如何忽略来自断开连接通道的消息

时间:2013-01-31 14:11:08

标签: java sockets netty

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

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

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

如何正确忽略来自断开连接的客户端的消息? 我在StringBuffer中使用messagedReceived,但对于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 个答案:

答案 0 :(得分:2)

您无法以您希望的方式解决问题。如果存在网络问题,那么技术上发送方可以随时断开连接,例如

  • 一旦线程进入messageReceived
  • 在您通过channelList进行迭代时
  • 当您在通过channelList进行迭代但在回显给发件人之后
  • 播放消息后

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

另外,如果你真的要使用字符串,那么看一下StringEncoder / StringDecoder。您的代码无法保证消息事件缓冲区包含完整的字符串。

答案 1 :(得分:1)

只需在每次发送时放置一个try / catch。如果其中一个失败,请关闭相应的频道。

答案 2 :(得分:1)

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

答案 3 :(得分: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。