簡體   English   中英

Netty Nio java中的通信

[英]Communication in Netty Nio java

我想在Netty nio中創建一個具有兩個客戶端和一個服務器的通信系統。 更具體地說,首先,我希望當兩個客戶端與服務器連接以從服務器發送消息時,之后能夠在兩個客戶端之間傳遞數據。 我正在使用此示例提供代碼 我可以在這里找到我對代碼的修改: 鏈接

似乎serverHandler中的channelRead在第一個客戶端被連接時工作,因此它總是返回1但是當連接第二個客戶端時不會更改為2.當兩個客戶端連接到服務器時,如何從服務器正確檢查? 如何從客戶端的主要功能中動態讀取此值? 那么這是讓兩個客戶溝通的最佳方式?

EDIT1:顯然似乎客戶端服務正在運行並直接關閉,因此每次運行新的NettyClient時都會連接,但之后關閉連接。 所以計數器總是從0到1。 正如我在下面的評論中所建議的那樣,我在相同的端口使用telnet測試它,並且計數器似乎正常增加,但是,NettyClient服務沒有。

EDIT2:看來我得到的問題來自future.addListener(ChannelFutureListener.CLOSE); 它位於ProcessingHandler class channelRead中。 當我評論它似乎代碼工作。 但是,我不確定評論的后果是什么。 此外,我希望從客戶端的主要功能檢查返回消息何時是特定的兩個。 如何,我可以創建一個等待來自服務器的特定消息的方法,同時它阻止主要功能。

 static EventLoopGroup workerGroup = new NioEventLoopGroup();
 static Promise<Object> promise = workerGroup.next().newPromise(); 
 public static void callClient() throws Exception {
    String host = "localhost";
    int port = 8080;
    try {
        Bootstrap b = new Bootstrap();
        b.group(workerGroup);
        b.channel(NioSocketChannel.class);
        b.option(ChannelOption.SO_KEEPALIVE, true);
        b.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(), new ClientHandler(promise));
            }
        });
        ChannelFuture f = b.connect(host, port).sync();
    } finally {
        //workerGroup.shutdownGracefully();
    }
}

我想在main函數內部調用方法並返回結果,當它為2時繼續使用主要功能。 但是,我無法在內部調用callClient,因為它將在同一個客戶端運行多次。

   callBack();
    while (true) {
        Object msg = promise.get();
        System.out.println("Case1: the connected clients is not two");
        int ret = Integer.parseInt(msg.toString());
        if (ret == 2){
            break;
        }
    }
   System.out.println("Case2: the connected clients is two");
   // proceed with the main functionality

如何更新第一個客戶端的promise變量。 當我運行兩個客戶端時,對於第一個客戶端,我總是收到消息:

案例1:連接的客戶端不是兩個

似乎承諾沒有正常更新,而對於第二個客戶我總是收到:

案例2:連接的客戶端是兩個

如果我的內存是正確的,ChannelHandlerContext是每個通道一個,它可以在它的管道中有多個ChannelHandler。 您的channels變量是處理程序類的實例變量。 並為每個連接創建一個新的 ProcessingHandler實例。 因此,一旦初始化,每個將在channels變量中具有一個且僅一個連接 - 它是為其創建的。

請參閱服務器代碼(NettyServer.java)中initChannel函數中的new ProcessingHandler() )。

您可以將channels變量設置為靜態,以便在ProcessingHandler實例之間共享它。 或者您可以在其他地方創建單個ProcessingHandler實例(例如,作為run()函數中的局部變量),然后將該實例傳遞給addLast調用而不是new ProcessingHandler()

為什么ChannelGroup頻道的大小始終是一個。 即使我連接更多客戶?

因為為每個新Channel (客戶端)調用子ChannelInitializer 在那里,您正在創建ProcessingHandler新實例,因此每個通道都會看到自己的ChannelGroup實例。

解決方案1 ​​ - 通道屬性

使用屬性並將其與Channel關聯。

在某處創建屬性(讓我們說在Constants類中):

public static final AttributeKey<ChannelGroup> CH_GRP_ATTR = 
       AttributeKey.valueOf(SomeClass.class.getName());

現在,創建將由ProcessingHandler的所有實例使用的ChannelGroup:

final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

在NettyServer中更新您的子ChannelInitializer

@Override
public void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(
        new RequestDecoder(), 
        new ResponseDataEncoder(), 
        new ProcessingHandler());

    ch.attr(Constants.CH_GRP_ATTR).set(channels);
}

現在您可以在處理程序中訪問ChannelGroup的實例,如下所示:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    final ChannelGroup channels = ctx.channel().attr(Constants.CH_GRP_ATTR).get();
    channels.add(ctx.channel());

這將起作用,因為每次新客戶端連接時,將使用與ChannelGroup相同的引用調用ChannelInitializer。

解決方案2 - 靜態字段

如果將ChannelGroup聲明為static,則所有類實例都將看到相同的ChannelGroup實例:

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

解決方案3 - 傳播共享實例

將參數引入ProcessingHandler構造函數:

private final ChannelGroup channels;
public ProcessingHandler(ChannelGroup chg) {
    this.channels = chg;
}

現在,你的NettyServer類中創建的實例ChannelGroup ,並將其傳播到ProcessingHandler構造函數:

final ChannelGroup channels = new 
      DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

@Override
public void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(
        new RequestDecoder(), 
        new ResponseDataEncoder(), 
        new ProcessingHandler(channels)); // <- here
}

就個人而言,我會選擇第一個解決方案,因為

  • 它明確地將ChannelGroup與Channel上下文相關聯
  • 您可以在其他處理程序中訪問相同的ChannelGroup
  • 您可以擁有多個服務器實例(在同一JVM中的不同端口上運行)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM