Netty NIO:读取接收到的消息

7
我正在使用Java中的Netty NIO开发客户端和服务器通信系统。我的代码可以在以下存储库中找到:链接。目前我有一个服务器和两个客户端,我正在将信息从服务器发送到客户端以及相反方向。
我现在需要解决的问题是:当我从第一个客户端接收到消息时,如何将该消息发送到第二个客户端(并且反过来,从客户端2发送到客户端1)。我该如何向特定客户端发送消息?
我注意到我的问题是由我尝试从服务器发送消息的方式引起的。以下是我的serverHandler中的代码:
for (Channel ch : channels1) {
    responseData.setIntValue(channels1.size());
    remoteAddr.add(ch.remoteAddress().toString());
    future = ch.writeAndFlush(responseData);
    //future.addListener(ChannelFutureListener.CLOSE);
    System.out.println("the requested data from the clients are: "+requestData);
    responseData1.setStringValue(requestData.toString());
    future = ch.writeAndFlush(responseData1);
    System.out.println(future);
}

默认情况下,我会发送关于连接数量的消息,但是当我从客户端1或2接收到消息时,我想将其发送回2和1。因此,我想在两个组件之间执行通信。如何从服务器发送到特定的客户端?我不确定如何将消息发送回客户端。


请问您能否更新源代码(GitHub)以便使用Maven进行构建? - Sergey Vyacheslavovich Brunov
我不熟悉Maven,不知道如何处理。 - konstantin
1个回答

7

一般方法

让我们来描述解决该问题的方法。

在服务器端接收数据时,使用通道的远程地址(java.net.SocketAddress Channel.remoteAddress() 方法)来识别客户端。

这种识别可以使用类似于 Map<SocketAddress, Client> 的映射来完成,其中 Client 类或接口应包含适当的客户端连接(通道)相关上下文,包括其 Channel。请确保及时更新映射:适当地处理“客户端已连接”和“客户端已断开连接”事件。

找到客户端后,您可以使用客户端连接(通道)映射向客户端发送适当的消息,但不包括当前发送消息的客户端。

此外,我建议您找一个使用 Netty 实现的好的聊天应用程序,并仔细研究它。

Netty 特定解决方案

让我们考虑服务器端实现,特别是 ProcessingHandler 类的实现。

它已经通过将活动通道表示为通道组来管理它们:

static final ChannelGroup channels1 =
    new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

让通道组保持最新

当前实现处理“通道变为活动状态”事件以保持通道组最新:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    channels1.add(ctx.channel());
    // ...
}

但这只是其中的一部分:同样需要对“通道不活跃”事件进行对称处理。实现应该如下:

@Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
    channels1.remove(ctx.channel());
}

广播:将接收到的消息发送到除当前频道外的所有频道

为了实现所需的行为,只需通过引入以下适当检查来更新实现:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // ...

    for (Channel ch : channels1) {
        // Does `ch` represent the channel of the current sending client?
        if (ch.equals(ctx.channel())) {
            // Skip.
            continue;
        }

        // Send the message to the `ch` channel.
        // ...
    }

    // ...
}

发送和接收字符串问题

目前ResponseData类的功能尚未实现。

为了使客户端和服务器正常工作,需要进行以下草案更改。

  1. The ResponseData class: the getStringValue and toString methods should be corrected:

    String getStringValue() {
        return this.strValue;
    }
    
    @Override
    public String toString() {
        return intValue + ";" + strValue;
    }
    
  2. The ResponseDataEncoder class: it should use the string value:

    private final Charset charset = Charset.forName("UTF-8");
    
    @Override
    protected void encode(final ChannelHandlerContext ctx, final ResponseData msg, final ByteBuf out) throws Exception {
        out.writeInt(msg.getIntValue());
        out.writeInt(msg.getStringValue().length());
        out.writeCharSequence(msg.getStringValue(), charset);
    }
    
  3. The ResponseDataDecoder class: it should use the string value:

    private final Charset charset = Charset.forName("UTF-8");
    
    @Override
    protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) throws Exception {
        ResponseData data = new ResponseData();
        data.setIntValue(in.readInt());
        int strLen = in.readInt();
        data.setStringValue(in.readCharSequence(strLen, charset).toString());
        out.add(data);
    }
    
  4. The ClientHandler class: it should correctly receive and handle the message:

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        final ResponseData responseData = (ResponseData) msg;
        System.out.println("The message sent from the server " + responseData);
        update.accept(responseData.getIntValue());
    }
    

参考资料

  1. Netty文档:SecureChat——基于TLS的聊天服务器,源自Telnet示例。特别是SecureChatServerHandler的实现。
  2. 《Netty实战》(ISBN-13: 978-1617291470),Norman Maurer, Marvin Allen Wolfthal,《第3部分——网络协议》,子章节《12.2我们的WebSocket应用程序》。涵盖了“基于浏览器的聊天应用程序”的实现。

1
我使用了您的示例代码。看起来服务器通常会发送连接通道的大小信息,但是对于responseData1.setStringValue(requestData.toString());的信息始终为零。 - konstantin
1
我认为我的代码只处理整数作为响应,但不确定如何以同样的方式处理字符串。 - konstantin
1
在这种情况下,我的服务器处理程序无法处理该情况。我从服务器接收字符串,但由于编码器和解码器类是为整数设计的,因此无法对它们进行处理并将它们发送回客户端。我不确定如何处理字符串的情况。 - konstantin
1
实际上,这一直是我的问题。我可以发送信息,但由于它尝试读取整数而不是字符串,因此始终为零。 - konstantin
1
好的,虽然有些愚蠢,但现在我又遇到了另一个问题,因为我想首先发送连接通道的数量,否则我的游戏无法正常工作。所以我首先想要检查已连接的客户端是否为两个,然后再交换信息。现在看起来我可以发送字符串,但无法正确发送已连接客户端的数量。 - konstantin
显示剩余18条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接