I'm used to Begin/end APM pattern and i would like to update my socket server to .Net 4.5/async/await. I wrote an sample code from internet sources and it's not working.
I would like to have all the connected clients separated to own classes after they been accepted to connect (not yet implemented..). The loop which accepts all incoming connections is running at own thread.
Basically Main.cs is the place where i accept client, create new class (Client.cs/Session.cs) for connection and point that accepted client to that class. Well, that is what i'm planning to do and its not in the code and the main problem currently are my knowledge of how to handle this accepting sequence and why i cant connect more than one client at the time ? I hope you can point me to correct answer.
Thank you in advance.
Codes
Form1.cs
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Server
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Own thread for accepting connections
Main ConnectionLoop = new Main(10);
Thread thread = new Thread(new ThreadStart(ConnectionLoop.PrepareThread));
thread.IsBackground = true;
thread.Start();
}
}
}
Main.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Server
{
public class Main
{
private int m_backLog = 0;
// constructor
public Main(int Backlog)
{
m_backLog = Backlog;
Console.WriteLine("Main class created, backlog: {0}", Backlog);
}
public void PrepareThread()
{
Console.WriteLine("Thread created");
StartAccepting(CancellationToken.None).Wait();
}
private async Task StartAccepting(CancellationToken token)
{
Console.WriteLine("Started listening..");
CancellationTokenSource cts = new CancellationTokenSource();
TcpListener listener = new TcpListener(IPAddress.Any, 6112);
listener.Start();
await AcceptClientsAsync(listener, cts.Token);
// Thread.Sleep(600000);
}
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
await EchoAsync(client, clientCounter, token);
}
}
private async Task EchoAsync(TcpClient client, int clientCounter, CancellationToken token)
{
using (client)
{
var buf = new byte[4096]; // buffer for stream
var stream = client.GetStream(); // stream itself
while (!token.IsCancellationRequested)
{
// some conditions we don't know is client connected, lets have timeout
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, token);
var completedTask = await Task.WhenAny(timeoutTask, amountReadTask).ConfigureAwait(false);
if (completedTask == timeoutTask)
{
var msg = Encoding.ASCII.GetBytes("Client timed out");
await stream.WriteAsync(msg, 0, msg.Length);
break;
}
var amountRead = amountReadTask.Result;
if (amountRead == 0) break; // end of stream
await stream.WriteAsync(buf, 0, amountRead, token).ConfigureAwait(false);
}
}
Console.WriteLine("Client {0} disconnected", clientCounter);
}
}
}
Async is for long running processes, and prevents unnecessary waiting on long running I/O bound processes. It DOESN'T provide any kind of concurrency whatsoever. It just frees up the CPU so it doesn't sit around waiting.
So you will need to utilize the rest of the TPL (Task Parallel Library) to provide the concurrency you need to allow simultaneous clients. Likely this means spinning off a Task for the client once the connection occurs, and using that Task to manage the client.
Async can compliment that by making sure each client doesn't block a full thread, but by itself async only helps you not block, it doesn't provide concurrency.
First off, I would like to strongly recommend reading all of the info over at MSDN about the TPL. It's alot, but it is good reading and will help immensely.
https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
As for a more concrete example, I can try.
In your loop where you accept clients, you will want to gain parallelism as soon as you have a connected client. This will enable your run loop to go back to accepting clients, and let the client still interact. So for example:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
Task.Run(async () => await EchoAsync(client, clientCounter, token), token);
}
}
Note this is psuedo code. I didn't try compiling it. But the concept is solid.
If you have large amounts of clients you will need to be more specific than just using Task.Run, but for a sample it works fine. It will utilize Thread pool threads for the parallelism. Which works fine for at least 100, but can decrease in performance after that.
An async function returns a task that is completed when that function completes.
In your AcceptClientsAsync function, you're awaiting the EchoAsync function. This means that AcceptTcpClientAsync will not be called again until after EchoAsync completes (that is, after the cancellation token is signalled).
For an asynchronous socket server, you should have one task that only accepts in a loop, and for each connection, you should have a "processing" task. These have to be independent - you can't await the processing task from the accepting task.
Update: Adding example, as per request:
private async Task AcceptClientsAsync(TcpListener listener, CancellationToken token)
{
var clientCounter = 0;
while (!token.IsCancellationRequested)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
clientCounter++;
Console.WriteLine("Client {0} accepted!", clientCounter);
var echoTask = EchoAsync(client, clientCounter, token);
// TODO: save the echoTask in some kind of per-client data structure.
}
}
Related
I'm trying out the timeout policy with polly.
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
timeoutPolicy().GetAwaiter().GetResult();
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
Console.ReadKey();
}
static async Task timeoutPolicy()
{
AsyncTimeoutPolicy<HttpResponseMessage> timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(), CancellationToken.None);
}
static Task<HttpResponseMessage> LongOperation()
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
Thread.Sleep(5000); // Sleep 5 seconds
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
});
}
I expect an exception to be thrown after 1 sec passed because that is the timeout upper limit I set up. But currently, no exception will be thrown and the method LongOperation() returns normally after around 5 secs.
Why does the timeout policy not work in this case?
Why does the timeout policy not work in this case?
Polly's TimeoutPolicy exists in two modes:
TimeoutStrategy.Optimistic expects governed delegates to respond to co-operative cancellation by CancellationToken.
TimeoutStrategy.Pessimistic allows the calling code to walk away from waiting for a delegate that doesn't respond to co-operative cancellation.
Optimistic mode is the default, so your posted code uses this. But (a) LongOperation() in the posted code does not respond to co-operative cancellation; so the policy does not time it out.
Pessimistic mode with asynchronous policies is intentionally designed only to govern delegates which conform to the normal async pattern. Thread.Sleep() in the posted LongOperation() is fully sychronous; so your example would additionally not be timed out just by switching to TimeoutStrategy.Pessimistic.
TimeoutStrategy.Optimistic is the best simulation of calls through HttpClient, as those calls do respond to CancellationToken.
Async timeout policy's optimistic mode timing out a long operation can be simulated with await Task.Delay(...) honouring a CancellationToken, like this:
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Polly;
using Polly.Timeout;
public class Program
{
public static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
try {
timeoutPolicy().GetAwaiter().GetResult();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
stopwatch.Stop();
Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
}
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(ct), CancellationToken.None);
}
static async Task<HttpResponseMessage> LongOperation(CancellationToken token)
{
await Task.Delay(5000, token);
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
}
}
Right now you're using Optimistic timeout which expects the delgate you're calling to respect the and respond to the cancellation token. Your delegate does not, in this case you need to use Pessimistic timeout to ensure your caller doesn't wait passed the defined timeout.
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1, TimeoutStrategy.Pessimistic); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(), CancellationToken.None);
}
Polly Timeout as stated in the docs Optimistic is the default.
As #moutain traveller already pointed out, optimistic timeout requires you to to pass and co-operate with the timeout cancellation. This is handled already by api calls using the HttpClient but in this sample case your code would look something like this:
static async Task timeoutPolicy()
{
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(1, TimeoutStrategy.Optimistic); // setup the timeout limit to be 1 sec
HttpResponseMessage response = await timeoutPolicy.ExecuteAsync((ct) => LongOperation(ct), CancellationToken.None);
}
static Task<HttpResponseMessage> LongOperation(CancellationToken token)
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var longTask = Task.Delay(5000);
while (!longTask.IsCompleted)
{
token.ThrowIfCancellationRequested();
}
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest
};
});
}
I am working on refactoring code that uses the Bootstrap protocol to update the firmware of several nodes in a machine. The current code looks something like this (pseudo-code):
public void StartUpdate()
{
Sokcet bootpSocket = new Socket():
StateObject bootpState = new StateObject(bootpSocket);
BOOTPReceive(bootpState);
SendMagicPacket();
while (!IsError && !IsUpdateComplete)
{
//wait for BOOTP/Update to finish before returning to caller
}
}
private void BOOTPReceive(object state)
{
bOOTPSocket.BeginReceive(PACKET_DATA, 0, PACKET_DATA.Length, 0, OnBOOTPReceive, state);
}
SendMagicPacket()
{
//create and send magic packet
// this will tell the node to respond with a BOOTPPacket
}
private void OnBOOTPReceive(IAsyncResult result)
{
StateObject state = (StateObject) result.AsyncState;
Socket handler = state.workSocket;
int bytesRcvd = handler.EndReceive(result);
packet = PACKET_DATA;
if(isValidBOOTP(packet))
{
SendBOOTPResponse();
}
else{
BOOTPReceive(); //keep listening for valid bootp response
}
}
private void SendBOOTPResponse()
{
UdpClient udpClient = new UdpClient();
udpClient.BeginSend(packetData, packetData.Length, BROADCAST_IP, (int)UdpPort.BOOTP_CLIENT_PORT, OnBOOTPSend, udpClient);
}
private void OnBOOTPSend(IAsyncResult result)
{
UdpClient udpClient = (UdpClient)result.AsyncState;
int bytesSent = udpClient.EndSend(result);
udpClient.Close();
}
What I want to do is convert this to async-await but still require that I don't return back to the caller right away. How would I go about doing this? Is this possible to do? And would this be the right thing to do since await-async propagates all the way to the top?
Pseudo-code of what I think this would look like:
public void StartUpdate()
{
bool result = await SendMagicPacket();
bool IsError = await BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
//don't return to caller until BOOTPCommunication is completed. How do i do this?
}
You need to wait for the two tasks try the following:
public async Task StartUpdate()
{
var resultTask = SendMagicPacket();
var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
await Task.WhenAll(new[]{resultTask, isErrorTask});
//don't return to caller until BOOTPCommunication is completed. How do i do this?
}
//wait for BOOTP/Update to finish before returning to caller
You don't need any async IO at all because you want to wait until all operations are done. I assume you have copied some sample code. Most sample code uses async socket APIs.
Switch everything over to synchronous socket APIs and you're done.
If you want to keep this async for some reason you can indeed switch to await and untangle this code. The pseudo-code you posted looks like a good goal. It forces the surrounding method to be async Task, though.
You can deal with that by making all callers recursively async as well. If you don't need to conserve threads you could block on that task and have a mostly synchronous call chain. At that point you lose all async benefits, though.
Radin was on the right track, but I think what you want is something like this:
You need to wait for the two tasks try the following:
public async Task StartUpdate()
{
var resultTask = SendMagicPacket();
var isErrorTask = BOOTPCommunication(); //Handles all of the BOOTP recieve/sends
Task.WhenAll(new[]{resultTask, isErrorTask}).Wait(); //Wait() will block so that the method doesn't return to the caller until both of the asynchronous tasks complete.
}
What that allows is SendMagicPacket and BOOTPCommunication to both fire simultaneously, but to wait for BOTH to complete. Using that pattern you can fire of N events simultaneously, while using Wait() to wait for all to finish so that the method itself returns synchronously.
How should the reading of bulk data from a device in C# be handled in .NET 4.0? Specifically I need to read quickly from a USB HID device that emits reports over 26 packets where order must be preserved.
I've tried doing this in a BackgroundWorker thread. It reads one packet from the device at a time, and process it, before reading more. This gives reasonably good response times, but it is liable to lose a packet here and there, and the overhead costs of a single packet read adds up.
while (!( sender as BackgroundWorker ).CancellationPending) {
//read a single packet
//check for header or footer
//process packet data
}
}
What is the best practice in C# for reading a device like this?
Background:
My USB HID device continuously reports a large amount of data. The data is split over 26 packets and I must preserver the order. Unfortunately the device only marks the first the last packets in each report, so I need to be able to catch every other packet in between.
For .Net 4 you can use a BlockingCollection to provide a threadsafe queue that can be used by a producer and a consumer. The BlockingCollection.GetConsumingEnumerable() method provides an enumerator which automatically terminates when the queue has been marked as completed using CompleteAdding() and is empty.
Here's some sample code. The payload is an array of ints in this example, but of course you would use whatever data type you need.
Note that for your specific example, you can use the overload of GetConsumingEnumerable() which accepts an argument of type CancellationToken.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
public static class Program
{
private static void Main()
{
var queue = new BlockingCollection<int[]>();
Task.Factory.StartNew(() => produce(queue));
consume(queue);
Console.WriteLine("Finished.");
}
private static void consume(BlockingCollection<int[]> queue)
{
foreach (var item in queue.GetConsumingEnumerable())
{
Console.WriteLine("Consuming " + item[0]);
Thread.Sleep(25);
}
}
private static void produce(BlockingCollection<int[]> queue)
{
for (int i = 0; i < 1000; ++i)
{
Console.WriteLine("Producing " + i);
var payload = new int[100];
payload[0] = i;
queue.Add(payload);
Thread.Sleep(20);
}
queue.CompleteAdding();
}
}
}
For .Net 4.5 and later, you could use the higher-level classes from Microsoft's Task Parallel Library, which has a wealth of functionality (and can be somewhat daunting at first sight).
Here's the same example using TPL DataFlow:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace Demo
{
public static class Program
{
private static void Main()
{
var queue = new BufferBlock<int[]>();
Task.Factory.StartNew(() => produce(queue));
consume(queue).Wait();
Console.WriteLine("Finished.");
}
private static async Task consume(BufferBlock<int[]> queue)
{
while (await queue.OutputAvailableAsync())
{
var payload = await queue.ReceiveAsync();
Console.WriteLine("Consuming " + payload[0]);
await Task.Delay(25);
}
}
private static void produce(BufferBlock<int[]> queue)
{
for (int i = 0; i < 1000; ++i)
{
Console.WriteLine("Producing " + i);
var payload = new int[100];
payload[0] = i;
queue.Post(payload);
Thread.Sleep(20);
}
queue.Complete();
}
}
}
If missing packets is a concern do not do your processing and your reading on the same thread. Starting with .NET 4.0 they added the System.Collections.Concurrent namespace which makes this very easy to do. All you need is a BlockingCollection which behaves as a queue for your incoming packets.
BlockingCollection<Packet> _queuedPackets = new BlockingCollection<Packet>(new ConcurrentQueue<Packet>());
void readingBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!( sender as BackgroundWorker ).CancellationPending)
{
Packet packet = GetPacket();
_queuedPackets.Add(packet);
}
_queuedPackets.CompleteAdding();
}
void processingBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<Packet> report = new List<Packet>();
foreach(var packet in _queuedPackets.GetConsumingEnumerable())
{
report.Add(packet);
if(packet.IsLastPacket)
{
ProcessReport(report);
report = new List<Packet>();
}
}
}
What will happen is while _queuedPackets is empty _queuedPackets.GetConsumingEnumerable() will block the thread not consuming any resources. As soon as a packet arrives it will unblock and do the next iteration of the foreach.
When you call _queuedPackets.CompleteAdding(); the foreach on your processing thread will run till the collection is empty then exit the foreach loop. If you don't want it to "finish up the queue" when you cancel you can easily change it up to quit early. I also am going to switch to using Tasks instead of Background workers because it makes the passing in parameters much easier to do.
void ReadingLoop(BlockingCollection<Packet> queue, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
Packet packet = GetPacket();
queue.Add(packet);
}
queue.CompleteAdding();
}
void ProcessingLoop(BlockingCollection<Packet> queue, CancellationToken token)
{
List<Packet> report = new List<Packet>();
try
{
foreach(var packet in queue.GetConsumingEnumerable(token))
{
report.Add(packet);
if(packet.IsLastPacket)
{
ProcessReport(report);
report = new List<Packet>();
}
}
}
catch(OperationCanceledException)
{
//Do nothing, we don't care that it happened.
}
}
//This would replace your backgroundWorker.RunWorkerAsync() calls;
private void StartUpLoops()
{
var queue = new BlockingCollection<Packet>(new ConcurrentQueue<Packet>());
var cancelRead = new CancellationTokenSource();
var cancelProcess = new CancellationTokenSource();
Task.Factory.StartNew(() => ReadingLoop(queue, cancelRead.Token));
Task.Factory.StartNew(() => ProcessingLoop(queue, cancelProcess.Token));
//You can stop each loop indpendantly by calling cancelRead.Cancel() or cancelProcess.Cancel()
}
My windows app's requirement are:
Using HttpWebRequest get web request/response every 3 seconds in one thread.(total is about 10 threads for doing this web request/response.)
Each thread use some global variables.
I want to use a System.Timers.Timer and async and await. But I don't know that is a best way for high performance. And then how to test them. I am a green in C#.
You could write a RepeatActionEvery() method as follows.
It's parameters are:
action - The action you want to repeat every so often.
interval - The delay interval between calling action().
cancellationToken - A token you use to cancel the loop.
Here's a compilable console application that demonstrates how you can call it. For an ASP application you would call it from an appropriate place.
Note that you need a way to cancel the loop, which is why I pass a CancellationToken to RepeatActionEvery(). In this sample, I use a cancellation source which automatically cancels after 8 seconds. You would probably have to provide a cancellation source for which some other code called .Cancel() at the appropriate time. See here for more details.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
sealed class Program
{
void run()
{
CancellationTokenSource cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(8));
Console.WriteLine("Starting action loop.");
RepeatActionEvery(() => Console.WriteLine("Action"),
TimeSpan.FromSeconds(1), cancellation.Token).Wait();
Console.WriteLine("Finished action loop.");
}
public static async Task RepeatActionEvery(Action action,
TimeSpan interval, CancellationToken cancellationToken)
{
while (true)
{
action();
Task task = Task.Delay(interval, cancellationToken);
try
{
await task;
}
catch (TaskCanceledException)
{
return;
}
}
}
static void Main(string[] args)
{
new Program().run();
}
}
}
I need to make TcpClient event driven rather than polling for messages all the time, so I thought: I will create a thread that would wait for a message to come and fire an event once it does. Here is a general idea:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace ThreadsTesting
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
//imitate a remote client connecting
TcpClient remoteClient = new TcpClient();
remoteClient.Connect(IPAddress.Parse("127.0.0.1"), 80);
//start listening to messages
p.startMessageListener();
//send some fake messages from the remote client to our server
for (int i = 0; i < 5; i++)
{
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
}
//sleep for a while to make sure the cpu is not used
Console.WriteLine("Sleeping for 2sec");
Thread.Sleep(2000);
//attempt to stop the server
p.stopMessageListener();
Console.ReadKey();
}
private CancellationTokenSource cSource;
private Task listener;
private TcpListener server;
private TcpClient client;
public Program()
{
server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
server.Start();
}
private void startMessageListener()
{
client = server.AcceptTcpClient();
//start listening to the messages
cSource = new CancellationTokenSource();
listener = Task.Factory.StartNew(() => listenToMessages(cSource.Token), cSource.Token);
}
private void stopMessageListener()
{
Console.Out.WriteLine("Close requested");
//send cancelation signal and wait for the thread to finish
cSource.Cancel();
listener.Wait();
Console.WriteLine("Closed");
}
private void listenToMessages(CancellationToken token)
{
NetworkStream stream = client.GetStream();
//check if cancelation requested
while (!token.IsCancellationRequested)
{
//wait for the data to arrive
while (!stream.DataAvailable)
{ }
//read the data (always 1 byte - the message will always be 1 byte)
byte[] bytes = new byte[1];
stream.Read(bytes, 0, 1);
Console.WriteLine("Got Data");
//fire the event
}
}
}
}
This for obvious reasons doesn't work correctly:
while (!stream.DataAvailable) blocks the thread and uses always 25% CPU (on 4-core CPU), even if no data is there.
listener.Wait(); will wait for ever since the while loop doesn't pick up that cancel has been called.
My alternative solution would be using async calls within the listenToMessages method:
private async Task listenToMessages(CancellationToken token)
{
NetworkStream stream = client.GetStream();
//check if cancelation requested
while (!token.IsCancellationRequested)
{
//read the data
byte[] bytes = new byte[1];
await stream.ReadAsync(bytes, 0, 1, token);
Console.WriteLine("Got Data");
//fire the event
}
}
This works exactly as I expected:
The CPU is not blocked if there are no messages in the queue, but we are still waiting for them
Cancelation request is picked up correctly and thread finished as expected
I wanted to go further though. Since listenToMessages now returns a Task itself, I thought there is no need of starting a task that would execute that method. Here is what I did:
private void startMessageListener()
{
client = server.AcceptTcpClient();
//start listening to the messages
cSource = new CancellationTokenSource();
listener = listenToMessages(cSource.Token);
}
This doesn't work as I have expected in the sence that when Cancel() is called, the ReadAsync() method doesn't seem to pick up the cancelation message from the token, and the thread doesn't stop, instead it is stuck on the ReadAsync() line.
Any idea why is this happening? I would think the ReadAsync will still pick up the token, as it did before...
Thanks for all your time and help.
-- EDIT --
Ok so after more in depth evaluation my solution no.2 doesn't really work as expected:
the thread itself ends to the caller and so the caller can continue. However, the thread is not "dead", so if we send some data it will execute once more!
Here is an example:
//send some fake messages from the remote client to our server
for (int i = 0; i < 5; i++)
{
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
}
Console.WriteLine("Sleeping for 2sec");
Thread.Sleep(2000);
//attempt to stop the server
p.stopListeners();
//check what will happen if we try to write now
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
Console.ReadKey();
This will output the message "Got Data" even though in theory we stopped! I will investigate further and report on my findings.
With modern libraries, any time you type new Thread, you've already got legacy code.
The core solution for your situation is asynchronous socket methods. There are a few ways to approach your API design, though: Rx, TPL Dataflow, and plain TAP come to mind. If you truly want events then EAP is an option.
I have a library of EAP sockets here. It does require a synchronizing context, so you'd have to use something like ActionDispatcher (included in the same library) if you need to use it from a Console application (you don't need this if you're using it from WinForms/WPF).
ReadAsync doesn't seem to support cancellation on a NetworkStream - have a look at the answers in this thread:
NetworkStream.ReadAsync with a cancellation token never cancels