TcpListener backlog concept misunderstanding - c#

I'm trying to understand the backlog parameter of TcpListener class but I struggle on how to achieve maximum number of pending connections at same time so I can test it.
I have a sample async server and client code. MSDN says that the backlog is the maximum length of the pending connections queue. I made the server listen for connections all the time and the client is connecting 30 times. What I expect is after the 20th request to throw a SocketException in the client because the backlog is set to 20. Why doesn't it block it?
My second misunderstanding is do I really need to put my logic of the accepted connection in a new thread assuming there is a slow operation which takes around 10 seconds e.g. sending a file over the TCP? Currently, I put my logic in a new Thread, I know it's not the best solution and instead I should use a ThreadPool but the question is principal. I tested it by changing the client side's loop to 1000 iterations and if my logic is not in a new thread, the connections were getting blocked after the 200th connection probably because Thread.Sleep slows the main thread each time by 10 seconds and the main thread is responsible for all the accept callbacks. So basically, I explain it myself as the following: if I want to use the same concept, I have to put my AcceptCallback logic in a new thread like I did or I have to do something like the accepted answer here: TcpListener is queuing connections faster than I can clear them. Am I right?
Server code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Server
{
class Program
{
private static readonly ManualResetEvent _mre = new ManualResetEvent(false);
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 80);
try
{
listener.Start(20);
while (true)
{
_mre.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);
_mre.WaitOne();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void AcceptCallback(IAsyncResult ar)
{
_mre.Set();
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
IPAddress ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
Console.WriteLine($"{ip} has connected!");
// Actually I changed it to ThreadPool
//new Thread(() =>
//{
// Console.WriteLine("Sleeping 10 seconds...");
// Thread.Sleep(10000);
// Console.WriteLine("Done");
//}).Start();
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
Console.WriteLine("Sleeping 10 seconds...");
Thread.Sleep(10000);
Console.WriteLine("Done");
}));
// Close connection
client.Close();
}
}
}
Client code:
using System;
using System.Net.Sockets;
namespace Client
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 30; i++)
{
Console.WriteLine($"Connecting {i}");
using (TcpClient client = new TcpClient()) // because once we are done, we have to close the connection with close.Close() and in this way it will be executed automatically by the using statement
{
try
{
client.Connect("localhost", 80);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadKey();
}
}
}
Edit: Since my second question might be a little bit confusing, I will post my code which includes sent messages and the question is should I leave it like that or put the NetworkStream in a new thread?
Server:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server
{
class Program
{
private static readonly ManualResetEvent _mre = new ManualResetEvent(false);
static void Main(string[] args)
{
// MSDN example: https://learn.microsoft.com/en-us/dotnet/framework/network-programming/asynchronous-server-socket-example
// A better solution is posted here: https://stackoverflow.com/questions/2745401/tcplistener-is-queuing-connections-faster-than-i-can-clear-them
TcpListener listener = new TcpListener(IPAddress.Any, 80);
try
{
// Backlog limit is 200 for Windows 10 consumer edition
listener.Start(5);
while (true)
{
// Set event to nonsignaled state
_mre.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), listener);
// Wait before a connection is made before continuing
_mre.WaitOne();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue
_mre.Set();
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
IPAddress ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
Console.WriteLine($"{ip} has connected!");
using (NetworkStream ns = client.GetStream())
{
byte[] bytes = Encoding.Unicode.GetBytes("test");
ns.Write(bytes, 0, bytes.Length);
}
// Use this only with backlog 20 in order to test
Thread.Sleep(5000);
// Close connection
client.Close();
Console.WriteLine("Connection closed.");
}
}
}
Client:
using System;
using System.Net.Sockets;
using System.Text;
namespace Client
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 33; i++)
{
Console.WriteLine($"Connecting {i}");
using (TcpClient client = new TcpClient()) // once we are done, the using statement will do client.Close()
{
try
{
client.Connect("localhost", 80);
using (NetworkStream ns = client.GetStream())
{
byte[] bytes = new byte[100];
int readBytes = ns.Read(bytes, 0, bytes.Length);
string result = Encoding.Unicode.GetString(bytes, 0, readBytes);
Console.WriteLine(result);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadKey();
}
}
}

The listen backlog is defined in RFC 6458 and tells the OS the maximum number of sockets allowed in the accept queue.
Incoming connections are placed in this queue by the TCP/IP stack and removed when the server calls Accept to handle the new connection.
In your question, both versions of the server code call Accept in a loop from the main thread, and wait for AcceptCallback to start before making another accept call. This results in quite fast draining of the queue.
To demonstrate listen queue overflow, its easiest to slow down your server's rate of accepting - E.g. slow it down to zero:
var serverEp = new IPEndPoint(IPAddress.Loopback, 34567);
var serverSocket = new TcpListener(serverEp);
serverSocket.Start(3);
for (int i = 1; i <= 10; i++)
{
var clientSocket = new TcpClient();
clientSocket.Connect(serverEp);
Console.WriteLine($"Connected socket {i}");
}
In your examples you could just add a sleep at the end of the Accept loop in the main thread, and increase the connection rate.
In the real world, the optimal backlog depends on:
The rate that the clients / internet / OS can fill the queue
The rate that the OS / server can process the queue
I don't recommend using Thread directly, here's how the server looks using Task and Socket Task Extensions:
static async Task Main(string[] args)
{
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, 80));
server.Listen(5);
while (true)
{
var client = await server.AcceptAsync();
var backTask = ProcessClient(client);
}
}
private static async Task ProcessClient(Socket socket)
{
using (socket)
{
var ip = ((IPEndPoint)(socket.RemoteEndPoint)).Address;
Console.WriteLine($"{ip} has connected!");
var buffer = Encoding.Unicode.GetBytes("test");
await socket.SendAsync(buffer, SocketFlags.None);
}
Console.WriteLine("Connection closed.");
}

Related

How to Timeout an Async UdpClient Receive AND Get a Response?

That title is a little weird, but I have a UdpClient and it uses ReceiveAsync to listen for response from the remote endpoint after a SendAsync. The problem is that the remote endpoint is a very flimsy IoT device that will either close the connection, or never reply.
I want to timeout the ReceiveAsync so the socket can be released when the device decides to do nothing. I saw a genius comment by AJ Richardson here that suggests doing this:
Task.WhenAny(
client.ReceiveAsync(),
Task.Delay(5000)
);
I'm sure that works great, but if the ReceiveAsync is successful, I need to read the response, but because it's wrapped in Task.WhenAny, I have no idea how to do that.
Could anyone give me any suggestions? Thanks in advance!
Clarification
To clarify, here's my scenario. I have a Hangfire background job. It receives a data model and based on it sends a message to an IoT device using UDP. The problem is that the devices are flimsy and will not respond, which means the client will be awaiting forever.
If that happens then the client will be holding onto the port, and depending on how many times the job is queued I can eventually run out of ports since their clients are just stuck awaiting.
To avoid that, I want to timeout after a 5 second period and release the client's port and other resources. That is where Task.WhenAny comes in. Either the ReceiveAsync or Task.Delay calls will complete first and end the process.
However, if ReceiveAsync completes first, I need to capture the response from it and do further processing with it. How do I do that?
Here is a more complete code sample of what I'm working with.
var iotAddress = new IPAddress(iot.Ip);
var iotEndpoint = new IPEndPoint(iotAddress, iot.Port);
try {
using (var client = new UdpClient(0, AddressFamily.InterNetwork)) {
client.Connect(iotEndpoint);
await client.SendAsync(bytes, bytes.Length);
if (!iot.WaitForResponse) {
return;
}
// await the response of the IoT device
var response = await client.ReceiveAsync();
// OR
//
// await either the response of the IoT device,
// or the delay to complete, effectively creating
// a timeout.
var timeoutOrComplete = await Task.WhenAny(
client.ReceiveAsync(),
Task.Delay(5000)
);
// If a response was received before the "timeout"
// was triggered, how do I get it?
var response = timeoutOrComplete.???
}
} catch {
// Ignore
}
Try following :
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
public class UdpState
{
public UdpClient u;
public IPEndPoint e;
public string receivedMessage;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
}
public static ManualResetEvent receiveDone = new ManualResetEvent(false);
public static void Main()
{
string ip = "172.0.0.1";
int port = 11111;
IPAddress iotAddress = IPAddress.Parse(ip);
IPEndPoint iotEndpoint = new IPEndPoint(iotAddress, port);
byte[] bytes = Encoding.UTF8.GetBytes("Hello World");
UdpState state = new UdpState();
try {
using (UdpClient client = new UdpClient(0, AddressFamily.InterNetwork)) {
client.Connect(iotEndpoint);
state.e = iotEndpoint;
state.u = client;
// await the response of the IoT device
client.BeginReceive(new AsyncCallback(ReceiveCallback), state);
client.BeginSend(bytes, bytes.Length, iotEndpoint, new AsyncCallback(SendCallback), client);
receiveDone.WaitOne();
var response = state.receivedMessage;
}
} catch {
// Ignore
}
}
public static void ReceiveCallback(IAsyncResult ar)
{
UdpState state = ar.AsyncState as UdpState;
UdpClient u = state.u;
IPEndPoint e = state.e;
state.buffer = u.EndReceive(ar, ref e);
state.receivedMessage = Encoding.ASCII.GetString(state.buffer);
receiveDone.Set();
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
UdpClient client = ar.AsyncState as UdpClient ;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}

How can I make a server program keep running no matter what happens to a client?

Take a look at the following two programs:
//Server
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MyServerProgram
{
class Program
{
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse("127.0.0.1");
int port = 2000;
TcpListener listener = new TcpListener(ip, port);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected " + ((IPEndPoint)client.Client.RemoteEndPoint).Address);
NetworkStream netStream = client.GetStream();
BinaryReader br = new BinaryReader(netStream);
try
{
while (client.Client.Connected)
{
string str = br.ReadString();
Console.WriteLine(str);
}
}
catch (Exception ex)
{
var inner = ex.InnerException as SocketException;
if (inner != null && inner.SocketErrorCode == SocketError.ConnectionReset)
Console.WriteLine("Disconnected");
else
Console.WriteLine(ex.Message);
br.Close();
netStream.Close();
client.Close();
listener.Stop();
}
}
}
}
//Client
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace MyClientProgram
{
class Program
{
static void Main(string[] args)
{
int port = 2000;
TcpClient client = new TcpClient("localhost", port);
NetworkStream netStream = client.GetStream();
BinaryWriter br = new BinaryWriter(netStream);
try
{
int i=1;
while (client.Client.Connected)
{
br.Write(i.ToString());
br.Flush();
i++;
int milliseconds = 2000;
System.Threading.Thread.Sleep(milliseconds);
}
}
catch
{
br.Close();
netStream.Close();
client.Close();
}
}
}
}
The problem I am facing with the Server is, the Server program exits as soon as the client is closed.
I want the server program to keep running no matter what a client does or happens to it.
How can I do that?
Try putting a while loop around your AcceptTcpClient (and associated logic).
To paraphrase from your server code:
boolean keepRunning = true;
while (keepRunning) {
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected ...") // and other stuff deleted.
// while client connected...
string str = br.ReadString();
// check to see if we should continue running.
keepRunning = ! "quit".equalsIgnoreCase(str);
// Other stuff
Note this is very insecure - any client regardless of where / who they are could terminate your server be sending a "quit" message to your server. In real life, you would probably require a more strict mechanism. Obviously with this mechanism, you will need your client to be able to generate the "quit" message text when you need it to do so.
Another method is to run the whole server in a Thread. Then in another thread, have a method that an operator could use to close the server (e.g. a menu selection in a Swing Application).
There are plenty of options you could choose from to "manage" the shutdown.
Also, as written, your code is single threaded. That is, it will wait for a client to connect, deal with that client and then exit (or if you apply the keepRunning while loop modification wait for the next client to connect). But, only one client can connect to this server at any one time.
To make it multi-threaded (can service multiple clients at one time), put the body of your server (the service code) into a Thread and invoke a new instance of the Thread to serve that client. After starting the service Thread, the main loop simply waits for the next client to connect.
Thus, your main loop will become something like this:
while (keepRunning) {
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Connected ...") // and other stuff deleted.
ServiceThread st = new ServiceThread(client);
st.start ();
}
and the Service Thread will be something like:
public class ServiceThread extends Thread {
private TcpClient client;
public ServiceThread (TcpClient client) {
this.client = client;
}
#override
public void run() {
NetworkStream netStream = client.GetStream();
BinaryReader br = new BinaryReader(netStream);
try {
while (client.Client.Connected) {
// Stuff deleted for clarity
}
}
catch (Exception ex) {
// Exception handling stuff deleted for clarity.
}
}
}

C# Find clients then create socket connection

I have been experimenting with some code to find other computer's on my network running my app. Firstly I broadcast and listen for those broadcasts. After that I am a little lost on the next step. Once Computer A knows the ip address of Computer B because of the broadcast how do I put Computer B in a state to accept socket connections. Then how does Computer A begin that connection?
Also I have tried doing the broadcasting with Multicast and cannot get that to work. If anyone has extra time could they show that as well.
Starting Listener and Sender:
private static void StartServer()
{
Task.Factory.StartNew(async () =>
{
await StartListeningAsync();
});
}
private static void StartClient()
{
Task.Factory.StartNew(async () =>
{
await StartSendingAsync();
});
}
Those inner methods:
private static async Task StartSendingAsync()
{
try
{
using (UdpClient _broadcaster = new UdpClient())
{
var ipHost = Dns.GetHostEntry("");
var ipAddr = ipHost.AddressList[ipHost.AddressList.Count() - 1];
IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, PORT);
var ipAddress = Encoding.ASCII.GetBytes(ipAddr.ToString());
for (int x = 0; x < 10; x++)
{
_broadcaster.Send(ipAddress, ipAddress.Count(), ip);
await Task.Delay(TimeSpan.FromSeconds(6));
}
}
}
catch (Exception ex)
{
}
}
private static async Task StartListeningAsync()
{
while (true)
{
using (UdpClient _listener = new UdpClient(PORT))
{
try
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT);
var incoming = await _listener.ReceiveAsync();
NewPing(incoming.Buffer, incoming.RemoteEndPoint);
await Task.Delay(TimeSpan.FromSeconds(10));
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut)
{
//Time out that's fine
}
}
}
}
}
What happens when the listener hears a broadcast:
private static void NewPing(byte[] incoming, IPEndPoint rem)
{
Console.WriteLine($"New ping from {rem.ToString()}, attempting to connect!");
//What should happen here now that one of the computer's know the other exists?
//Start some kind of Socket Accepting method?
}
If I understand correctly, you want a machine to send out a UDP broadcast, and then establish a TCP connection between the two?
I'd perform the following steps:
B is listening for UDP broadcasts
A wants a TCP connection with B, so
A starts listening for incoming TCP connections
A sends out a broadcast saying "I'm listening"
B receives the broadcast and attempts to connect to A
I've created two sample console applications, one server, one client:
Server:
using System;
using System.Net;
using System.Net.Sockets;
class Program
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, 11220));
listener.Start();
listener.AcceptSocketAsync().ContinueWith((t) => HandleClient(t.Result));
Console.WriteLine($"Listening for TCP connections at port 11220...");
using (UdpClient broadcaster = new UdpClient(AddressFamily.InterNetwork))
{
broadcaster.EnableBroadcast = true;
Console.WriteLine("Broadcasting...");
broadcaster.Send(new byte[] { 1, 2, 3 }, 3, new IPEndPoint(IPAddress.Broadcast, 11221));
}
while (Console.ReadKey(true).Key != ConsoleKey.Q)
{
Console.WriteLine("Press 'Q' to quit");
}
}
static void HandleClient(Socket socket)
{
Console.WriteLine($"TCP connection accepted ({socket.RemoteEndPoint})!");
}
}
Client:
using System;
using System.Net;
using System.Net.Sockets;
class Program
{
static void Main(string[] args)
{
using (UdpClient client = new UdpClient(11221, AddressFamily.InterNetwork))
{
Console.WriteLine("Waiting for broadcast...");
client.EnableBroadcast = true;
client.ReceiveAsync().ContinueWith((t) => HandleBroadcast(t.Result)).Wait();
}
Console.WriteLine("Press any key to continue");
Console.ReadKey(true);
}
static void HandleBroadcast(UdpReceiveResult result)
{
Console.WriteLine($"Received broadcast from {result.RemoteEndPoint}, attempting to connect via TCP");
Socket socket = new Socket(result.RemoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(result.RemoteEndPoint.Address, 11220));
}
}
First start the client, then the server. The data sent in the broadcast is just dummy data, but you could include some information like the TCP port number the server is listening at.
PS. Remember that this is just a proof of concept.

TcpClient.Close() works only with Thread.Sleep()

I have simple server that gets string from client and prints it on screen.
I also have simple client, sending data and closing:
static void Main()
{
var client = new TcpClient("localhost", 26140);
var stream = client.GetStream();
Byte[] data = System.Text.Encoding.UTF8.GetBytes("CALC qwer");
stream.Write(data, 0, data.Length);
stream.Close();
client.Close();
//Thread.Sleep(100);
}
And with uncommented string 'Thread.Sleep(100)' it works ok.
But when commenting, sometimes ( 1 of 5-10 runs ) client doesn't send the string.
Watching wireshark and netstat I've noticed that client sends SYN,ACK package, establishes connection and exits without sending anything and without closing the socket.
Could anyone explain this behaivor? Why sleep helps? What am I doing wrong?
UPD:
With this sample code adding flush() before closing really works, thanks Fox32.
But after it I returned to my initial code:
var client = new TcpClient("localhost", 26140);
client.NoDelay = true;
var stream = client.GetStream();
var writer = new StreamWriter(stream);
writer.WriteLine("CALC qwer");
writer.Flush();
stream.Flush();
stream.Close();
client.Close();
And it isn't working, even with NoDelay. It's bad - using StreamWriter over network stream?
UPD:
Here is server code:
static void Main(string[] args)
{
(new Server(26140)).Run();
}
In Server class:
public void Run()
{
var listener = new TcpListener(IPAddress.Any, port);
listener.Start();
while (true)
{
try
{
var client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted: " + client.Client.RemoteEndPoint);
var stream = client.GetStream();
stream.ReadTimeout = 2000;
byte[] buffer = new byte[1000];
stream.Read(buffer, 0, 1000);
var s = Encoding.UTF8.GetString(buffer);
Console.WriteLine(s);
}
catch (Exception ex)
{
Console.WriteLine("ERROR! " + ex.Message);
}
}
}
UPD:
Adding even Sleep(1) makes crashes happen in 1 of 30-50 clients running at the same time.
And adding Sleep(10) seems to be solving it totally, I can't catch any crash.
Don't understand, why socket needs this several milliseconds to close correctly.
The TcpClient is using the Nagle's algorithm and waits for more data before sending it over the wire. If you close the socket to fast, no data is trasmitted.
You have multiple ways to solve this problem:
The NetworkStream has a Flush method for flushing the stream content (I'm not sure if this method does anything from the comment on MSDN)
Disable Nagle's algorithm: Set NoDelay of the TcpCLient to true.
The last option is to set the LingerState of the TcpClient. The Close method documentation states, that the LingerState is used while calling Close
In almost all cases you are supposed to call Shutdown on a Socket or TcpClient before disposing it. Disposing rudely kills the connection.
Your code basically contains a race condition with the TCP stack.
Setting NoDelay is also a fix for this but hurts performance. Calling Flush IMHO still results an an disorderly shutdown. Don't do it because they are just hacks which paint over the problem by hiding the symptoms. Call Shutdown.
I want to stress that Shutdown being called on the Socket is the only valid solution that I know of. Even Flush just forces the data onto the network. It can still be lost due to a network hickup. It will not be retransmitted after Close has been called because Close is a rude kill on the socket.
Unfortunately TcpClient has a bug which forces you to go to the underlying Socket to shut it down:
tcpClient.Client.Shutdown();
tcpClient.Close();
According to Reflector, if you have ever accessed GetStream this problem arises and Close does not close the underlying socket. In my estimation this bug was produced because the developer did not really know about the importance of Shutdown. Few people know and many apps are buggy because of it. A related question.
In your server side code you are only calling Read() once, but you can't assume the data will be available when you call read. You have to continue reading in a loop until no more data is available. See the full example below.
I have tried to reproduce your issue with the minimal amount of code and was not able to. The server prints out the clients message everytime. No special settings such as NoDelay and no explicit Close() or Flush(), just Using statements which ensures all resources are properly disposed.
class Program
{
static int port = 123;
static string ip = "1.1.1.1";
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
StartServer();
waitHandle.WaitOne();
for (int x=0; x<1000; x++)
{
StartClient(x);
}
Console.WriteLine("Done starting clients");
Console.ReadLine();
}
static void StartClient(int count)
{
Task.Factory.StartNew((paramCount) =>
{
int myCount = (int)paramCount;
using (TcpClient client = new TcpClient(ip, port))
{
using (NetworkStream networkStream = client.GetStream())
{
using (StreamWriter writer = new StreamWriter(networkStream))
{
writer.WriteLine("hello, tcp world #" + myCount);
}
}
}
}, count);
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
try
{
TcpListener listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Listening...");
waitHandle.Set();
while (true)
{
TcpClient theClient = listener.AcceptTcpClient();
Task.Factory.StartNew((paramClient) => {
TcpClient client = (TcpClient)paramClient;
byte[] buffer = new byte[32768];
MemoryStream memory = new MemoryStream();
using (NetworkStream networkStream = client.GetStream())
{
do
{
int read = networkStream.Read(buffer, 0, buffer.Length);
memory.Write(buffer, 0, read);
}
while (networkStream.DataAvailable);
}
string text = Encoding.UTF8.GetString(memory.ToArray());
Console.WriteLine("from client: " + text);
}, theClient);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}, TaskCreationOptions.LongRunning);
}
}
UPD:
I've tested this bug on several computers and nothing crashed. Seems like it is a local bug on my computer.
ENDOFUPD
So, what I've found about reproducing this bug.
#Despertar - your code works well. But it isn't reproduce conditions of this bug. On client you need to send data and quit after it. And in your code many clients are sending data and after all application is closing.
This is how I'm testing this on my computer:
I have server ( just accepting connection and print incoming data ), client ( just sends data once end exits ) and running utility ( runs client exe several times ).
So, I starts server, copies running utility to the clients folder and runs it.
Running ulility starts 150 clients connecting to server and 5-10 of them dies ( I see error in the server console ). And uncommenting Thread.Sleep() on client works well, no errors.
Can anyone try to reproduce this version of code?
Client code:
private static void Main(string[] args)
{
try
{
using (TcpClient client = new TcpClient(ip, port))
{
using (NetworkStream networkStream = client.GetStream())
{
using (StreamWriter writer = new StreamWriter(networkStream))
{
writer.WriteLine("# hello, tcp world #");
writer.Flush();
}
networkStream.Flush();
networkStream.Close();
}
client.Close();
//Thread.Sleep(10);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Code, running client several times ( compile it in exe file and put near client's exe - this code will run many clients one by one ):
static void Main(string[] args)
{
string path = "YOU_CLIENT_PROJECT_NAME.exe";
for (int i = 0; i < 150; i++ )
{
Console.WriteLine(i);
Process.Start(path);
Thread.Sleep(50);
}
Console.WriteLine("Done");
Console.ReadLine();
}
( don't forget to change path to corrent exe filename )
Server code:
class Program
{
static int port = 26140;
static AutoResetEvent waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
StartServer();
waitHandle.WaitOne();
Console.ReadLine();
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
try
{
TcpListener listener = new TcpListener(port);
listener.Start();
Console.WriteLine("Listening...");
waitHandle.Set();
while (true)
{
TcpClient theClient = listener.AcceptTcpClient();
Task.Factory.StartNew(paramClient =>
{
try
{
TcpClient client = (TcpClient) paramClient;
byte[] buffer = new byte[32768];
MemoryStream memory = new MemoryStream();
using (NetworkStream networkStream = client.GetStream())
{
networkStream.ReadTimeout = 2000;
do
{
int read = networkStream.Read(buffer, 0, buffer.Length);
memory.Write(buffer, 0, read);
} while (networkStream.DataAvailable);
string text = Encoding.UTF8.GetString(memory.ToArray());
}
}
catch (Exception e)
{
Console.WriteLine("ERROR: " + e.Message);
}
}, theClient);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}, TaskCreationOptions.LongRunning);
}
}
I've tried code, reproducing this bug on several computers. No one crashes. Seems like it's my local computer bug.
Thanks for everybody for trying to help me.
Anyway, it's so strange. If I'll found out why this bug exists on my computer, I'll write about it.

c# sockets handling multiple clients

I have the following code which I want to implement as my server. As I understand it is async. and should allow connections from multiple clients...
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("Listening...");
while (true)
{
IAsyncResult res = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
connectionWaitHandle.WaitOne();
}
}
private void HandleAsyncConnection(IAsyncResult res)
{
TcpListener listener = (TcpListener)res.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(res);
connectionWaitHandle.Set();
StringBuilder sb = new StringBuilder();
var data = new byte[client.ReceiveBufferSize];
using (NetworkStream ns = client.GetStream())
{
// Test reply
Byte[] replyData = System.Text.Encoding.ASCII.GetBytes(DateTime.Now.ToString());
ns.Write(replyData, 0, replyData.Length);
ns.Flush();
ns.Close();
}
client.Close();
}
I have a test app which simply fires requests to my server. As you can see in the code the server just replies with its date/time. The test app sends say 20 requests which are simply test strings. For each of these requests it opens a socket, sends the data to my server and then closes the socket again.
This works fine with one test app running. However, if I open two test apps the second one cannot connect to the server. I thought because I am handling the request async. and because my test app opens then closes the socket before each call I could handle requests from multiple clients?
Edit
If using >=.Net4.5, it's better to use the new network methods that then permit the adoption of async and await. As such, it might be better to follow the example I provided in this post as a starting point.
Original Post
The following code demonstrates how to accept multiple clients asynchronously without spinning off a new thread per connection.
private TcpListener listener;
public void Start()
{
listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("Listening...");
StartAccept();
}
private void StartAccept()
{
listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
}
private void HandleAsyncConnection(IAsyncResult res)
{
StartAccept(); //listen for new connections again
TcpClient client = listener.EndAcceptTcpClient(res);
//proceed
}
You can use this pattern for most async operations.
The way you did it, there is no benefit compared to using AcceptTcpClient. Simply loop and create a new thread for each accepted connection:
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("Listening...");
while (canRun)
{
var client = listener.AcceptTcpClient();
new Thread(ClientThread).Start(client);
}
}
private void ClientThread(IAsyncResult res)
{
TcpClient client = (TcpClient)res.AsyncState;
StringBuilder sb = new StringBuilder();
var data = new byte[client.ReceiveBufferSize];
using (NetworkStream ns = client.GetStream())
{
// Test reply
Byte[] replyData = System.Text.Encoding.ASCII.GetBytes(DateTime.Now.ToString());
ns.Write(replyData, 0, replyData.Length);
ns.Flush();
ns.Close();
}
client.Close();
}
A golden rule when using asynchronous methods is to NOT block the operation. Blocking would defeat the purpose of using async ops.
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 10250);
listener.Start();
Console.WriteLine("Listening...");
listener.BeginAcceptTcpClient(OnAccept, listener);
}
private void OnAccept(IAsyncResult res)
{
TcpListener listener = (TcpListener)res.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(res);
StringBuilder sb = new StringBuilder();
var data = new byte[client.ReceiveBufferSize];
using (NetworkStream ns = client.GetStream())
{
// Test reply
Byte[] replyData = System.Text.Encoding.ASCII.GetBytes(DateTime.Now.ToString());
ns.Write(replyData, 0, replyData.Length);
ns.Flush();
ns.Close();
}
client.Close();
}

Categories