Java 如何使用vert.x-rx创建反应式客户端-服务器TCP通信

Java 如何使用vert.x-rx创建反应式客户端-服务器TCP通信,java,reactive-programming,vert.x,reactive,Java,Reactive Programming,Vert.x,Reactive,我目前正在从事一个项目,该项目需要在外部系统和我将要编写的应用程序(Java)之间进行TCP通信。众所周知,使用常规NIO很容易实现这一点。然而,作为我正在进行的这个新项目的一部分,我必须使用Vert.x来提供TCP通信。请参阅下图: 在右边,我的应用程序作为TCP服务器运行,等待来自外部系统的连接,在左边。我读过,要创建TCP并侦听连接,只需执行以下操作: NetServer server = vertx.createNetServer(); server.listen(1234, "loc

我目前正在从事一个项目,该项目需要在外部系统和我将要编写的应用程序(Java)之间进行TCP通信。众所周知,使用常规NIO很容易实现这一点。然而,作为我正在进行的这个新项目的一部分,我必须使用Vert.x来提供TCP通信。请参阅下图:

在右边,我的应用程序作为TCP服务器运行,等待来自外部系统的连接,在左边。我读过,要创建TCP并侦听连接,只需执行以下操作:

NetServer server = vertx.createNetServer();
server.listen(1234, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed to bind!");
  }
});
然而,当外部系统连接到我的应用程序并通过TCP发送EchoRequestMessages时,我不知道该如何处理。我的应用程序必须获取接收到的字节缓冲区,将其解码为EchoRequestMessage POJO,然后将EchoResponseMessage编码为字节缓冲区,以发送回外部系统


如何使用vert.x-rx对EchoRequestMessage的接收、解码、EchoResponseMessage的编码执行反应式编程,然后将其发送回外部系统,所有这些都在一个builder模式类型设置中完成。我读过关于观察和订阅的书,但我不知道该观察什么或订阅什么。非常感谢您的帮助。

要从套接字读取数据,您可以使用
RecordParser
。在套接字连接上,数据通常由换行符分隔:

RecordParser parser = RecordParser.newDelimited("\n", sock);
RecordParser
是一个Vert.x
ReadStream
,因此可以将其转换为可流动的

FlowableHelper.toFlowable(parser)
FlowableHelper.toFlowable(new LengthPrefixedStream(sock))
现在,如果可以从
缓冲区创建
EchoRequestMessage

public class EchoRequestMessage {
  private String message;

  public static EchoRequestMessage fromBuffer(Buffer buffer) {
    // Deserialize
  }

  public String getMessage() {
    return message;
   }
 }
public class EchoResponseMessage {
  private final String message;

  public EchoResponseMessage(String message) {
    this.message = message;
  }

  public Buffer toBuffer() {
    // Serialize;
  }
}
以及转换为缓冲区的
EchoResponseMessage

public class EchoRequestMessage {
  private String message;

  public static EchoRequestMessage fromBuffer(Buffer buffer) {
    // Deserialize
  }

  public String getMessage() {
    return message;
   }
 }
public class EchoResponseMessage {
  private final String message;

  public EchoResponseMessage(String message) {
    this.message = message;
  }

  public Buffer toBuffer() {
    // Serialize;
  }
}
您可以使用RxJava操作符实现echo服务器流:

vertx.createNetServer().connectHandler(sock -> {

  RecordParser parser = RecordParser.newDelimited("\n", sock);

  FlowableHelper.toFlowable(parser)
    .map(EchoRequestMessage::fromBuffer)
    .map(echoRequestMessage -> {
      return new EchoResponseMessage(echoRequestMessage.getMessage());
    })
    .subscribe(echoResponseMessage -> sock.write(echoResponseMessage.toBuffer()).write("\n"), throwable -> {
      throwable.printStackTrace();
      sock.close();
    }, sock::close);

}).listen(1234);
[编辑]如果协议中的消息不是以行分隔的,而是以长度为前缀,则可以创建自定义的
读取流

class LengthPrefixedStream implements ReadStream<Buffer> {
  final RecordParser recordParser;
  boolean prefix = false;

  private LengthPrefixedStream(ReadStream<Buffer> stream) {
    recordParser = RecordParser.newFixed(4, stream);
  }

  @Override
  public ReadStream<Buffer> exceptionHandler(Handler<Throwable> handler) {
    recordParser.exceptionHandler(handler);
    return this;
  }

  @Override
  public ReadStream<Buffer> handler(Handler<Buffer> handler) {
    if (handler == null) {
      recordParser.handler(null);
      return this;
    }
    recordParser.handler(buffer -> {
      if (prefix) {
        prefix = false;
        recordParser.fixedSizeMode(buffer.getInt(0));
      } else {
        prefix = true;
        recordParser.fixedSizeMode(4);
        handler.handle(buffer);
      }
    });
    return this;
  }

  @Override
  public ReadStream<Buffer> pause() {
    recordParser.pause();
    return this;
  }

  @Override
  public ReadStream<Buffer> resume() {
    recordParser.resume();
    return this;
  }

  @Override
  public ReadStream<Buffer> endHandler(Handler<Void> endHandler) {
    recordParser.endHandler(endHandler);
    return this;
  }
}

我发现使用RecordParser.fixedSizeMode()方法将传入的数据包聚合到一条消息相当复杂

我使用RecordParser.newDelimited。 如果您可以处理发送到TCP接收器的数据,那么您可以在发送的每条消息的末尾添加一个唯一的分隔符,而不是在每个消息的前面加上4字节的消息长度

代码示例:(我没有使用RxJava语法)

发送

byte[] serialized = SerializationUtils.serialize(o);
senderSocket.write(Buffer.buffer(serialized).appendString(MSG_DELIMITER));
System.out.println("I sent some bytes: " + serialized.length);
protected static String MSG_DELIMITER = "_I_Am_so0o_UNIQUEQE_";
protected Vertx vertx;
protected final RecordParser parser = RecordParser.newDelimited(MSG_DELIMITER, new Output());

public static main(String[] args) {
    this.vertx = Vertx.vertx();
    this.vertx.deployVerticle(new Receiver());
}

public class Receiver extends AbstractVerticle {
    @Override
    public void start() {

        NetServer receiver = this.vertx.createNetServer();

        receiver.connectHandler(socket ->

                socket.handler(parser::handle));

        receiver.listen(port, host, res -> {
            if (res.succeeded()) {
                System.out.println("Receiver is now listening!");
            } else {
                System.out.println("Failed to bind!");
            }
        });
    }

    @Override
    public void stop() {
        System.out.println("Receiver stopped");
    }
}

public class Output implements Handler<Buffer> {

    @Override
    public void handle(Buffer event) {
        try {
            E deserialized = (E) SerializationUtils.deserialize(event.getBytes());
            queue.put(deserialized);
            System.out.println("I received some bytes: " + event.length());

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
阅读

byte[] serialized = SerializationUtils.serialize(o);
senderSocket.write(Buffer.buffer(serialized).appendString(MSG_DELIMITER));
System.out.println("I sent some bytes: " + serialized.length);
protected static String MSG_DELIMITER = "_I_Am_so0o_UNIQUEQE_";
protected Vertx vertx;
protected final RecordParser parser = RecordParser.newDelimited(MSG_DELIMITER, new Output());

public static main(String[] args) {
    this.vertx = Vertx.vertx();
    this.vertx.deployVerticle(new Receiver());
}

public class Receiver extends AbstractVerticle {
    @Override
    public void start() {

        NetServer receiver = this.vertx.createNetServer();

        receiver.connectHandler(socket ->

                socket.handler(parser::handle));

        receiver.listen(port, host, res -> {
            if (res.succeeded()) {
                System.out.println("Receiver is now listening!");
            } else {
                System.out.println("Failed to bind!");
            }
        });
    }

    @Override
    public void stop() {
        System.out.println("Receiver stopped");
    }
}

public class Output implements Handler<Buffer> {

    @Override
    public void handle(Buffer event) {
        try {
            E deserialized = (E) SerializationUtils.deserialize(event.getBytes());
            queue.put(deserialized);
            System.out.println("I received some bytes: " + event.length());

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
protected static String MSG\u DELIMITER=“\u I\u Am\u so0o\u UNIQUEQE”;
受保护的Vertx-Vertx;
受保护的最终RecordParser parser=RecordParser.newDelimited(MSG_DELIMITER,new Output());
公共静态main(字符串[]args){
this.vertx=vertx.vertx();
这个.vertx.deployVerticle(新的接收器());
}
公共类接收器扩展AbstractVerticle{
@凌驾
公开作废开始(){
NetServer receiver=this.vertx.createNetServer();
receiver.connectHandler(套接字->
handler(解析器::handle));
接收器。侦听(端口、主机、res->{
如果(res.successed()){
System.out.println(“接收器正在监听!”);
}否则{
System.out.println(“绑定失败!”);
}
});
}
@凌驾
公共停车场(){
System.out.println(“接收器停止”);
}
}
公共类输出实现处理程序{
@凌驾
公共无效句柄(缓冲区事件){
试一试{
E反序列化=(E)SerializationUtils.deserialize(event.getBytes());
put(反序列化);
System.out.println(“我收到了一些字节:“+event.length()”);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}

Hi@tsegismont,谢谢你的回答。但是,我接收的数据不是由换行符分隔的。实际上没有使用分隔符。消息以包含消息长度的消息的前4个字节的格式发送。因此,消息的长度是可变的。我将如何使用RecordParser来解析这些可变长度的消息?@emjay然后我将创建自己的ReadStream并将其转换为可流动的
。我会更新答案的。太棒了!非常感谢你对吉斯蒙特的帮助!