[英]How to disconnect tcp connections to a java akka stream server?
I have simple TCP server that echoes back data received from the client connection.我有简单的 TCP 服务器,可以回显从客户端连接接收到的数据。
I want to do the following我想做以下事情
Code snippet that I am working with我正在使用的代码片段
// handles individual connections
final Sink<IncomingConnection, CompletionStage<Done>> handler = Sink.foreach(conn -> {
connectionCount++;
if (connectionCount < 2) {
System.out.println("Client connected from: " + conn.remoteAddress());
conn.handleWith(Flow.<ByteString>create(), actorSystem);
}
// Is not handling a good away to avoid the incoming connection?
// When I use netcat to connect to this server I see it saying
//connected and then the connection goes away when a second connection is attempted.
});
//Create the TCP server
Source<IncomingConnection, CompletionStage<ServerBinding>> source = Tcp.get(actorSystem)
.bind("127.0.0.1", 8888);
// connect the server source to the sink handler and run/materialize it
final CompletionStage<ServerBinding> bindingFuture = source.to(handler)
.run(actorSystem);
bindingFuture.handle((ServerBinding binding, Throwable exception) -> {
if (binding != null) {
System.out.println("Server started, listening on: " + binding.localAddress());
}
else {
System.err.println("Server could not bind to : " + exception.getMessage());
actorSystem.terminate();
}
return NotUsed.getInstance();
});
} }
Edit 1编辑 1
I kind of got problem 1 to work using a keepalive processing stage and echoes back the received data.我使用keepalive处理阶段解决了问题1并回显了接收到的数据。 So insert a flow with a keepalive that adds a bytestring to be sent back at regular intervals.因此,插入一个带有 keepalive 的流,该流添加要定期发回的字节串。 This is not perfect and I will try a fan in with the timer source that injects data in. Then there was this Flow.fromSinkAndSource that I tried, looked promising but could not get it to work.这并不完美,我将尝试使用注入数据的计时器源。然后我尝试了这个 Flow.fromSinkAndSource,看起来很有希望,但无法让它工作。 https://doc.akka.io/docs/akka/current/stream/operators/Flow/fromSinkAndSource.html https://doc.akka.io/docs/akka/current/stream/operators/Flow/fromSinkAndSource.html
What currently works is this change目前有效的是这种变化
ActorSystem actorSystem = ActorSystem.create(Behaviors.empty(), "actorSystem");
final ByteString keepAliveMessage = ByteString.fromString("KEEP ALIVE");
Flow<ByteString, ByteString, NotUsed> keepAliveInject = Flow.of(ByteString.class)
.keepAlive(Duration.ofSeconds(1), () -> keepAliveMessage);
final Sink<IncomingConnection, CompletionStage<Done>> handler = Sink.foreach(conn -> {
System.out.println("Client connected from: " + conn.remoteAddress());
conn.handleWith(keepAliveInject.via(Flow.<ByteString>create()), actorSystem);
});
This is how I have solved the some of my problems.这就是我如何解决我的一些问题。
Allowing N number of connections and sending a message on a client connection.允许 N 个连接并在客户端连接上发送消息。
The code can be run use multiple netcat clients to connect to it and see the behavior.可以使用多个 netcat 客户端连接到它并查看行为来运行代码。
package com.example;
import java.util.concurrent.CompletionStage;
import akka.Done;
import akka.NotUsed;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.javadsl.Behaviors;
import akka.stream.FlowShape;
import akka.stream.SinkShape;
import akka.stream.SourceShape;
import akka.stream.UniformFanInShape;
import akka.stream.UniformFanOutShape;
import akka.stream.javadsl.Concat;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.GraphDSL;
import akka.stream.javadsl.Partition;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.stream.javadsl.Tcp;
import akka.stream.javadsl.Tcp.IncomingConnection;
import akka.stream.javadsl.Tcp.ServerBinding;
import akka.util.ByteString;
/**
* We want to creat a TCP server listening on port 8888. Will allow N=2
* connections. Any more than 2 will be rejected. On client connect the server
* will send a "OnConnect" string. Server will maintain count of connections, if
* client connects connection count will be increments and if client disconnects
* connection count will be decremented. Server will behave as a echo server,
* responding back with data sent by the client.
*/
public class SimpleStream05 {
private static int connectionCount = 0;
private static int maxConnectioCount = 2;
private static void run() {
ActorSystem actorSystem = ActorSystem.create(Behaviors.empty(), "actorSystem");
Flow<ByteString, ByteString, NotUsed> serverLogic = Flow.fromGraph(GraphDSL.create(builder -> {
// We have our source that will send the onConnect message to the tcp client.
SourceShape<ByteString> onConnectSourceShape = builder
.add(Source.single(ByteString.fromString("OnConnect")));
// Incoming Connection that just echoes back what we received.
UniformFanInShape<ByteString, ByteString> concatShape = builder.add(Concat.create());
FlowShape<ByteString, ByteString> echoFlowShape = builder.add(Flow.of(ByteString.class).map(y -> y));
/*
* Connect the single source to concat and connect the echo flows output to
* concat. When a tcp connection comes in, this handler (flow) is called, concat
* will first take the input from the onConnectSourceShape which is a single and
* will emit just once and complete. Once onConnectSourceShape completes concat
* will take data from the second input echoFlowShape which is nothing but data
* from the tcp connection.
*/
builder.from(onConnectSourceShape).toFanIn(concatShape); // onConnectSourceShape -> concatShape
builder.from(echoFlowShape).toFanIn(concatShape); // echoFlowShape -> concatShape
return FlowShape.of(echoFlowShape.in(), concatShape.out());
}));
final Sink<IncomingConnection, CompletionStage<Done>> handler = Sink.foreach(conn -> {
System.out.println("Client connected from: " + conn.remoteAddress());
// conn.handleWith(serverLogic.via(Flow.<ByteString>create()), actorSystem);
conn.handleWith(
serverLogic.via(Flow.<ByteString>create()).watchTermination((prevMatValue, completionStage) -> {
completionStage.whenComplete((done, exc) -> {
connectionCount--;
System.out.println("Watch Termination called. Connection count:" + connectionCount);
if (done != null) {
System.out.println("The stream materialized " + prevMatValue.toString());
} else
System.out.println(exc.getMessage());
});
return prevMatValue;
}), actorSystem);
});
Flow<IncomingConnection, IncomingConnection, NotUsed> connectioncountFlow = Flow
.fromGraph(GraphDSL.create(builder -> {
SinkShape<IncomingConnection> sinkCancelled = builder.add(Sink.cancelled());
FlowShape<IncomingConnection, IncomingConnection> inFlowShape = builder
.add(Flow.of(IncomingConnection.class).map(conn -> {
connectionCount++;
return conn;
}));
UniformFanOutShape<IncomingConnection, IncomingConnection> partition = builder
.add(Partition.create(IncomingConnection.class, 2, param -> {
if (connectionCount > maxConnectioCount) {
connectionCount = maxConnectioCount;
System.out.println("Outlet 0 -> Sink.cancelled");
return 0;
}
System.out.println("Outlet 1 -> forward to handler");
return 1;
}));
builder.from(inFlowShape).toFanOut(partition);
builder.from(partition.out(0)).to(sinkCancelled);
return new FlowShape<>(inFlowShape.in(), partition.out(1));
}));
Source<IncomingConnection, CompletionStage<ServerBinding>> source = Tcp.get(actorSystem).bind("127.0.0.1",
8888);
CompletionStage<ServerBinding> bindingFuture = source.via(connectioncountFlow).to(handler).run(actorSystem);
bindingFuture.handle((binding, throwable) -> {
if (binding != null) {
System.out.println("Server started, listening on: " + binding.localAddress());
} else {
System.err.println("Server could not bind to : " + throwable.getMessage());
actorSystem.terminate();
}
return NotUsed.getInstance();
});
}
public static void main(String[] args) throws InterruptedException {
SimpleStream05.run();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.