在 netty 中接收请求并发送响应

Receive request and send response in netty

我有这样的服务器:

//...
ServerBootstrap networkServer = new ServerBootstrap();
networkServer
  .group(bossGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 128)
  .handler(new LoggingHandler(LogLevel.TRACE))
  .childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        public void initChannel(SocketChannel channel) throws Exception {
          ChannelPipeline p = channel.pipeline();
          p.addLast(requestHandler);
        }
      });
// ...

像这样的请求处理程序:

@Sharable
@Component
public class RequestHandler extends ChannelInboundHandlerAdapter {

private static final Logger logger = LogManager.getLogger(RequestHandler.class);

@Override
public void channelRead(ChannelHandlerContext ctx, Object obj) {
  // (1)  ctx.write(obj);

  ByteBuf in = (ByteBuf) obj;
  byte[] out = new byte[in.readableBytes()];
  try {
    int i = 0;
    while (in.readableBytes() > 0) {
      out[i++] = in.readByte();
    }

    Sproto.SProto msg = Sproto.SProto.parseFrom(out);

    Sproto.SProto response = Sproto.SProto.newBuilder()
      .setStatusCode(Common.ConfirmCodeStatus.OK)
      .setConnectResponse(Player.ConnectResponse.newBuilder())
      .build();

  // (2)   ctx.write(response);
  // (3)   ctx.write(obj);
  } catch (Exception ex) {
    logger.error(ex.getMessage(), ex);
  }
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
  ctx.flush();
}

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

当我通过以下方式发回请求时:

// (1)  ctx.write(obj);

它完美运行!

但是当我在解析请求后尝试发送 protobuf 响应时:

// (2)   ctx.write(response);

或者只发回请求:

// (3)   ctx.write(obj);

没用!我在客户端没有看到此响应。

这是怎么回事? 读取消息、执行一些逻辑并向客户端发送新响应的正确方法是什么?

您的管道在 ChannelInitializer 中初始化,只有一个处理程序:您的 requestHandler

此频道是一个 ChannelInboundHandlerAdapter 具有任何已定义类型的频道,因此它可以处理 Object 类型。因此,当收到数据包 (ByteBuf) 时,您的处理程序将被正确调用。

然后当你调用write( object )(你可能应该调用writeAndFlush())时,参数被发送到管道进行处理。如果参数是 ByteBuf,数据将按预期写入通道连接。但如果不是,就像你的对象是 protobuf 消息的实例一样,你需要一个出站处理程序到管道中以将这种对象转换为 ByteBuf.

Netty 提供了一个 Protobuf encoder/decoder handlers that you can add on your pipeline. But you can also extends ChannelOutboundHandlerAdapter 的实现,并编写了您自己的方式来序列化 protobuf 消息。

在您的具体情况下,您的管道应该类似于:

  • 一个处理 protobuf 消息的出站处理程序,将其转换为 ByteBuf 并在管道上向下游发送
  • 处理 ByteBuf 的入站处理程序,将其转换为 protobuf 消息并在管道上游向上游发送此消息
  • 处理 protobuf 消息并执行某些操作(如发送响应)的入站处理程序