Why TCPClient doesn't receive text data - c#

I have two projects: client and server.
Server code:
static void Main()
{
var listener = new TcpListener(System.Net.IPAddress.Loopback, 13000);
listener.Start();
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
var textStream = new StreamReader(stream);
while (true)
{
var line = textStream.ReadLine();
if (line == "") continue;
if (line == null) continue;
var clientIp = IPAddress.Parse(line.Split(':').First());
var clientPort = int.Parse(line.Split(':').Last());
var response = new LobbyConnectionResponse(StatusCode.OK, "Connected", new List<Player>(), new Leader("J", Guid.NewGuid()));
SendResponseToClient(clientIp, clientPort, response);
Console.WriteLine(line);
Thread.Sleep(lobbyResponseTimeout);
}
}
listener.Stop();
}
static Task SendResponseToClient(IPAddress ip, int port, LobbyConnectionResponse response)
{
var task = Task.Run(() =>
{
using (var tcpClient = new TcpClient())
{
tcpClient.Connect(ip, port);
var stream = tcpClient.GetStream();
var textStream = new StreamWriter(stream);
string jsonString = JsonSerializer.Serialize(response);
textStream.WriteAsync(jsonString);
stream.FlushAsync();
}
});
return task;
}
Client code:
static void Main()
{
StartListener();
using (var client = new TcpClient())
{
client.Connect("127.0.0.1", 13000);
var stream = client.GetStream();
var textStream = new StreamWriter(stream);
textStream.WriteLine("127.0.0.1:12000");
textStream.Flush();
}
while(true)
Thread.Sleep(1000);
Thread.Sleep(1000);
}
static Task StartListener()
{
var task = Task.Run(() =>
{
var listener = new TcpListener(System.Net.IPAddress.Loopback, 12000);
listener.Start();
while (true)
{
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
var textStream = new StreamReader(stream);
while (true)
{
if (stream.DataAvailable)
Console.WriteLine("hfdsgdf");
var line = textStream.ReadLine();
if (line == null || line == "") continue;
var response = (LobbyConnectionResponse)JsonSerializer.Deserialize(stream, typeof(LobbyConnectionResponse));
Console.WriteLine(response.Description);
Thread.Sleep(100);
}
}
Thread.Sleep(100);
}
listener.Stop();
});
return task;
}
On the server's console, I had message:"127.0.0.1:12000" but client's console are empty, why client doesn't receive my text data? 0 Exceptions, clients code just looping infinity and every iteration continues because line == null
When I tried to make that on a single port ( server and client), nothing worked. I want that client get my JSON and deserialize it into class instance.

You need to send data through client/stream variable instead of creating a new connection from server to client.
var listener = new TcpListener(System.Net.IPAddress.Loopback, 13000);
listener.Start();
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
// send response to client
byte[] buffer = Encoding.UTF8.GetBytes("foo");
stream.Write(buffer, 0, buffer.Length);
stream.Close();
}

Related

C# No argument given that corresponds to the required formal parameter 'stream' of 'Receiver(NetworkStream) Error CS7036

I'm trying to create a C# Socket multi-user server (and eventually client).
I want this server to be able to accept multiple clients (who all join and leave at different random moments) and at the same time displays their data (that the server received).
Here is my SocketMultiServer code:
using System.Net.Sockets;
using System.Text;
using System;
using System.Threading;
// Threading
Thread MainThread = Thread.CurrentThread;
Console.Title = "Multi Server";
// Aanmaken van server
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, 6969);
listener.Start(5);
Console.WriteLine("Server online...");
// 2 threads
Thread ListenerThread = new Thread(o => Listener(listener));
Thread ReceiverThread = new Thread(o => Receiver());
//Thread ReceiverThread = new Thread(o => Receiver(NetworkStream stream));
ListenerThread.Start();
ReceiverThread.Start();
static void Listener(TcpListener listener)
{
Console.WriteLine("TEST 2");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client connected...");
NetworkStream stream = client.GetStream();
}
static void Receiver(NetworkStream stream)
{
while (true)
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
int recv = 0;
foreach (byte b in buffer)
{
if (b != 0)
{
recv++;
}
}
string request = Encoding.UTF8.GetString(buffer, 0, recv);
Console.WriteLine(request);
}
}
I'm having the error: "Error CS7036 There is no argument given that corresponds to the required formal parameter 'stream' of 'Receiver(NetworkStream)' " at l17
Thread ReceiverThread = new Thread(o => Receiver());
My Client code (work in progress)
using System.Net.Sockets;
using System.Text;
using System;
TcpClient client = new TcpClient("127.0.0.1", 6969);
Console.WriteLine("Connected...");
Console.Write("Username > ");
string username = Console.ReadLine();
string username_ = username + (": ");
while (true)
{
Console.WriteLine("Message to send > ");
string msg = Console.ReadLine();
if (msg == "exit()")
{
break;
}
else
{
// msg converten
int byteCount = Encoding.ASCII.GetByteCount(msg + 1);
byte[] sendData = new byte[byteCount];
sendData = Encoding.ASCII.GetBytes(username_ + msg);
NetworkStream stream = client.GetStream();
stream.Write(sendData, 0, sendData.Length);
}
}
Does someone have a fix? I'm quite new to C#
Thanks in advance!
Your primary issue is that you need to pass NetworkStream to Receiver.
However, this is not the way to do a multi-receive server anyway. You are best off using Task and async.
CancellationTokenSource cancellation = new CancellationTokenSource();
static async Task Main()
{
Console.Title = "Multi Server";
Console.WriteLine("Press ENTER to stop server...");
var task = Task.Run(RunServer);
Console.ReadLine(); // wait for ENTER
cancellation.Cancel();
await task;
}
static async Task RunServer()
{
TcpListener listener;
try
{
listener = new TcpListener(IPAddress.Any, 6969);
listener.Start(5);
Console.WriteLine("Server online...");
while (!cancellation.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected...");
Task.Run(async () => await Receiver(client), cancellation.Token);
}
}
catch (OperationCancelException)
{ //
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (listener?.Active == true)
listener.Stop();
}
}
static async Task Receiver(TcpClient client)
{
try
{
using client;
using var stream = client.GetStream();
var buffer = new byte[1024];
while (!cancellation.IsCancellationRequested)
{
var bytesReceived = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesReceived == 0)
break;
string request = Encoding.UTF8.GetString(buffer, 0, bytesReceived);
Console.WriteLine(request);
}
}
catch (OperationCancelException)
{ //
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
You should have similar async code for the client. Don't forget to dispose everything with using

C# Socket is not responding in seperate thread

Hello I am working on some code which envolves a hello protocol between two servers as a keep alive and to process files on the brother if a server goes down.
I run the same service on two different machines but on one machine it does not recieve and gets stuck on socket.recieve().
i have traced a wireshark and c# debug but the client sends but the server never responds, which is causing a problem for this feature
Thanks
Richard
public static void Listen(ServiceController serviceController, Server server, List<Server> RemoteServers)
{
while (!serviceController.StopService)
{
if (server.ThisDevice)
{
TcpListener listener = new TcpListener(1);
string response = "Hello Recieved";
byte[] responsebyte = Encoding.ASCII.GetBytes(response);
try
{
listener = new TcpListener(server.IPAddress, 11000);
listener.Start();
byte[] Bytes = new byte[256];
Listening = true;
while (!serviceController.StopService)
{
Socket socket = listener.AcceptSocket();
int result = socket.Receive(Bytes);
ASCIIEncoding aSCII = new ASCIIEncoding();
string strres = aSCII.GetString(Bytes,0,result);
string stripped = strres.Substring(0, 5);
if (stripped == "Hello")
{
Console.WriteLine(stripped);
socket.Send(responsebyte);
string clientIP = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString();
ProcessHello(stripped,clientIP,RemoteServers);
}
socket.Close();
}
}
catch (Exception ex)
{
listener.Stop();
Listening = false;
}
}
}
}
}
public static void StartClient(Server server)
{
if (!server.ThisDevice)
{
TcpClient tcpClient = new TcpClient(server.IPAddress.ToString(),11000);
NetworkStream stream = tcpClient.GetStream();
string msg = "Hello";
string response = "";
byte[] bytes = new byte[256];
bytes = Encoding.ASCII.GetBytes(msg);
stream.Write(bytes, 0, bytes.Length);
StreamReader reader = new StreamReader(stream, Encoding.ASCII);
try
{
response = reader.ReadToEnd();
}
finally
{
// Close the reader
reader.Close();
}
tcpClient.Close();
}
public void StartClient(List<Server> servers)
{
_ClientStarted = true;
var remote = servers.Where(s => !s.ThisDevice).ToList();
Thread t = new Thread(() => ExecuteThreadedClient(remote));
t.Start();
}
void ExecuteThreadedClient(List<Server> servers)
{
while (!_ServiceController.StopService)
{
try
{
//Token.ThrowIfCancellationRequested();
foreach (var server in servers)
{
TCPHelloClient.StartClient(server);
}
Thread.Sleep(_HelloTimer);
}
catch (OperationCanceledException e)
{
Thread.Sleep(60000);
}
catch(Exception e)
{
Thread.Sleep(_HelloTimer);
}
}
_ClientStarted = false;
// Task.Delay(_HelloTimer).Wait();
}
public void StartServer(List<Server> servers)
{
try
{
//var task = Task.Run(() => ExecuteThreadedServer(server), Token);
Server local = servers.First(s => s.ThisDevice);
var remote = servers.Where(s => !s.ThisDevice).ToList();
Thread t = new Thread(() => TCPHelloServer.Listen(_ServiceController, local, remote));
t.Start();
}
catch(Exception e)
{
}
}

HTTPS proxy implementation, how to detect a completed request

I'm attempting to write a simple async https proxy server in c#.
I would like to know how I should detect/handle when the request is complete, and how to exit my bActive loop, assuming a loop like this is appropriate.
Would really appreciate some pointers on if my approach is correct and what I could do to improve the logic.
The issue I seem to be running into is that the time it takes for an endpoint to respond along with the network delay means I DataAvailable doenst always have data but there may still be some sending. Requiring a sleep and another attmempt which in turn causes the long completion time in requests.
Listen for TCP connection
Extract CONNECT header and open a connection to the requested server
Copy the requestStream to proxyStream
Copy the proxyStream to the requestStream
Sleep waiting for data and repeat 3 - 4 until no data is avaiable on both streams. Then break out of the loop and close connection.
public async Task Start()
{
listener.Start();
while (listen)
{
if (listener.Pending())
{
HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100); //<--- timeout
}
}
}
private static async Task HandleClient(TcpClient clt)
{
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
int count;
const string connectText = "connect";
const string hostText = "Host: ";
bool bActive = true;
List<Task> tasks = new List<Task>();
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
proxyStream.ReadTimeout = 100;
proxyStream.WriteTimeout = 100;
while (bActive)
{
if (proxyStream.DataAvailable && hostHeaderAvailable == 0)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
requestStream.ReadTimeout = 100;
requestStream.WriteTimeout = 100;
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
// delay here seems to prevent the following proxyStream.read from failing as data is not yet avaiable
// without it the loop runs and has to timeout before running again
await Task.Delay(1);
}
}
hostHeaderAvailable++;
if (requestStream == null || !requestClient.Connected || !clt.Connected)
{
bActive = false;
break;
}
Console.WriteLine(proxyStream.DataAvailable || requestStream.DataAvailable);
if (proxyStream.DataAvailable || requestStream.DataAvailable)
{
Task task = proxyStream.CopyToAsync(requestStream);
Task task2 = requestStream.CopyToAsync(proxyStream);
tasks.Add(task);
tasks.Add(task2);
await Task.WhenAll(tasks).ConfigureAwait(false);
bActive = false;
break;
}
await Task.Delay(10);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
clt.Close();
}
An older attempt that used ReadAsync/WriteAsync too longer to response and still had the timeout issue.
Listen for TCP connection
Extract CONNECT header and open a connection to the requested server
Read data from requestStream and copy to proxyStream
Wait checking if data is avaiable on either stream
If data avaiable read from proxyStream and write to requestStream
If data avaiable read from requestStream and write to proxyStream
Sleep waiting for data and repeat 5 - 6 until no data is avaiable on eitboth streams. Then break out of the loop and close connection.
private static TcpListener listener = new TcpListener(IPAddress.Parse("192.168.0.25"), 13000);
private static bool listen = true;
public async Task Start()
{
listener.Start();
while (listen)
{
if (listener.Pending())
{
await HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100);
}
}
}
private static async Task HandleClient(TcpClient clt)
{
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
int count;
const string connectText = "connect";
const string hostText = "Host: ";
bool bActive = true;
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
while (bActive)
{
while (proxyStream.DataAvailable)
{
// handle connect
if (hostHeaderAvailable == 0)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
// delay here seems to prevent the following proxyStream.read from failing as data is not yet avaiable
// without it the loop runs and has to timeout before running again
await Task.Delay(20);
}
}
hostHeaderAvailable++;
if (requestClient.Connected && hostHeaderAvailable > 1)
{
count = await proxyStream.ReadAsync(bytes, 0, bytes.Length);
await requestStream.WriteAsync(bytes, 0, count);
}
}
while (requestStream.DataAvailable)
{
count = await requestStream.ReadAsync(bytes, 0, bytes.Length);
await proxyStream.WriteAsync(bytes, 0, count);
}
// attempt to detect a timeout / end of data avaiable
var timeout = 0;
while (!proxyStream.DataAvailable && !requestStream.DataAvailable)
{
if (timeout > 5)
{
bActive = false;
break;
}
await Task.Delay(10);
timeout++;
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
UPDATE
As per AgentFire's answer I have now come to the following working code:
public static async Task HandleDisconnect(TcpClient tcp, TcpClient tcp2, CancellationToken cancellationToken)
{
while (true)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
Console.WriteLine("The requesting client has dropped its connection.");
cancellationToken = new CancellationToken(true);
break;
}
}
if (tcp2.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp2.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Server disconnected
Console.WriteLine("The destination client has dropped its connection.");
cancellationToken = new CancellationToken(true);
break;
}
}
await Task.Delay(1);
}
}
private static async Task HandleClient(TcpClient clt)
{
List<Task> tasks = new List<Task>();
var bytes = new byte[clt.ReceiveBufferSize];
var hostHeaderAvailable = 0;
NetworkStream requestStream = null;
const string connectText = "connect";
try
{
using (NetworkStream proxyStream = clt.GetStream())
using (TcpClient requestClient = new TcpClient())
{
proxyStream.ReadTimeout = 100;
proxyStream.WriteTimeout = 100;
if (proxyStream.DataAvailable && hostHeaderAvailable == 0)
{
await proxyStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await requestClient.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
requestStream = requestClient.GetStream();
requestStream.ReadTimeout = 100;
requestStream.WriteTimeout = 100;
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await proxyStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
}
}
hostHeaderAvailable++;
CancellationToken cancellationToken = new CancellationToken(false);
Task task = proxyStream.CopyToAsync(requestStream, cancellationToken);
Task task2 = requestStream.CopyToAsync(proxyStream, cancellationToken);
Task handleConnection = HandleDisconnect(clt, requestClient, cancellationToken);
tasks.Add(task);
tasks.Add(task2);
tasks.Add(handleConnection);
await Task.WhenAll(tasks).ConfigureAwait(false);
// close conenctions
clt.Close();
clt.Dispose();
requestClient.Close();
requestClient.Dispose();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
UPDATE
Attempt at using CancellationTokenSource
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellationToken = source.Token;
TaskFactory factory = new TaskFactory(cancellationToken);
tasks.Add(factory.StartNew(() => {proxyStream.CopyToAsync(requestStream);}, cancellationToken));
tasks.Add(factory.StartNew(() => {requestStream.CopyToAsync(proxyStream);}, cancellationToken));
tasks.Add(factory.StartNew(async () => {
//wait for this to retur, then cancel the token
await HandleDisconnect(clt, requestClient);
source.Cancel();
}, cancellationToken));
try
{
await factory.ContinueWhenAll(tasks.ToArray(),
(results) =>
{
Console.WriteLine("Tasks complete");
}, cancellationToken);
}
catch (AggregateException ae)
{
foreach (Exception e in ae.InnerExceptions)
{
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException)e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally
{
source.Dispose();
}
UPDATE
public static class extensionTcpClient{
public static bool CheckIfDisconnected(this TcpClient tcp)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
}
return true;
}
}
class ProxyMaintainer
{
private static TcpListener listener = new TcpListener(IPAddress.Parse("192.168.0.25"), 13000);
public ProxyMaintainer()
{
}
public async Task Start()
{
Console.WriteLine("###############################");
Console.WriteLine("Listening on 192.168.0.25:13000");
Console.WriteLine("###############################\n");
listener.Start();
while (listen)
{
if (listener.Pending())
{
HandleClient(await listener.AcceptTcpClientAsync());
}
else
{
await Task.Delay(100); //<--- timeout
}
}
}
private static async Task Transport(NetworkStream from, NetworkStream to, Func<bool> isAlivePoller, CancellationToken token)
{
byte[] buffer = new byte[4096];
while (isAlivePoller())
{
while (from.DataAvailable)
{
int read = await from.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);
await to.WriteAsync(buffer, 0, read, token);
}
// Relieve the CPU a bit.
await Task.Delay(10, token).ConfigureAwait(false);
}
}
private static async Task HandleClient(TcpClient clientFrom)
{
var hostHeaderAvailable = 0;
int count;
var bytes = new byte[clientFrom.ReceiveBufferSize];
const string connectText = "connect";
NetworkStream toStream = null;
using (var fromStream = clientFrom.GetStream())
using(TcpClient clientTo = new TcpClient())
using (var manualStopper = new CancellationTokenSource())
{
count = await fromStream.ReadAsync(bytes, 0, bytes.Length);
var text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
if (text.ToLower().StartsWith(connectText))
{
// extract the url and port
var host = text.Remove(0, connectText.Length + 1);
var hostIndex = host.IndexOf(" ", StringComparison.Ordinal);
var hostEntry = host.Remove(hostIndex).Split(new[] { ":" }, StringSplitOptions.None);
// connect to the url and prot supplied
await clientTo.ConnectAsync(hostEntry[0], Convert.ToInt32(hostEntry[1]));
toStream = clientTo.GetStream();
// send 200 response to proxyStream
const string sslResponse = "HTTP/1.0 200 Connection established\r\n\r\n";
var sslResponseBytes = Encoding.UTF8.GetBytes(sslResponse);
await fromStream.WriteAsync(sslResponseBytes, 0, sslResponseBytes.Length);
}
bool Poller() => clientFrom.CheckIfDisconnected() && clientTo.CheckIfDisconnected();
Task one = Transport(fromStream, toStream, Poller, manualStopper.Token);
Task two = Transport(toStream, fromStream, Poller, manualStopper.Token);
await Task.WhenAll(one, two).ConfigureAwait(false);
//await one; await two; // To get exceptions if you want them and there are any.
// Alternatively, you can use Task.WhenAll to get exceptions aggregated for you.
}
Console.WriteLine("Closing connection");
}
}
Well, tell you what. The data availability, when it comes to HTTP, lies only in one parameter (if we omit things like WebSocket), which is called Connection and is passed as a Header as a one of two possible states: Close or Keep-Alive.
If Close is chosen by the client, the server is obliged to close the conection as soon as the request is served, whereas Keep-Alive tells the server that, if it doesn't want to, it may leave connection open for another request.
Let's consider both cases.
If client chooses Keep-Alive, the connection will persist and work as intended, indefinetely. But:
If either side drops the connection, there is an easy way to detect that. This piece of code was found on StackOverflow and it was told that it still works perfectly:
public static bool CheckIfDisconnected(this TcpClient tcp)
{
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return true;
}
}
return false;
}
So I believe that you, as a proxy-server, are not obliged to manage connection states at all and can leave it to the actual communication parties. All you have to do is to detect when either of your connections - proxy or request - is dropped, drop the other one and call it a day.
P.S. Now, you also asked about asynchronicity.
I must add that TCP connections are considered full-duplex. which means you are free to create two async-running tasks, both reading and writing to their own sinks. My thoughts, it would be the optimal course of action.
To answer your other question
You are still using Stream.CopyToAsync which, as I have told you, is not going to succeed as long as any communicating party decides to wait a bit before sending another chunk of data.
You are also somewhat overcomplicating your solution.
I would put it this way:
async Task Transport(NetworkStream from, NetworkStream to, Func<bool> isAlivePoller, CancellationToken token)
{
byte[] buffer = new byte[4096];
while (isAlivePoller())
{
while (from.DataAvailable)
{
int read = await from.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);
await to.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);
}
// Relieve the CPU a bit.
await Task.Delay(100, token).ConfigureAwait(false);
}
}
And then in your main code:
using TcpClient clientFrom = ...;
using TcpClient clientTo = ...;
using var fromStream = clientFrom.GetStream();
using var toStream = clientTo.GetStream();
using var manualStopper = new CancellationTokenSource();
bool Poller() => clientFrom.CheckIfDisconnected() && clientTo.CheckIfDisconnected();
Task one = Transport(fromStream, toStream, Poller, stopper.Token);
Task two = Transport(toStream, fromStream, Poller, stopper.Token);
await Task.WhenAny(one, two).ConfigureAwait(false);
//await one; await two; // To get exceptions if you want them and there are any.
// Alternatively, you can use Task.WhenAll to get exceptions aggregated for you.
And you are pretty much done here.

C# Reading stream from TcpClient

I'm making a server - client program using TcpClient and server.
To send from the server I use:
static void send(string data)
{
sw.WriteLine(data + Environment.NewLine);
}
And when the client connects I'm sending some text:
client = listener.AcceptTcpClient();
sr = new StreamReader(client.GetStream());
sw = new StreamWriter(client.GetStream());
string line;
try
{
send("Hello world");
} //More code
And to read from client:
string data;
data = sr.ReadLine();
if(data != string.Empty || data != null)
{
MessageBox.Show(data);
}
I tried putting inside while(true) and it froze, I tried putting inside a timer tick loop and it froze...
How do I fix this?
P.S: The client and the server are 2 different projects.
I believe you need something like this:
try
{
listen = new TcpListener(myAddress, port);
listen.Start();
Byte[] bytes;
while (true)
{
TcpClient client = listen.AcceptTcpClient();
NetworkStream ns = client.GetStream();
if(client.ReceiveBufferSize > 0){
bytes = new byte[client.ReceiveBufferSize];
ns.Read(bytes, 0, client.ReceiveBufferSize);
string msg = Encoding.ASCII.GetString(bytes); //the message incoming
MessageBox.Show(msg);
}
}
}
catch(Exception e)
{
//e
}
Then have this code as a background thread:
Thread thread = new Thread(the functions name);
thread.IsBackground = true;
thread.Start();
I hope I understand what you need.
Try this one, grandpa's way.
int i = 0;
while (stream.DataAvailable == true)
{
bytes[i] = ((byte)stream.ReadByte());
i++;
}
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);

Trying to read from NetworkStream crashes program, no exception or error

I'm trying to read data from a socket, however whenever I try to read the entire stream my program hangs. With no error or exception.
I can manually read say 4 bytes and it will work, however I no longer know the exact size of the data the server will send, therefore I wish to read the entire stream. What am I doing wrong?
It hangs when calling ReadToEnd().
Stream input = socket.GetStream();
byte[] request = new byte[5];
input.Write(request, 0, 5); //send request
StreamReader reader = new StreamReader(input);
if (input.CanRead == true)
{
string test = reader.ReadToEnd();
}
Your code canot know how long the stream is, it's possibly not ended so its going to continue to block until it has. Below is an example server and client (in no way is this a robust implementation) but if you consider the following code you should see how to send a request and receive a response:
public class Server
{
private readonly Thread _listenThread;
private readonly TcpListener _tcpListener;
public Server()
{
_tcpListener = new TcpListener(IPAddress.Any, 3000);
_listenThread = new Thread(Listen);
_listenThread.Start();
}
private void Listen()
{
var tcpListener = _tcpListener;
if (tcpListener != null)
{
tcpListener.Start();
}
while (true)
{
TcpClient client = _tcpListener.AcceptTcpClient();
Console.Out.WriteLine("Connection Accepted");
Thread clientThread = new Thread(DoWork);
clientThread.Start(client);
}
}
private void DoWork(object client)
{
TcpClient tcpClient = client as TcpClient;
if (tcpClient == null)
{
throw new ArgumentNullException("client", "Must pass client in");
}
using (NetworkStream clientStream = tcpClient.GetStream())
{
byte[] message = new byte[1024];
while (true)
{
Console.Out.WriteLine("Waiting for message");
int bytesRead = clientStream.Read(message, 0, 1024);
if (bytesRead == 0)
{
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
string received = encoder.GetString(message, 0, bytesRead);
Console.Out.WriteLine(String.Format("Read {0}", received));
if (received.Equals("Hello Server !"))
{
byte[] buffer = encoder.GetBytes("Hello Client!");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
}
}
}
You would need a client that did something like this
static void Main(string[] args)
{
try
{
using (TcpClient clientSock = new TcpClient(IPAddress.Loopback.ToString(), 3000))
{
using (Stream clientStream = clientSock.GetStream())
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] helloMessage = encoder.GetBytes("Hello Server !");
clientStream.Write(helloMessage, 0, helloMessage.Length);
clientStream.Flush();
using (StreamReader streamReader = new StreamReader(clientStream))
{
while(true)
{
char[] buffer = new char[1024];
streamReader.Read(buffer,0, 1024);
Console.Out.WriteLine(new string(buffer));
}
}
}
}
Console.ReadLine();
}
catch (Exception)
{
// do something here
throw;
}
}

Categories