From 578bd70621763f56fc3027bf1d49a9b049dc9bee Mon Sep 17 00:00:00 2001 From: ravengerUA Date: Tue, 26 Sep 2017 08:34:42 +0300 Subject: [PATCH 1/5] Improve Streams IO docs --- .../streams/workingwithstreamingio.md | 49 +++++++ .../DocsExamples/Streams/StreamTcpDocTests.cs | 137 ++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs diff --git a/docs/articles/streams/workingwithstreamingio.md b/docs/articles/streams/workingwithstreamingio.md index 407ce83584f..00c5d8b4ee8 100644 --- a/docs/articles/streams/workingwithstreamingio.md +++ b/docs/articles/streams/workingwithstreamingio.md @@ -4,6 +4,55 @@ title: Working with streaming IO --- # Working with streaming IO +Akka Streams provides a way of handling File IO and TCP connections with Streams. While the general approach is very similar to the [Actor based TCP handling using Akka IO](xref:akka-io), by using Akka Streams you are freed of having to manually react to back-pressure signals, as the library does it transparently for you. + +## Streaming TCP + +### Accepting connections: Echo Server +In order to implement a simple EchoServer we bind to a given address, which returns a `Source>`, which will emit an `IncomingConnection` element for each new connection that the Server s`hould handle: + +[!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=echo-server-simple-bind)] + +> TODO: Pic1 + +Next, we simply handle each incoming connection using a `Flow` which will be used as the processing stage to handle and emit `ByteString` from and to the TCP Socket. Since one `ByteString` does not have to necessarily correspond to exactly one line of text (the client might be sending the line in chunks) we use the `Framing.Delimiter` helper to chunk the inputs up into actual lines of text. The last boolean argument indicates that we require an explicit line ending even for the last message before the connection is closed. In this example we simply add exclamation marks to each incoming text message and push it through the flow: + +[!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=echo-server-simple-handle)] + +Notice that while most building blocks in Akka Streams are reusable and freely shareable, this is not the case for the incoming connection Flow, since it directly corresponds to an existing, already accepted connection its handling can only ever be materialized once. + +Closing connections is possible by cancelling the incoming connection `Flow` from your server logic (e.g. by connecting its downstream to a `Sink.Cancelled` and its upstream to a `Source.Empty`). It is also possible to shut down the server’s socket by cancelling the `IncomingConnection` source `connections`. + +We can then test the TCP server by sending data to the TCP Socket using netcat: + +> TODO: Pic2 + +### Connecting: REPL Client + +In this example we implement a rather naive Read Evaluate Print Loop client over TCP. Let’s say we know a server has exposed a simple command line interface over TCP, and would like to interact with it using Akka Streams over TCP. To open an outgoing connection socket we use the `OutgoingConnection` method: + +[!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=repl-client)] + +The `repl` flow we use to handle the server interaction first prints the servers response, then awaits on input from the command line (this blocking call is used here just for the sake of simplicity) and converts it to a `ByteString` which is then sent over the wire to the server. Then we simply connect the TCP pipeline to this processing stage–at this point it will be materialized and start processing data once the server responds with an initial message. + +A resilient REPL client would be more sophisticated than this, for example it should split out the input reading into a separate `SelectAsync` step and have a way to let the server write more data than one `ByteString` chunk at any given time, these improvements however are left as exercise for the reader. + +### Avoiding deadlocks and liveness issues in back-pressured cycles +When writing such end-to-end back-pressured systems you may sometimes end up in a situation of a loop, in which either side is waiting for the other one to start the conversation. One does not need to look far to find examples of such back-pressure loops. In the two examples shown previously, we always assumed that the side we are connecting to would start the conversation, which effectively means both sides are back-pressured and can not get the conversation started. There are multiple ways of dealing with this which are explained in depth in [Graph cycles, liveness and deadlocks](xref:streams-working-with-graphs#graph-cycles-liveness-and-deadlocks), however in client-server scenarios it is often the simplest to make either side simply send an initial message. + +> [!NOTE] +> In case of back-pressured cycles (which can occur even between different systems) sometimes you have to decide which of the sides has start the conversation in order to kick it off. This can be often done by injecting an initial message from one of the sides–a conversation starter. + +To break this back-pressure cycle we need to inject some initial message, a “conversation starter”. First, we need to decide which side of the connection should remain passive and which active. Thankfully in most situations finding the right spot to start the conversation is rather simple, as it often is inherent to the protocol we are trying to implement using Streams. In chat-like applications, which our examples resemble, it makes sense to make the Server initiate the conversation by emitting a “hello” message: + +[!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=welcome-banner-chat-server)] + +To emit the initial message we merge a `Source` with a single element, after the command processing but before the framing and transformation to `ByteString` this way we do not have to repeat such logic. + +In this example both client and server may need to close the stream based on a parsed command - `BYE` in the case of the server, and `q` in the case of the client. This is implemented by taking from the stream until `q` and and concatenating a `Source` with a single `BYE` element which will then be sent after the original source completed. + +### Using framing in your protocol +Streaming transport protocols like TCP just pass streams of bytes, and does not know what is a logical chunk of bytes from the application's point of view. Often when implementing network protocols you will want to introduce your own framing. This can be done in two ways: An end-of-frame marker, e.g. end line `\n`, can do framing via `Framing.Delimiter`. Or a length-field can be used to build a framing protocol. ## Streaming File IO diff --git a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs new file mode 100644 index 00000000000..2291ad022bf --- /dev/null +++ b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs @@ -0,0 +1,137 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Akka.Streams; +using Akka.Streams.Dsl; +using Akka.TestKit.Xunit2; +using Xunit; +using Xunit.Abstractions; +using Akka.Actor; +using Akka.IO; +using Tcp = Akka.Streams.Dsl.Tcp; +using TcpExt = Akka.Streams.Dsl.TcpExt; +using Akka.Util.Internal; + +namespace DocsExamples.Streams +{ + public class StreamTcpDocTests : TestKit + { + private ActorMaterializer Materializer { get; } + + public StreamTcpDocTests(ITestOutputHelper output) + : base("", output) + { + Materializer = Sys.Materializer(); + } + + [Fact] + public async Task Simple_server_connection_must_bind_and_unbind() + { + + #region echo-server-simple-bind + var tcp = new TcpExt(Sys.AsInstanceOf()); + Tcp.ServerBinding binding = await tcp + .Bind("127.0.0.1", 8888) + .To(Sink.Ignore()) + .Run(Materializer); + + // close server after everything is done + await binding.Unbind(); + #endregion + } + + [Fact] + public void Simple_server_connection_must_handle_connection() + { + #region echo-server-simple-handle + var tcp = new TcpExt(Sys.AsInstanceOf()); + Source> connections = + tcp.Bind("127.0.0.1", 8888); + + connections.RunForeach(connection => + { + Console.WriteLine($"New connection from: {connection.RemoteAddress}"); + + var echo = Flow.Create() + .Via(Framing.Delimiter( + ByteString.FromString("\n"), + maximumFrameLength: 256, + allowTruncation: true)) + .Select(c => c.ToString()) + .Select(c => c + "!!!\n") + .Select(ByteString.FromString); + + connection.HandleWith(echo, Materializer); + }, Materializer); + #endregion + } + + [Fact] + public void Simple_server_connection_must_REPL() + { + var tcp = new TcpExt(Sys.AsInstanceOf()); + #region repl-client + var connection = tcp.OutgoingConnection("127.0.0.1", 8888); + + var replParser = Flow.Create().TakeWhile(c => c != "q") + .Concat(Source.Single("BYE")) + .Select(elem => ByteString.FromString($"{elem}\n")); + + var repl = Flow.Create() + .Via(Framing.Delimiter( + ByteString.FromString("\n"), + maximumFrameLength: 256, + allowTruncation: true)) + .Select(c => c.ToString()) + .Select(text => + { + Console.WriteLine($"Server: {text}"); + return text; + }) + .Select(text => + { + return Console.ReadLine(); // TODO: implement + }) + .Via(replParser); + + connection.Join(repl).Run(Materializer); + #endregion + } + + [Fact] + public void Simple_server_must_initial_server_banner_echo_server() + { + var tcp = new TcpExt(Sys.AsInstanceOf()); + var connections = tcp.Bind("127.0.0.1", 8888); + var serverProbe = CreateTestProbe(); + + #region welcome-banner-chat-server + connections.RunForeach(connection => + { + var commandParser = Flow.Create().TakeWhile(c => c != "BYE").Select(c => c + "!"); + + var welcomeMessage = $"Welcome to: {connection.LocalAddress}, you are: {connection.RemoteAddress}!"; + var welcome = Source.Single(welcomeMessage); + + var serverLogic = Flow.Create() + .Via(Framing.Delimiter( + ByteString.FromString("\n"), + maximumFrameLength: 256, + allowTruncation: true)) + .Select(c => c.ToString()) + .Select(command => + { + serverProbe.Tell(command); + return command; + }) + .Via(commandParser) + .Merge(welcome) + .Select(c => c + "\n") + .Select(ByteString.FromString); + + connection.HandleWith(serverLogic, Materializer); + }, Materializer); + #endregion + } + } +} From 619a730bb01624be6c4b739b937b33c730604390 Mon Sep 17 00:00:00 2001 From: ravengerUA Date: Wed, 27 Sep 2017 11:52:38 +0300 Subject: [PATCH 2/5] fixed remarks --- .../streams/workingwithstreamingio.md | 14 +- docs/examples/DocsExamples.sln | 145 +++++++++++++++++- .../DocsExamples/Streams/StreamTcpDocTests.cs | 38 +++-- docs/images/tcp-stream-bind.png | Bin 0 -> 14554 bytes docs/images/tcp-stream-run.png | Bin 0 -> 40277 bytes src/core/Akka.Streams/Dsl/Tcp.cs | 2 + 6 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 docs/images/tcp-stream-bind.png create mode 100644 docs/images/tcp-stream-run.png diff --git a/docs/articles/streams/workingwithstreamingio.md b/docs/articles/streams/workingwithstreamingio.md index 00c5d8b4ee8..f72b3fcf561 100644 --- a/docs/articles/streams/workingwithstreamingio.md +++ b/docs/articles/streams/workingwithstreamingio.md @@ -13,7 +13,7 @@ In order to implement a simple EchoServer we bind to a given address, which retu [!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=echo-server-simple-bind)] -> TODO: Pic1 +![tcp stream bind](/images/tcp-stream-bind.png) Next, we simply handle each incoming connection using a `Flow` which will be used as the processing stage to handle and emit `ByteString` from and to the TCP Socket. Since one `ByteString` does not have to necessarily correspond to exactly one line of text (the client might be sending the line in chunks) we use the `Framing.Delimiter` helper to chunk the inputs up into actual lines of text. The last boolean argument indicates that we require an explicit line ending even for the last message before the connection is closed. In this example we simply add exclamation marks to each incoming text message and push it through the flow: @@ -21,11 +21,17 @@ Next, we simply handle each incoming connection using a `Flow` which will be use Notice that while most building blocks in Akka Streams are reusable and freely shareable, this is not the case for the incoming connection Flow, since it directly corresponds to an existing, already accepted connection its handling can only ever be materialized once. -Closing connections is possible by cancelling the incoming connection `Flow` from your server logic (e.g. by connecting its downstream to a `Sink.Cancelled` and its upstream to a `Source.Empty`). It is also possible to shut down the server’s socket by cancelling the `IncomingConnection` source `connections`. +Closing connections is possible by cancelling the incoming connection `Flow` from your server logic (e.g. by connecting its downstream to a `Sink.Cancelled` and its upstream to a `Source.Empty`). It is also possible `to shut down the server’s socket by cancelling the `IncomingConnection` source `connections`. -We can then test the TCP server by sending data to the TCP Socket using netcat: +[!code-csharp[StreamTcpDocTests.cs](../../examples/DocsExamples/Streams/StreamTcpDocTests.cs?name=close-incoming-connection)] -> TODO: Pic2 +We can then test the TCP server by sending data to the TCP Socket using `netcat` (on Windows it is possible to use Linux Subsystem for Windows): +``` +echo -n "Hello World" | netcat 127.0.0.1 8888 +Hello World!!! +``` + +![tcp stream run](/images/tcp-stream-run.png) ### Connecting: REPL Client diff --git a/docs/examples/DocsExamples.sln b/docs/examples/DocsExamples.sln index e1a29dbeb33..8867487bd72 100644 --- a/docs/examples/DocsExamples.sln +++ b/docs/examples/DocsExamples.sln @@ -1,11 +1,31 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.14 +VisualStudioVersion = 15.0.26730.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocsExamples", "DocsExamples\DocsExamples.csproj", "{47E0257B-23D0-4D0E-B567-4DFCC3B2B2E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tutorials", "Tutorials\Tutorials.csproj", "{3C3559CC-D45D-4621-BB81-FCD6E7B38B4A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tutorials", "Tutorials\Tutorials.csproj", "{3C3559CC-D45D-4621-BB81-FCD6E7B38B4A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka", "..\..\src\core\Akka\Akka.csproj", "{E02A1282-A92D-4958-83BD-BA887843BB3E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Internal", "Internal", "{8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Streams", "..\..\src\core\Akka.Streams\Akka.Streams.csproj", "{BE36174C-0FC4-4A90-B12E-A1FEB1498244}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence", "..\..\src\core\Akka.Persistence\Akka.Persistence.csproj", "{9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.TestKit", "..\..\src\core\Akka.TestKit\Akka.TestKit.csproj", "{D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Cluster.Tools", "..\..\src\contrib\cluster\Akka.Cluster.Tools\Akka.Cluster.Tools.csproj", "{D25C1249-6732-425E-BE76-72A0E5351AAB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.TestKit.Xunit2", "..\..\src\contrib\testkits\Akka.TestKit.Xunit2\Akka.TestKit.Xunit2.csproj", "{E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Cluster", "..\..\src\core\Akka.Cluster\Akka.Cluster.csproj", "{3A67944B-05B5-410C-93C7-654DC91E7F1C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Remote", "..\..\src\core\Akka.Remote\Akka.Remote.csproj", "{E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{62B898A3-7448-4394-A9AA-2996EAD0EEB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -41,8 +61,129 @@ Global {3C3559CC-D45D-4621-BB81-FCD6E7B38B4A}.Release|x64.Build.0 = Release|Any CPU {3C3559CC-D45D-4621-BB81-FCD6E7B38B4A}.Release|x86.ActiveCfg = Release|Any CPU {3C3559CC-D45D-4621-BB81-FCD6E7B38B4A}.Release|x86.Build.0 = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|x64.ActiveCfg = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|x64.Build.0 = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|x86.ActiveCfg = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Debug|x86.Build.0 = Debug|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|Any CPU.Build.0 = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|x64.ActiveCfg = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|x64.Build.0 = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|x86.ActiveCfg = Release|Any CPU + {E02A1282-A92D-4958-83BD-BA887843BB3E}.Release|x86.Build.0 = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|x64.ActiveCfg = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|x64.Build.0 = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|x86.ActiveCfg = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Debug|x86.Build.0 = Debug|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|Any CPU.Build.0 = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|x64.ActiveCfg = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|x64.Build.0 = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|x86.ActiveCfg = Release|Any CPU + {BE36174C-0FC4-4A90-B12E-A1FEB1498244}.Release|x86.Build.0 = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|x64.Build.0 = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Debug|x86.Build.0 = Debug|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|Any CPU.Build.0 = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|x64.ActiveCfg = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|x64.Build.0 = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|x86.ActiveCfg = Release|Any CPU + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B}.Release|x86.Build.0 = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|x64.Build.0 = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Debug|x86.Build.0 = Debug|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|Any CPU.Build.0 = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|x64.ActiveCfg = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|x64.Build.0 = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|x86.ActiveCfg = Release|Any CPU + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C}.Release|x86.Build.0 = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|x64.ActiveCfg = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|x64.Build.0 = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|x86.ActiveCfg = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Debug|x86.Build.0 = Debug|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|Any CPU.Build.0 = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|x64.ActiveCfg = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|x64.Build.0 = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|x86.ActiveCfg = Release|Any CPU + {D25C1249-6732-425E-BE76-72A0E5351AAB}.Release|x86.Build.0 = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|x64.ActiveCfg = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|x64.Build.0 = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|x86.ActiveCfg = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Debug|x86.Build.0 = Debug|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|Any CPU.Build.0 = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|x64.ActiveCfg = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|x64.Build.0 = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|x86.ActiveCfg = Release|Any CPU + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C}.Release|x86.Build.0 = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|x64.Build.0 = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Debug|x86.Build.0 = Debug|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|Any CPU.Build.0 = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|x64.ActiveCfg = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|x64.Build.0 = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|x86.ActiveCfg = Release|Any CPU + {3A67944B-05B5-410C-93C7-654DC91E7F1C}.Release|x86.Build.0 = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|x64.Build.0 = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Debug|x86.Build.0 = Debug|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|Any CPU.Build.0 = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x64.ActiveCfg = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x64.Build.0 = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x86.ActiveCfg = Release|Any CPU + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x86.Build.0 = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x64.ActiveCfg = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x64.Build.0 = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x86.ActiveCfg = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x86.Build.0 = Debug|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|Any CPU.Build.0 = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x64.ActiveCfg = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x64.Build.0 = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x86.ActiveCfg = Release|Any CPU + {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E02A1282-A92D-4958-83BD-BA887843BB3E} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {BE36174C-0FC4-4A90-B12E-A1FEB1498244} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {9E25D89B-0516-4C2A-A976-36A4DB2FEE3B} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {D5CA0DCD-7E61-4D59-B3C1-45797BFDE65C} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {D25C1249-6732-425E-BE76-72A0E5351AAB} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {E9DAAAA6-2638-4388-9D11-8ADDC6A01C9C} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {3A67944B-05B5-410C-93C7-654DC91E7F1C} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA} = {8FF8A4B3-FCC0-4892-B5FF-F11C827AB1F3} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {76767832-3C78-4846-B241-FBCCD4996F8D} + EndGlobalSection EndGlobal diff --git a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs index 2291ad022bf..348ed1e4564 100644 --- a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs +++ b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Akka; using Akka.Streams; using Akka.Streams.Dsl; using Akka.TestKit.Xunit2; @@ -27,13 +28,14 @@ public StreamTcpDocTests(ITestOutputHelper output) [Fact] public async Task Simple_server_connection_must_bind_and_unbind() { - #region echo-server-simple-bind - var tcp = new TcpExt(Sys.AsInstanceOf()); - Tcp.ServerBinding binding = await tcp - .Bind("127.0.0.1", 8888) - .To(Sink.Ignore()) - .Run(Materializer); + // define an incoming request processing logic + Flow echo = Flow.Create(); + + Tcp.ServerBinding binding = await Sys.TcpStream() + .BindAndHandle(echo, Materializer, "localhost", 9000); + + Console.WriteLine($"Server listening at {binding.LocalAddress}"); // close server after everything is done await binding.Unbind(); @@ -44,9 +46,8 @@ public async Task Simple_server_connection_must_bind_and_unbind() public void Simple_server_connection_must_handle_connection() { #region echo-server-simple-handle - var tcp = new TcpExt(Sys.AsInstanceOf()); Source> connections = - tcp.Bind("127.0.0.1", 8888); + Sys.TcpStream().Bind("127.0.0.1", 8888); connections.RunForeach(connection => { @@ -66,12 +67,26 @@ public void Simple_server_connection_must_handle_connection() #endregion } + [Fact] + public void Simple_server_connection_must_close_incoming_connection() + { + Source> connections = + Sys.TcpStream().Bind("127.0.0.1", 8888); + + connections.RunForeach(connection => + { + #region close-incoming-connection + var closed = Flow.FromSinkAndSource(Sink.Cancelled(), Source.Empty()); + connection.HandleWith(closed, Materializer); + #endregion + }, Materializer); + } + [Fact] public void Simple_server_connection_must_REPL() { - var tcp = new TcpExt(Sys.AsInstanceOf()); #region repl-client - var connection = tcp.OutgoingConnection("127.0.0.1", 8888); + var connection = Sys.TcpStream().OutgoingConnection("127.0.0.1", 8888); var replParser = Flow.Create().TakeWhile(c => c != "q") .Concat(Source.Single("BYE")) @@ -101,8 +116,7 @@ public void Simple_server_connection_must_REPL() [Fact] public void Simple_server_must_initial_server_banner_echo_server() { - var tcp = new TcpExt(Sys.AsInstanceOf()); - var connections = tcp.Bind("127.0.0.1", 8888); + var connections = Sys.TcpStream().Bind("127.0.0.1", 8888); var serverProbe = CreateTestProbe(); #region welcome-banner-chat-server diff --git a/docs/images/tcp-stream-bind.png b/docs/images/tcp-stream-bind.png new file mode 100644 index 0000000000000000000000000000000000000000..2e51bb028097e5cd613927c58b9b0d0291c527b8 GIT binary patch literal 14554 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 eja~8I /// TBD + [InternalApi] public TcpExt(ExtendedActorSystem system) { _system = system; From 89d286d9a3233b153378bee55871a66b5885a74b Mon Sep 17 00:00:00 2001 From: ravengerUA Date: Wed, 27 Sep 2017 13:22:28 +0300 Subject: [PATCH 3/5] changed example --- .../DocsExamples/Streams/StreamTcpDocTests.cs | 83 +++++++++++-------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs index 348ed1e4564..9e9c112389b 100644 --- a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs +++ b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading.Tasks; using Akka; using Akka.Streams; @@ -9,9 +9,8 @@ using Xunit.Abstractions; using Akka.Actor; using Akka.IO; +using Akka.Util; using Tcp = Akka.Streams.Dsl.Tcp; -using TcpExt = Akka.Streams.Dsl.TcpExt; -using Akka.Util.Internal; namespace DocsExamples.Streams { @@ -82,37 +81,6 @@ public void Simple_server_connection_must_close_incoming_connection() }, Materializer); } - [Fact] - public void Simple_server_connection_must_REPL() - { - #region repl-client - var connection = Sys.TcpStream().OutgoingConnection("127.0.0.1", 8888); - - var replParser = Flow.Create().TakeWhile(c => c != "q") - .Concat(Source.Single("BYE")) - .Select(elem => ByteString.FromString($"{elem}\n")); - - var repl = Flow.Create() - .Via(Framing.Delimiter( - ByteString.FromString("\n"), - maximumFrameLength: 256, - allowTruncation: true)) - .Select(c => c.ToString()) - .Select(text => - { - Console.WriteLine($"Server: {text}"); - return text; - }) - .Select(text => - { - return Console.ReadLine(); // TODO: implement - }) - .Via(replParser); - - connection.Join(repl).Run(Materializer); - #endregion - } - [Fact] public void Simple_server_must_initial_server_banner_echo_server() { @@ -122,6 +90,7 @@ public void Simple_server_must_initial_server_banner_echo_server() #region welcome-banner-chat-server connections.RunForeach(connection => { + // server logic, parses incoming commands var commandParser = Flow.Create().TakeWhile(c => c != "BYE").Select(c => c + "!"); var welcomeMessage = $"Welcome to: {connection.LocalAddress}, you are: {connection.RemoteAddress}!"; @@ -146,6 +115,52 @@ public void Simple_server_must_initial_server_banner_echo_server() connection.HandleWith(serverLogic, Materializer); }, Materializer); #endregion + + var input = new AtomicReference>(new List { "Hello world", "What a lovely day", null }); + + string ReadLine(string prompt) + { + // TODO: implement it + switch (input.Value) + { + default: + return null; + } + } + + { + var connection = Sys.TcpStream().OutgoingConnection("127.0.0.1", 8888); + } + + { + #region repl-client + var connection = Sys.TcpStream().OutgoingConnection("127.0.0.1", 8888); + + var replParser = Flow.Create().TakeWhile(c => c != "q") + .Concat(Source.Single("BYE")) + .Select(elem => ByteString.FromString($"{elem}\n")); + + var repl = Flow.Create() + .Via(Framing.Delimiter( + ByteString.FromString("\n"), + maximumFrameLength: 256, + allowTruncation: true)) + .Select(c => c.ToString()) + .Select(text => + { + Output.WriteLine($"Server: {text}"); + return text; + }) + .Select(text => ReadLine("> ")) + .Via(replParser); + + connection.Join(repl).Run(Materializer); + #endregion + } + + serverProbe.ExpectMsg("Hello world", TimeSpan.FromSeconds(20)); + serverProbe.ExpectMsg("What a lovely day"); + serverProbe.ExpectMsg("BYE"); } } } From 8e8a8425d571d3ff8a49f8e76e3b62773a46d5b2 Mon Sep 17 00:00:00 2001 From: ravengerUA Date: Sat, 30 Sep 2017 18:57:25 +0300 Subject: [PATCH 4/5] Fixed an example --- docs/examples/DocsExamples.sln | 16 +--------------- .../DocsExamples/Streams/StreamTcpDocTests.cs | 13 +++---------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/docs/examples/DocsExamples.sln b/docs/examples/DocsExamples.sln index 8867487bd72..187f435cdb2 100644 --- a/docs/examples/DocsExamples.sln +++ b/docs/examples/DocsExamples.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.16 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocsExamples", "DocsExamples\DocsExamples.csproj", "{47E0257B-23D0-4D0E-B567-4DFCC3B2B2E1}" EndProject @@ -25,8 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Cluster", "..\..\src\c EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Remote", "..\..\src\core\Akka.Remote\Akka.Remote.csproj", "{E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{62B898A3-7448-4394-A9AA-2996EAD0EEB9}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -157,18 +155,6 @@ Global {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x64.Build.0 = Release|Any CPU {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x86.ActiveCfg = Release|Any CPU {E5AB69B8-8D2A-406A-AB00-FBA6FBA0D0BA}.Release|x86.Build.0 = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x64.ActiveCfg = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x64.Build.0 = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x86.ActiveCfg = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Debug|x86.Build.0 = Debug|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|Any CPU.Build.0 = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x64.ActiveCfg = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x64.Build.0 = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x86.ActiveCfg = Release|Any CPU - {62B898A3-7448-4394-A9AA-2996EAD0EEB9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs index 9e9c112389b..a8767ebf07c 100644 --- a/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs +++ b/docs/examples/DocsExamples/Streams/StreamTcpDocTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading.Tasks; using Akka; @@ -116,17 +117,9 @@ public void Simple_server_must_initial_server_banner_echo_server() }, Materializer); #endregion - var input = new AtomicReference>(new List { "Hello world", "What a lovely day", null }); + var input = new ConcurrentQueue(new[] { "Hello world", "What a lovely day" }); - string ReadLine(string prompt) - { - // TODO: implement it - switch (input.Value) - { - default: - return null; - } - } + string ReadLine(string prompt) => input.TryDequeue(out var cmd) ? cmd : "q"; { var connection = Sys.TcpStream().OutgoingConnection("127.0.0.1", 8888); From 099144a7cf4a5926fce6350d012b5e2b2a022aaa Mon Sep 17 00:00:00 2001 From: ravengerUA Date: Sat, 30 Sep 2017 20:31:28 +0300 Subject: [PATCH 5/5] api approval --- src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt index 6936cb04644..9d35779f325 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt @@ -1790,6 +1790,7 @@ namespace Akka.Streams.Dsl public class TcpExt : Akka.Actor.IExtension { protected readonly System.TimeSpan BindShutdownTimeout; + [Akka.Annotations.InternalApiAttribute()] public TcpExt(Akka.Actor.ExtendedActorSystem system) { } public Akka.Streams.Dsl.Source> Bind(string host, int port, int backlog = 100, System.Collections.Immutable.IImmutableList options = null, bool halfClose = False, System.Nullable idleTimeout = null) { } public System.Threading.Tasks.Task BindAndHandle(Akka.Streams.Dsl.Flow handler, Akka.Streams.IMaterializer materializer, string host, int port, int backlog = 100, System.Collections.Immutable.IImmutableList options = null, bool halfClose = False, System.Nullable idleTimeout = null) { }