How do I write a simple--bare minimum needed for it to work--test application that illustrates how to use IPC/Named Pipes?
For example, how would one write a console application where Program 1 says "Hello World" to Program 2 and Program 2 receives message and replies "Roger That" to Program 1.
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
StartServer();
Task.Delay(1000).Wait();
//Client
var client = new NamedPipeClientStream("PipesOfPiece");
client.Connect();
StreamReader reader = new StreamReader(client);
StreamWriter writer = new StreamWriter(client);
while (true)
{
string input = Console.ReadLine();
if (String.IsNullOrEmpty(input)) break;
writer.WriteLine(input);
writer.Flush();
Console.WriteLine(reader.ReadLine());
}
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("PipesOfPiece");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
writer.WriteLine(String.Join("", line.Reverse()));
writer.Flush();
}
});
}
}
}
For someone who is new to IPC and Named Pipes, I found the following NuGet package to be a great help.
GitHub: Named Pipe Wrapper for .NET 4.0
To use first install the package:
PS> Install-Package NamedPipeWrapper
Then an example server (copied from the link):
var server = new NamedPipeServer<SomeClass>("MyServerPipe");
server.ClientConnected += delegate(NamedPipeConnection<SomeClass> conn)
{
Console.WriteLine("Client {0} is now connected!", conn.Id);
conn.PushMessage(new SomeClass { Text: "Welcome!" });
};
server.ClientMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
{
Console.WriteLine("Client {0} says: {1}", conn.Id, message.Text);
};
server.Start();
Example client:
var client = new NamedPipeClient<SomeClass>("MyServerPipe");
client.ServerMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
{
Console.WriteLine("Server says: {0}", message.Text);
};
client.Start();
Best thing about it for me is that unlike the accepted answer here it supports multiple clients talking to a single server.
You can actually write to a named pipe using its name, btw.
Open a command shell as Administrator to get around the default "Access is denied" error:
echo Hello > \\.\pipe\PipeName
Linux dotnet core doesn't support namedpipes!
Try TcpListener if you deploy to Linux
This NamedPipe Client/Server code round trips a byte to a server.
Client writes byte
Server reads byte
Server writes byte
Client reads byte
DotNet Core 2.0 Server ConsoleApp
using System;
using System.IO.Pipes;
using System.Threading.Tasks;
namespace Server
{
class Program
{
static void Main(string[] args)
{
var server = new NamedPipeServerStream("A", PipeDirection.InOut);
server.WaitForConnection();
for (int i =0; i < 10000; i++)
{
var b = new byte[1];
server.Read(b, 0, 1);
Console.WriteLine("Read Byte:" + b[0]);
server.Write(b, 0, 1);
}
}
}
}
DotNet Core 2.0 Client ConsoleApp
using System;
using System.IO.Pipes;
using System.Threading.Tasks;
namespace Client
{
class Program
{
public static int threadcounter = 1;
public static NamedPipeClientStream client;
static void Main(string[] args)
{
client = new NamedPipeClientStream(".", "A", PipeDirection.InOut, PipeOptions.Asynchronous);
client.Connect();
var t1 = new System.Threading.Thread(StartSend);
var t2 = new System.Threading.Thread(StartSend);
t1.Start();
t2.Start();
}
public static void StartSend()
{
int thisThread = threadcounter;
threadcounter++;
StartReadingAsync(client);
for (int i = 0; i < 10000; i++)
{
var buf = new byte[1];
buf[0] = (byte)i;
client.WriteAsync(buf, 0, 1);
Console.WriteLine($#"Thread{thisThread} Wrote: {buf[0]}");
}
}
public static async Task StartReadingAsync(NamedPipeClientStream pipe)
{
var bufferLength = 1;
byte[] pBuffer = new byte[bufferLength];
await pipe.ReadAsync(pBuffer, 0, bufferLength).ContinueWith(async c =>
{
Console.WriteLine($#"read data {pBuffer[0]}");
await StartReadingAsync(pipe); // read the next data <--
});
}
}
}
Related
I'm creating a client server app using c# and what it does :
1- the client sends a msg then the server sends it back to the client
2- I want to check what the client send and do some process like if the client send "chrome"
the server checks it and open chrome.exe
I don't know why its not working here on my code:
when the client sends chrome it always shows client disconnected message
also i cant convert it to windows froms app its shows lots of errors
#server code!
using System;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;
using System.Text;
namespace consoleTcpServer {
class Program {
class ConnectionHandler {
private Socket client;
private NetworkStream ns;
private StreamReader reader;
private StreamWriter writer;
private static int connections = 0;
//The constructor take the accepted client as argument
public ConnectionHandler(Socket client) {
this.client = client;
}
public void HandleConnection() {
try {
ns = new NetworkStream(client);
reader = new StreamReader(ns);
writer = new StreamWriter(ns);
connections++;
Console.WriteLine("New client accepted: {0} active connections",
connections);
writer.WriteLine("Welcome to my server");
writer.Flush();
string input;
while ((input = reader.ReadLine()).Length != 0) {
if (input.Contains("chrome")) {
System.Diagnostics.Process.Start("chrome.exe", "www.google.com");
} else if (input.Contains("fox")) {
System.Diagnostics.Process.Start("firefox.exe", "www.facebook.com");
}
Console.WriteLine(input);
writer.WriteLine(input);
writer.Flush();
}
// ns.Close();
// client.Close();
connections--;
Console.WriteLine("Client disconnected: {0} active connections", connections);
} catch (Exception) {
connections--;
Console.WriteLine("Client disconnected: {0} active connections",
connections);
}
}
}
static void Main(string[] args) {
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 9050);
server.Bind(localEP);
server.Listen(10);
Console.WriteLine("Waiting for a client");
while (true) {
try {
Socket client = server.Accept();
ConnectionHandler handler = new ConnectionHandler(client);
Thread thread = new Thread(new ThreadStart(handler.HandleConnection));
thread.Start();
} catch (Exception) {
Console.WriteLine("Connection failed ..");
}
//client.Close();
//server.Shutdown();
}
}
}
}
#client code
using System;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace consoleTcpClient {
class Program {
static void Main(string[] args) {
// Console.WriteLine("Hello World!");
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
client.Connect(remoteEP);
NetworkStream stream = new NetworkStream(client);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
String input = reader.ReadLine();
Console.WriteLine(input);
String line = null;
while (true) {
Console.Write("Enter Message for Server <Enter to Stop >: ");
line = Console.ReadLine();
//writing for server
writer.WriteLine(line);
writer.Flush();
if (line.Length != 0) {
line = "Echo: " + reader.ReadLine();
Console.WriteLine(line);
}
}
// client.Close();
}
}
}
Thanks for taking the time to look at my question! This is my first post, so please excuse me if i did something wrong forum wise :)
EDIT: I appear to have solved my problem. I simply read the TCP stream 1 line at a time, and from there I use if statements to determine the reading start and stop spots.
Updated Code:
using System;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Text;
namespace RFLY_CMD
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
TcpListener server = new TcpListener(IPAddress.Parse("0.0.0.0"), 7362);
server.Start();
Console.WriteLine("Listening...");
Console.WriteLine();
while (true)
{
ClientWorking cw = new ClientWorking(server.AcceptTcpClient());
new Thread(new ThreadStart(cw.DoSomethingWithClient)).Start();
}
}
}
class ClientWorking
{
private Stream ClientStream;
private TcpClient Client;
public ClientWorking(TcpClient Client)
{
this.Client = Client;
ClientStream = Client.GetStream();
}
public void DoSomethingWithClient()
{
StreamWriter sw = new StreamWriter(ClientStream);
StreamReader sr = new StreamReader(sw.BaseStream);
string data;
bool cap = false;
string full_string = null;
try
{
while (true)
{
data = sr.ReadLine();
if (!string.IsNullOrEmpty(data) && !string.IsNullOrWhiteSpace(data) || cap)
{
if(data == "... start")
{
cap = true;
Console.WriteLine("---START FOUND!!!---");
}
if (cap)
{
Console.WriteLine(data);
full_string += data + "--:BREAK:--";
}
if (data == "... end")
{
cap = false;
Console.WriteLine("---END FOUND!!!---");
Console.WriteLine();
Console.WriteLine("-------------FULL STRING-------------");
Console.WriteLine(full_string);
}
}
}
}
finally
{
Console.Write("Ending...");
sw.Close();
}
}
}
}
Working Screenshot
End Edit.
Ok, so here is the problem... I have an application sending XML like so:
XML Screenshot
But, I only need the string of the text in the red box shown here:
Target Data
This is my first time dealing with XML and I have a little experience with TCP. Other than that, I am in WAY over my head.
Thanks in advance!
Here is my code:
using System;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using System.Text;
using System.Xml;
namespace RFLY_CMD
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 7362);
server.Start();
Console.WriteLine("Started.");
while (true)
{
ClientWorking cw = new ClientWorking(server.AcceptTcpClient());
new Thread(new ThreadStart(cw.DoSomethingWithClient)).Start();
}
}
}
class ClientWorking
{
private Stream ClientStream;
private TcpClient Client;
public ClientWorking(TcpClient Client)
{
this.Client = Client;
ClientStream = Client.GetStream();
}
public void DoSomethingWithClient()
{
StreamWriter sw = new StreamWriter(ClientStream);
StreamReader sr = new StreamReader(sw.BaseStream);
string data;
try
{
while (true)
{
data = sr.ReadToEnd();
if (!string.IsNullOrEmpty(data) && !string.IsNullOrWhiteSpace(data))
{
//Console.Clear();
Console.WriteLine(data);
//Console.WriteLine("---------------");
}
sr.DiscardBufferedData();
}
}
finally
{
Console.Write("Ending...");
sw.Close();
}
}
}
}
My server is running continuously to receive data from Agent, but the Agent is getting closed after a single run.
Even my timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); is not working properly.
Agent Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using System.Threading;
namespace HeartBeatAgent
{
public class Agent
{
static string clientdata = "IP1 Version 1";
static UdpClient UdpAgent = new UdpClient();
static System.Timers.Timer timer = new System.Timers.Timer();
static int ip = 127001;
static int port = 6060;
static IPEndPoint receivePoint;
static void Main(string[] args)
{
UdpAgent = new UdpClient(port);
receivePoint = new IPEndPoint(new IPAddress(ip), port);
Thread startClient = new Thread(new ThreadStart(startAgent));
//Start the Thread
startClient.Start();
}
public static void startAgent()
{
Console.WriteLine("This is Agent");
//Send Data
System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();
string sendString = clientdata.ToString();
byte[] sendMsg = encode.GetBytes(sendString);
//Send to Server to corresponding port of server
UdpAgent.Send(sendMsg, sendMsg.Length, "localhost", 6767);
Console.WriteLine("Msg sent to server 4m agent");
//Receive Data from Server
byte[] recMsg = UdpAgent.Receive(ref receivePoint);
System.Text.ASCIIEncoding receive = new System.Text.ASCIIEncoding();
//Split it up
string[] temp = receive.GetString(recMsg).Split(new Char[] { ' ' });
Console.WriteLine(temp[2] + temp[1] + temp[0]);
**timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);**
timer.Interval = 10000;
timer.Start();
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
startAgent();
}
}
}
Server Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace HeartBeatServer
{
public class Server
{
public static void Main(string[] args)
{
UdpClient UdpServer = new UdpClient(6767);
int count = 0;
while (true)
{
try
{
Dictionary<string, int> agentCount = new Dictionary<string, int>();
Console.WriteLine("This is server");
//Define a Receive point
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
//Receive Data from Agent
byte[] recData = UdpServer.Receive(ref RemoteIpEndPoint);
System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding();
//Split it up by space
string[] temp = encode.GetString(recData).Split(new Char[] { ' ' });
if (agentCount.ContainsKey(temp[0]))
{
var coun = agentCount[temp[0]];
agentCount.Add(temp[0], coun++);
Console.WriteLine(coun);
}
else
{
agentCount.Add(temp[0], count);
Console.WriteLine(temp[0], count);
//Re-send the Data to Agent
byte[] sendData = encode.GetBytes(System.DateTime.Now.ToString());
UdpServer.Send(sendData, sendData.Length, "localhost", 6060);
Console.WriteLine("Reply Message Sent to Agent 4m Server");
}
Console.WriteLine(temp[0] + temp[1] + temp[2]);
continue;
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}
I need to send message continuously to server at time interval of 5-10 seconds. Able to achieve first time only. Any idea where I am going wrong?
The problem is that you are creating a Thread, which calls a method and once that method ends the Thread will cease execution. Instead of using a thread, replace your Main method with this:
static void Main(string[] args)
{
UdpAgent = new UdpClient(port);
receivePoint = new IPEndPoint(new IPAddress(ip), port);
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 10000;
timer.Start();
}
and remove all timer related code from the startAgent method.
Sorry for asking about the 'same' thing over and over again. It's yet another edition of the chat. With a lot of searching around I've finally found out how to pass the client list (or at least I hope so) to the Chat function.
However I don't know if this is even supposed to work:
Everytime a client connects :
clients.Add(clientSocket);
var ctThread = new System.Threading.Thread(() => Chat(clients));
where the Chat function hopefully correctly receives the clients via
public void Chat(List<TcpClient> clients)
and then writes this out
foreach (var client in clients)
{
writer.Write(message);
}
With the client having 2 threads (not sure if they can actually read/write at the same time)
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
Did I pass the client list to the function properly? and can it actually correctly send the messages? Because right now the server is not responding to anything that I type on the client.
Full code:
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
namespace MultiServeris
{
class Multiserveris
{
static void Main(string[] args)
{
TcpListener ServerSocket = new TcpListener(1000);
ServerSocket.Start();
List<TcpClient> clients = new List<TcpClient>();
Console.WriteLine("Server started.");
while (true)
{
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
handleClient client = new handleClient();
clients.Add(clientSocket);
client.startClient(clientSocket,clients);
}
}
}
public class handleClient
{
TcpClient clientSocket;
public void startClient(TcpClient inClientSocket, List<TcpClient> clients)
{
this.clientSocket = inClientSocket;
var ctThread = new System.Threading.Thread(() => Chat(clients));
}
public void Chat(List<TcpClient> clients)
{
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
while (true)
{
string message = reader.ReadString();
foreach (var client in clients)
{
writer.Write(message);
}
}
}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace Klientas
{
class Klientas
{
public static void Write()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
string str = Console.ReadLine();
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);
}
}
public static void Read()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
string message = reader.ReadString();
Console.WriteLine(message);
}
}
static void Main(string[] args){
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
}
}
}
TCP is not design for broadcasting which is why you're having to loop through all your clients. A better approach would be to use a protocol that supports brodcast use the SignalR Framework or if you want baremetal access use UDP. Here's a great SignalR chat example.
https://dhavalupadhyaya.wordpress.com/tag/signalr-chat-example/
I'm not sure if the title is all that informative.
I am trying to find/write a socket server that will accept a connection from the client (telnet) and then on behalf of the connected client, connect to one of four telnet servers inside the network.
Once connected I keep a counter of how many connections there are, and then if there are 4 total connections, disallow any new connections until one of the four is available.
I have written this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static int nodeCount = 4;
static int currentNode = 1;
static void Main(string[] args)
{
ServerProgram server = new ServerProgram();
}
class ServerProgram
{
private TcpListener tcpPrimaryListener;
private Thread listenThread;
public ServerProgram()
{
this.tcpPrimaryListener = new TcpListener(IPAddress.Any, 23);
Console.WriteLine("Telnet BBS Port Concentrator Server Started.");
Console.WriteLine("--------------------------------------------");
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpPrimaryListener.Start();
while (true)
{
TcpClient client = this.tcpPrimaryListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
if (currentNode <= nodeCount)
{
Console.WriteLine("Connection thread created.");
StreamWriter swStream;
StreamWriter swStream2;
StreamReader srStream;
StreamReader srStream2;
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
TcpClient telnet = new TcpClient("192.168.100.5" + currentNode, 23);
NetworkStream telnetStream = telnet.GetStream();
currentNode++;
while (true)
{
srStream = new StreamReader(tcpClient.GetStream());
swStream2 = new StreamWriter(tcpClient.GetStream());
srStream2 = new StreamReader(telnet.GetStream());
swStream = new StreamWriter(telnet.GetStream());
swStream.Write(srStream.ReadToEnd());
swStream2.Write(srStream2.ReadToEnd());
}
}
}
}
}
}
I've changed this example multiple times, so I don't really know anymore what I have and have not tried. I'm willing to try anything.
The purpose is actually running this to allow one telnet port open through the firewall, and allowing connections into a small network of DOS machines running telnet fossil driver BBS software. I would just like to redirect telnet traffic to an available system using only one port.
The problem is that I cannot figure out how to actually connect the two sockets together and pass data between them as it happens. The incoming socket and the socket I created on behalf of the server to the server.
Thanks.
UPDATE:
This is what is working for me, I'm still looking over for bugs but it's working so far.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static int nodeCount = 2;
static int currentNode = 1;
static void Main(string[] args)
{
ServerProgram server = new ServerProgram();
}
class ServerProgram
{
private TcpListener tcpPrimaryListener;
private Thread listenThread;
public ServerProgram()
{
this.tcpPrimaryListener = new TcpListener(IPAddress.Any, 23);
Console.WriteLine("Telnet BBS Port Concentrator Server Started.");
Console.WriteLine("--------------------------------------------");
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpPrimaryListener.Start();
while (true)
{
TcpClient client = this.tcpPrimaryListener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
string noNodes = "Sorry all nodes are occupied.";
if (currentNode <= nodeCount)
{
Console.WriteLine("Client connected.");
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
TcpClient telnet = new TcpClient("10.24.9.11", 23);
//TcpClient telnet = new TcpClient("192.168.100.5" + currentNode, 23);
NetworkStream telnetStream = telnet.GetStream();
currentNode++;
ByPass linkedSockets = new ByPass(tcpClientStream, telnetStream);
}
else
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream tcpClientStream = tcpClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
tcpClientStream.Write(Encoding.ASCII.GetBytes(noNodes), 0, noNodes.Length);
}
}
}
public class ByPass
{
public ByPass(Stream s1, Stream s2)
{
var cTokenSource = new CancellationTokenSource();
var cToken = cTokenSource.Token;
Task.Factory.StartNew(() => Process(s1, s2, cToken, cTokenSource), cToken);
Task.Factory.StartNew(() => Process(s2, s1, cToken, cTokenSource), cToken);
cToken.Register(() => cancelNotification());
}
public void Process(Stream s1, Stream s2, CancellationToken ct, CancellationTokenSource cTokenSource)
{
byte[] buf = new byte[0x10000];
while (true)
{
if (ct.IsCancellationRequested)
{
break;
}
try
{
int len = s1.Read(buf, 0, buf.Length);
s2.Write(buf, 0, len);
}
catch
{
s1.Close(); s2.Close();
cTokenSource.Cancel();
break;
}
}
}
}
static void cancelNotification()
{
Console.WriteLine("Client disconnected.");
currentNode--;
}
}
}
I think, you can create a class similar to below to pass data between two streams
public class ByPass
{
public ByPass(Stream s1, Stream s2)
{
Task.Factory.StartNew(() => Process(s1, s2));
Task.Factory.StartNew(() => Process(s2, s1));
}
public void Process(Stream sIn, Stream sOut)
{
byte[] buf = new byte[0x10000];
while (true)
{
int len = sIn.Read(buf, 0, buf.Length);
sOut.Write(buf, 0, len);
}
}
}
I have made little changes and it works perfect on my side
public class StreamTransmitter
{
static TaskCompletionSource<bool> ts;
public static async Task Start(Stream s1, Stream s2, CancellationToken token)
{
ts = new TaskCompletionSource<bool>();
Process(s1, s2, token);
Process(s2, s1, token);
await ts.Task;
}
private static async Task Process(Stream sIn, Stream sOut, CancellationToken token)
{
byte[] buf = new byte[0x10000];
int len = 0;
do
{
len = await sIn.ReadAsync(buf, 0, buf.Length, token);
await sOut.WriteAsync(buf, 0, len, token);
}
while (len > 0 && !token.IsCancellationRequested);
ts.SetResult(true);
}
}