连接上的Netty消息

时间:2018-04-05 21:50:15

标签: netty

我刚刚开始使用Netty并想慢慢接近它以真正了解事情的运作方式。基于独立的套接字测试程序,我有一个我想要开始的初始用例:

  • 从客户端连接到服务器时,立即发送消息并处理响应

够了......或者我认为。我已经看了好几天了,并没有真正理解为什么这不符合预期。

这是原始测试程序,它再次只是连接到远程服务器并立即将bytebuffer写入服务器。然后,服务器立即发送一个ack响应,该响应刚刚写入控制台。

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.net.*;
import java.util.StringTokenizer;
import java.nio.charset.StandardCharsets;

public class SocketTest {
  public static void main(String[] args) throws IOException {

    final String host = "remote host";
    final int port = 123456;

    final String msg = "hi";
    final byte sync = (byte)0x02;
    final short value1 = (short)70;
    final byte value2 = (byte)12;
    final byte value3 = (byte)0x4c;
    final int value4 = 1;
    final short value5 = (short)0x03;
    final short value6 = (short)0xffff;

    try {
      SocketChannel socketChannel
          = SocketChannel.open(new InetSocketAddress(host, port));

       ByteBuffer buf = ByteBuffer.allocate(15);
       buf.put(sync);
       buf.putShort(value1);
       buf.put(value2);
       buf.put(value3);
       buf.putInt(value4);
       buf.putShort(value5);
       buf.putShort(value6);
       buf.put(msg.getBytes(StandardCharsets.UTF_8));
       buf.flip();

       //write
       while(buf.hasRemaining()) {
         socketChannel.write(buf);
       }

       //read
       ByteBuffer inBuf = ByteBuffer.allocate(78);
       while (socketChannel.read(inBuf) > 0) {
           System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), new String(inBuf.array(), StandardCharsets.UTF_8));
       }
    } catch(UnknownHostException e) {
      System.exit(1);
    } catch(IOException ioe) {
      System.exit(1);
    } finally {
      socketChannel.close();
    }
  }
}

我接受了相同的测试并尝试使用Netty的这个基本用例:

NettySocketTest

import java.net.InetSocketAddress;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelFuture;
import io.netty.channel.socket.SocketChannel;

public class NettySocketTest {

  private final String host;
  private final int port;
  private SocketChannel channelInstance;

  public NettySocketTest(String host, int port) {
    this.host = host;
    this.port = port;
  }

  public void start() throws Exception {
    EventLoopGroup group = new NioEventLoopGroup();

    try {
      Bootstrap b = new Bootstrap();
      b.group(group)
        .channel(NioSocketChannel.class)
        .remoteAddress(new InetSocketAddress(host, port))
        .handler(new ChannelInitializer<SocketChannel>() {
          @Override
          public void initChannel (SocketChannel channel) throws Exception {
            channel.pipeline().addLast(new ClientTestHandler());
          }
        });

      ChannelFuture f = b.connect().sync();
      f.channel().closeFuture().sync();
    } catch(Exception e) {
      e.printStackTrace();
    }finally {
      group.shutdownGracefully().sync();
    }
  }
}

ClientTestHandler

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.ChannelFutureListener;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.ByteBuf;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class ClientTestHandler extends SimpleChannelInboundHandler {

  final String msg = "hi";

  @Override
  public void channelActive(final ChannelHandlerContext ctx) {
    //existing test code - [1]
    ByteBuffer buf = ByteBuffer.allocate(15);
    buf.put((byte)0x02);
    buf.putShort((short)70);
    buf.put((byte)12);
    buf.put((byte)0x4c);
    buf.putInt(101);
    buf.putShort((short)0x03);
    buf.putShort((short)0xffff);
    buf.put(msg.getBytes(StandardCharsets.UTF_8));
    buf.flip();

    //preferred implementation - [2]
    /**
    CompositeByteBuf messageBuf = Unpooled.compositeBuffer();
    ByteBuf syncBuf = ctx.alloc().buffer(1);
    syncBuf.writeByte((byte)0x02);

    ByteBuf headerBuf = ctx.alloc().buffer(12);
    headerBuf.writeShort((short)70);
    headerBuf.writeByte((byte)12);
    headerBuf.writeByte((byte)0x4c);
    headerBuf.writeInt(101);
    headerBuf.writeShort((short)0x03);
    headerBuf.writeShort((short)0xffff);

    ByteBuf bodyBuf = ctx.alloc().buffer(2);
    bodyBuf.writeBytes("hi".getBytes(StandardCharsets.UTF_8));
    messageBuf.addComponents(syncBuf, headerBuf, bodyBuf);
    **/

    //DOESN'T WORK - [3]
    final ChannelFuture f = ctx.writeAndFlush(buf);

    //ALSO DOESN'T WORK 
    //final ChannelFuture f = ctx.channel().writeAndFlush(buf);

      f.addListener(new ChannelFutureListener() {
          @Override
          public void operationComplete(ChannelFuture future) {
              assert f == future;
              ctx.close();
          }
      });
    }

  @Override
  public void channelRead0(ChannelHandlerContext ctx, Object resp) {
    ByteBuf msg = (ByteBuf) resp;
    System.out.println("Response received: " + msg.toString(StandardCharsets.UTF_8));
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

在ClientTestHandler中:

  1. 我使用现有的ByteBuffer代码并尝试按原样使用它,然后转换为Netty提供的便捷方法。见#2。
  2. 这是尝试使用CompositeByteBuf。 1或2都不起作用。
  3. 尝试使用ChannelHandlerContext写出消息,另一个尝试直接在上下文中使用频道,希望其中一个会违反回复。
  4. 我的期望是将消息发送到服务器,然后将响应打印到控制台。

    客户端代码执行:

    1. 成功启动/连接
    2. 成功调用channelActive
    3. 在ChannelFutureListener
    4. 上成功调用operationComplete
    5. 永远不会调用channelRead0方法
    6. 我故意没有在管道中添加任何其他东西,所以我可以简单地开始修改代码并在我去的时候学习。我将CompositeByteBuf还原为nio.ByteBuffer,所以我可以从我在其他测试中使用的相同代码开始。

      使用channelActive在连接时立即向服务器发送字节是否正确?任何人都可以帮助我理解我在这个基本用例中做错了什么以及为什么没有捕获响应(假设消息实际上已发送)?

1 个答案:

答案 0 :(得分:1)

您的问题是由于当您的处理程序添加到管道时,它已经连接。这与客户端不同,客户端仍然需要解析服务器的IP地址并打开与它的连接。

您应该覆盖p channelRegistered](https://netty.io/4.0/api/io/netty/channel/ChannelInboundHandlerAdapter.html#channelRegistered-io.netty.channel.ChannelHandlerContext-),并使用ctx.channel().isActive()上的if语句检查频道是否已处于活动状态,如果有效,请直接开始发送消息。

请注意,对于大多数协议,让客户端先发送一些内容会更好,因为在这种情况下,攻击者无法从随机端口快速获取您的应用程序名称和版本,并允许端口统一,在同一个端口上运行多个服务。