2017-12-06 14 views
0

Java Nettyを学ぶだけで、ソケット7000経由で非常に単純なクライアント/サーバを作成できます。これは、telnet localhost 7000を使用してサーバーにメッセージを送信し、エコーメッセージを受信すると機能します。Java nettyクライアントはサーバにメッセージを送信できませんが、telnetでサーバを切断することができます

ただし、Java Nettyクライアントでは動作しません。サーバは何も受け取らない、クライアントは何も送らない?私はコンソールにいくつかの文章を追加しようとしました。ここで

は、この例のクラスである:私はEchoServerを実行すると、その後、私はEchoClientを実行

クライアント

public final class EchoClient { 

    public static void main(String[] args) throws Exception { 
     EventLoopGroup group = new NioEventLoopGroup(); 
     try { 
      Bootstrap bootstrap = new Bootstrap(); 
      bootstrap.group(group) 
        .channel(NioSocketChannel.class) 
        .option(ChannelOption.TCP_NODELAY, true) 
        .handler(new ChannelInitializer<SocketChannel>() { 
         @Override 
         public void initChannel(SocketChannel ch) throws Exception { 
          ch.pipeline().addLast(
            new LoggingHandler(LogLevel.TRACE), 
            new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()), 
            new StringEncoder(), 
            new StringDecoder(), 
            new EchoClientHandler()); 
         } 
        }); 

      // Start the connection attempt. 
      bootstrap.connect("localhost", 7000).sync().channel().closeFuture().sync(); 
      System.out.println("Message sent successfully."); 
     } finally { 
      group.shutdownGracefully(); 
     } 
    } 
} 

public class EchoClientHandler extends SimpleChannelInboundHandler<String> { 


    /** 
    * Constructor for the class 
    * 
    * @param message the message you want to transmit 
    */ 
    public EchoClientHandler() { 

    } 

    @Override 
    public void channelActive(ChannelHandlerContext ctx) throws Exception { 
     String message = "message from client."; 
     System.out.println("Sending message: " + message); 
     ctx.write(message); 
     ctx.flush(); 
     ctx.close(); 
    } 

    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
     System.out.println("Error caught in the communication service: " + cause); 
     ctx.close(); 
    } 


    @Override 
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { 
     System.out.println("Received message: " + msg); 
    } 
} 

サーバー

public final class EchoServer { 

    static final boolean SSL = System.getProperty("ssl") != null; 
    static final int PORT = Integer.parseInt(System.getProperty("port", "7000")); 

    public static void main(String[] args) throws Exception { 
     EventLoopGroup bossGroup = new NioEventLoopGroup(); 
     EventLoopGroup workerGroup = new NioEventLoopGroup(); 
     try { 
      ServerBootstrap serverBootstrap = new ServerBootstrap(); 
      serverBootstrap.group(bossGroup, workerGroup) 
        .channel(NioServerSocketChannel.class) 
        .handler(new LoggingHandler(LogLevel.TRACE)) 
        .childHandler(new ChannelInitializer<SocketChannel>() { 
         @Override 
         public void initChannel(SocketChannel ch) throws Exception { 
          ch.pipeline().addLast(
            new LoggingHandler(LogLevel.TRACE), 
            new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()), 
            new StringEncoder(), 
            new StringDecoder(), 
            new EchoServerHandler()); 
         } 
        }); 

      System.out.println("Server is listening on port 7000."); 

      // Start the server. 
      ChannelFuture channelFuture = serverBootstrap.bind("localhost", 7000).sync(); 

      // Wait until the server socket is closed. 
      channelFuture.channel().closeFuture().sync(); 


     } finally { 
      // Shut down all event loops to terminate all threads. 
      bossGroup.shutdownGracefully(); 
      workerGroup.shutdownGracefully(); 
     } 
    } 
} 



public class EchoServerHandler extends SimpleChannelInboundHandler<String> { 

    @Override 
    public void channelActive(ChannelHandlerContext ctx) throws Exception { 
     String message = "message from server."; 
     System.out.println("Sending message: " + message); 
     ctx.write(message); 
     ctx.flush(); 
    } 

    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
     // Close the connection when an exception is raised. 
     System.out.println("Error in receiving message."); 
     cause.printStackTrace(); 
     ctx.close(); 
    } 

    @Override 
    protected void channelRead0(ChannelHandlerContext ctx, String message) throws Exception { 
     System.out.println("Received message: " + message); 
     ctx.write(message); 
     ctx.flush(); 
     //ctx.close(); 
    } 
} 

だから、EchoClientからの出力は

です
Sending message: message from client. 
Message sent successfully. 
Then application stopped. 

そしてEchoServerからの出力は

Sending message: message from server. 
+0

localhost 7000にtelnetしてサーバーにメッセージを書き込み、コンソールにエコーメッセージを受信できます。 –

+0

クライアント側の 'channelActive()'の 'ctx.close()'を削除して、もう一度やり直してください。書き込みは非同期なので、書き込みが完了する前にチャンネルが閉じられる可能性があります。 – Apolozeus

+0

@Apolozeusはい、そうですが、このctx.close()はクライアントを停止しますが、クライアントが停止しても応答がありません。接続が同じシステム上にあり、非常に単純なメッセージ転送のときは、このようにすべきではありません。 –

答えて

2

コードでは、DelimiterBasedFrameDecoderハンドラがデコーダ/エンコーダの前に来ています。したがって、転送されたメッセージに予期された区切り文字がない場合(この場合は改行など)、クライアント/サーバーの両方は待機し続け、メッセージがまだ転送中であると考えます。したがって、問題を解決する方法は2つあります。クライアントとサーバーの両方

  • あなたがチャネルを介してメッセージを送信するたびにで

    1. 削除new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter())、新しい行区切りを追加してください。さもなければ、メッセージは他端からデコードできません。サンプルコードの下にクライアント

      から

    EchoClientHandlerクラス

    public class EchoClientHandler extends SimpleChannelInboundHandler<String> { 
    
        @Override 
        public void channelActive(ChannelHandlerContext ctx) throws Exception { 
         String message = "message from client."; 
         System.out.println("Sending message: " + message); 
         ctx.writeAndFlush(message + System.lineSeparator()); 
        } 
    
        @Override 
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
         System.out.println("Error caught in the communication service: " + cause); 
         ctx.close(); 
        } 
    
        @Override 
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { 
         System.out.println("Received message: " + msg); 
        } 
    } 
    

    EchoServerHandlerクラス

    public class EchoServerHandler extends SimpleChannelInboundHandler<String> { 
    
        @Override 
        public void channelActive(ChannelHandlerContext ctx) throws Exception { 
         String message = "message from server."; 
         System.out.println("Sending message: " + message); 
         ctx.writeAndFlush(message + System.lineSeparator()); 
        } 
    
        @Override 
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
         System.out.println("Error in receiving message."); 
         cause.printStackTrace(); 
         ctx.close(); 
        } 
    
        @Override 
        protected void channelRead0(ChannelHandlerContext ctx, String message) throws Exception { 
         System.out.println("Received message: " + message); 
         ctx.writeAndFlush(message + System.lineSeparator()); 
        } 
    } 
    

    サーバからの出力

    Server is listening on port 7000. 
    Sending message: message from server. 
    Received message: message from client. 
    

    出力を見つけてください。

    Sending message: message from client. 
    Received message: message from server. 
    Received message: message from client. 
    
  • +0

    答えを説明してください。なぜその行を削除する必要がありますか?その行は何ですか? –

    +0

    答えを更新する必要がある理由を説明してください。 –

    +0

    @Apolozeusなぜこの設定の新しいDelimiterBasedFrameDecoder(Integer.MAX_VALUE、Delimiters.lineDelimiter())が、あなたと同じようにlineSeperatorを明示的に追加する必要がないのか分かりません。しかし、それはあなたの一種です。 –

    0

    では、(LoggingHandlerまたはWiresharkのを使用して)あなたのクライアントとTelnetの間にあなたのプロトコルを比較あなたがこれを行う場合、あなたはそれらの2つのプロトコル間の1の大きな違いがあることがわかります。

    違いは、最後に新しい改行を付けずにメッセージを送信しているのに対し、telnetはメッセージを送信するという点です。

    これを解決するには、メッセージの最後に新しい改行を追加するか、これを自動的に行う新しいハンドラを作成するのがとても簡単です。あなたが作ることができる

    ctx.write(message + "\n"); 
    

    もう一つの改善ではなく、最初の書き込みのwriteAndFlushを呼び出し、その後、フラッシュされ、これはまた、コーディングしながら、あなたが中断取得するときに、他のメソッドの呼び出しを逃すのを防ぐことができます。

    +0

    はい、このブレークラインは、Nettyの代わりにクライアントhttps://alvinalexander.com/blog/post/java/simple-java-socket-client-class-programのJavaソケットを使用して新しいソースから知っていたものです。 ctx.writeAndFlush()の場合、実際にこれを何度もやったが動作しなかったので、ctx.write()に変更した。 ctx.flush()と同じです。 –

    関連する問題