Java 如何使用vert.x-rx创建反应式客户端-服务器TCP通信
我目前正在从事一个项目,该项目需要在外部系统和我将要编写的应用程序(Java)之间进行TCP通信。众所周知,使用常规NIO很容易实现这一点。然而,作为我正在进行的这个新项目的一部分,我必须使用Vert.x来提供TCP通信。请参阅下图: 在右边,我的应用程序作为TCP服务器运行,等待来自外部系统的连接,在左边。我读过,要创建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
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.xReadStream
,因此可以将其转换为可流动的:
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并将其转换为可流动的
。我会更新答案的。太棒了!非常感谢你对吉斯蒙特的帮助!