I am working on an app that has an HttpListener. My goal is for the user to turn the listener off and on as they choose. I put the Listener in a new thread and I'm having a problem aborting that thread. I read somewhere that if you try to abort a thread that is in an unmanaged context, then as soon as it re-enters a managed context the ThreadAbortException will be fired. It appears that an HttpListener's GetContext() method is unmanaged because when I try to abort the thread nothing happens until I make a web request against my app. THEN the thread exits. The problem is when I attempt to kill the thread, I may start up the thread again later on the same port and an HttpListenerException goes off saying that the prefix is already registered.
How can I kill a cross thread HttpListener? Is there a managed alternative to GetContext() that will allow the thread to abort? Can I abort the thread in a way that unmanaged code will halt?
What about something like:
public class XListener
{
HttpListener listener;
public XListener(string prefix)
{
listener = new HttpListener();
listener.Prefixes.Add(prefix);
}
public void StartListen()
{
if (!listener.IsListening)
{
listener.Start();
Task.Factory.StartNew(async () =>
{
while (true) await Listen(listener);
}, TaskCreationOptions.LongRunning);
Console.WriteLine("Listener started");
}
}
public void StopListen()
{
if (listener.IsListening)
{
listener.Stop();
Console.WriteLine("Listener stopped");
}
}
private async Task Listen(HttpListener l)
{
try
{
var ctx = await l.GetContextAsync();
var text = "Hello World";
var buffer = Encoding.UTF8.GetBytes(text);
using (var response = ctx.Response)
{
ctx.Response.ContentLength64 = buffer.Length;
ctx.Response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
catch (HttpListenerException)
{
Console.WriteLine("screw you guys, I'm going home!");
}
}
}
Usage:
var x = new XListener("http://locahost:8080");
x.StartListen();
Thread.Sleep(500); // test purpose only
x.StopListen();
Thread.Sleep(500); // test purpose only
x.StartListen();
/* OUTPUT:
=> Listener started
=> Listener stopped
=> screw you guys, I'm going home!
=> Listener started */
You need to signal the thread to call HttpListener.Stop() and wait for the thread to finish by calling Thread.Join()
All you need to do is call stop on the listener. Since your listener thread is blocked on GetContext you will need to do this on another thread. IIRC this will cause GetContext to throw, so you will want to handle that exception and clean up. Calling Thread.Abort should be your last resort and wont cause the listener to stop listening until it is garbage collected anyway.
using System;
using System.Net;
using System.Text;
class Server
{
HttpListener listener = new HttpListener();
public Server(string url)
{
listener.Prefixes.Add(url);
}
void Callback(IAsyncResult result)
{
HttpListenerContext context = listener.EndGetContext(result);
byte[] buffer = Encoding.UTF8.GetBytes("Hello world!");
context.Response.ContentLength64 = buffer.Length;
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
listener.BeginGetContext(new AsyncCallback(Callback), listener);
}
public void Start()
{
listener.Start();
listener.BeginGetContext(new AsyncCallback(Callback), listener);
}
public void Stop()
{
listener.Stop();
}
public void Close()
{
listener.Close();
}
}
Related
I'm working with a Windows socket application using async callbacks. If I use Thread to start _StartListening, when I call StopListening, the loop still stops at allDone.WaitOne(). But the Task version will be OK.
What's the difference?
My code is a modified version of this
The original version with ManualResetEvent has a race condition mentioned by felix-b. I changed it to SemaphoreSlim but the problem is still there.
I tried in debug mode and it seems that the break point never be hit at if (cancelToken.IsCancellationRequested) after I call StopListening even I don't start the client.
Sorry. I found that I accidentally started two socket servers. That's the problem.
class WinSocketServer:IDisposable
{
public SemaphoreSlim semaphore = new SemaphoreSlim(0);
private CancellationTokenSource cancelSource = new CancellationTokenSource();
public void AcceptCallback(IAsyncResult ar)
{
semaphore.Release();
//Do something
}
private void _StartListening(CancellationToken cancelToken)
{
try
{
while (true)
{
if (cancelToken.IsCancellationRequested)
break;
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
semaphore.Wait();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("Complete");
}
public void StartListening()
{
Task.Run(() => _StartListening(cancelSource.Token));//OK
var t = new Thread(() => _StartListening(cancelSource.Token));
t.Start();//Can't be stopped by calling StopListening
}
public void StopListening()
{
listener.Close();
cancelSource.Cancel();
semaphore.Release();
}
public void Dispose()
{
StopListening();
cancelSource.Dispose();
semaphore.Dispose();
}
}
Your code has a race condition that can lead to deadlock (sometimes). Let's name the threads "listener" (one that runs _StartListening) and "control" (one that runs StopListening):
Listener thread: if (cancelToken.IsCancellationRequested) -> false
Control thread: cancelSource.Cancel()
Control thread: allDone.Set()
Listener thread: allDone.Reset() -> accidentally resets the stop request!
Listener thread: listener.BeginAccept(...)
Control thread: stopListening() exits, while the listener continues to work!
Listener thread: allDone.WaitOne() -> deadlock! no one will do allDone.Set().
The problem is in how you use the allDone event, it should be the other way around: _StartListening should do allDone.Set() just before it exits for whatever reason, whereas StopListening should do allDone.WaitOne():
class WinSocketServer:IDisposable
{
// I guess this was in your code, necessary to show proper stopping
private Socket listener = new Socket(......);
public ManualResetEvent allDone = new ManualResetEvent(false);
private CancellationTokenSource cancelSource = new CancellationTokenSource();
private void _StartListening(CancellationToken cancelToken)
{
try
{
listener.Listen(...); // I guess
allDone.Reset(); // reset once before starting the loop
while (!cancelToken.IsCancellationRequested)
{
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
allDone.Set(); // notify that the listener is exiting
Console.WriteLine("Complete");
}
public void StartListening()
{
Task.Run(() => _StartListening(cancelSource.Token));
}
public void StopListening()
{
// notify the listener it should exit
cancelSource.Cancel();
// cancel possibly pending BeginAccept
listener.Close();
// wait until the listener notifies that it's actually exiting
allDone.WaitOne();
}
public void Dispose()
{
StopListening();
cancelSource.Dispose();
allDone.Dispose();
}
}
UPDATE
It worth noting that listener.BeginAccept won't return until there is a new client connection. When stopping the listener, it is necessary to close the socket (listener.Close()) to force BeginAccept to exit.
The difference in Thread/Task behavior is really weird, it probably can originate from the Task thread being a background thread, while the regular thread being a foreground one.
I want to create a simple TCP server in .NET Core 2.0 using asynchrony (because from what I understand, it's more reasonable than spawning threads) with the async/await approach (because I believe it's more up-to-date than the one with IAsyncResult and *Begin/*End methods).
I've written this small server that accepts new connections from clients and then begins to send them 100 messages (with a 1s delay between them).
The main question is:
If I'm not spawning new threads, then how the server continues to send delayed messages to several clients, when in fact it's "waiting for connection"? Are there any hidden low-level signals/events involved, or are there really just new threads?
The second question is:
If I'm not using this brand new async Main syntax sugar and I'm not "awaiting" the async task of sending the messages -- am I using the asynchrony correctly?
class Program
{
public static void Main(string[] args)
{
StartServer();
}
public static void StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = listener.AcceptTcpClient(); // synchronous
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // not awaited -- StartServer is not async
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
await stream.WriteAsync(msg, 0, msg.Length); // returning back to caller?
await Task.Delay(1000); // and what about here?
}
client.Close();
}
}
What is the difference between the original code and the version below? What difference does async Main make?
class Program
{
public static async Task Main(string[] args)
{
await StartServer();
}
public static async Task StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = await listener.AcceptTcpClientAsync(); // does it make any difference when done asynchronously?
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // cannot await here, because it blocks next connections
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
var result = stream.WriteAsync(msg, 0, msg.Length);
await Task.Delay(1000);
await result;
}
client.Close();
}
}
The answer to the main question:
In the background, as a rule, objects work with some api(winapi for windows). These api can implement asynchrony differently. For example, events(winapi events) or callbacks. So the answer is yes - there are hidden signals or threads. For example, you can see the Ping class. Ping.InternalSend using ThreadPool.RegisterWaitForSingleObject for the task of asynchrony.
The answer about async Main:
In your first code because StartServer is not async the Main method will not get back control until your "accept" cycle ends.
In your second code, the Main method will get back control then listener.AcceptTcpClientAsync invoked. But because you using await StartServer(); the Main method will be wait until StartServer ends.
Some code for to explain:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
// not await for exploration
var task = StartServer();
Console.WriteLine("Main StartServer finished");
task.Wait();
Console.WriteLine("task.Wait() finished");
Console.ReadKey();
}
static async Task StartServer()
{
Console.WriteLine("StartServer enter");
for(int i = 0; i < 3; i++) {
await Task.Delay(1000); // listener.AcceptTcpClientAsync simulation
SendHundredMessages();
}
Console.WriteLine("StartServer exit");
}
static async Task SendHundredMessages()
{
Console.WriteLine("SendHundredMessages enter");
await Task.Run(() => {
Thread.Sleep(2000);
});
Console.WriteLine("SendHundredMessages exit");
}
}
}
This code generate this output:
StartServer enter
Main StartServer finished
SendHundredMessages enter
SendHundredMessages enter
SendHundredMessages exit
SendHundredMessages enter
StartServer exit
task.Wait() finished
SendHundredMessages exit
SendHundredMessages exit
As you can see, the Main method continued execution right after first Task.Delay.
A warning:
You do not wait end of SendHundredMessages, and this is very bad. In example output you can see that "SendHundredMessages ending" after "task.Wait() finished". In example application of course it not danger, but in real project you can get big problem.
I have a windows service which is monitoring a Socket using TCP/IP protocol.As per my requirement my code is establishing a connection to the Machine and receiving data from there and this i want continuously,that's why i have made it in windows service.But the problem that i am facing is that, the service is reading socket ports for 3-4 hours after that automatically stops reading from the port whereas my service status from Services.msc shows its running.
Here is my code for windows Service..
string ipaddress, textfileSaveLocation;
Byte[] bBuf;
string buf;
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private System.Threading.Thread _thread;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
string df = "";
public Service1()
{
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
_thread = new Thread(DoWork);
_thread.Start();
}
private void DoWork()
{
// create and monitor socket here...
ipaddress = "192.168.1.100";
int port = int.Parse("8181");
byte[] data = new byte[1024];
string stringData;
string input;
IPAddress ipadd = IPAddress.Parse(ipaddress);
IPEndPoint ipend = new IPEndPoint(ipadd, port);
sock.NoDelay = false;
try
{
sock.Connect(ipend);
}
catch (Exception dfg)
{
return;
}
try
{
input = "Client here";
sock.Send(Encoding.ASCII.GetBytes(input));
while (!_shutdownEvent.WaitOne(0))
{
data = new byte[1024];
int recv = sock.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
}
}
catch (Exception DFGFD)
{
}
}
protected override void OnStop()
{
sock.Shutdown(SocketShutdown.Both);
sock.Close();
_shutdownEvent.Set();
_thread.Join(); // wait for thread to stop
}
}
}
Why is my service stopping receiving data after 3-4 hours ?Please help me to resolve this.
Here is my code to insert ServerMachine data into text file..
try
{
FileStream fs = new FileStream(textfileSaveLocation, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite);
StreamWriter swr = new StreamWriter(textfileSaveLocation,true);
swr.WriteLine(stringData);
swr.Close();
swr.Dispose();
fs.Close();
fs.Dispose();
}
catch (Exception Ex)
{
}
As #nobugz has pointed out, you have two problems.
First, you're swallowing any exceptions that occur in DoWork(). You're catching them, yes, but you're not reporting them. Why does it stop after a few hours? Well, the server might have closed, which would terminate your socket connection. You're not attempting to reconnect, so the thread exits, but the process continues to run. Put some exception reporting in to see why the socket is closing and then handle it accordingly.
And this leads to the second point, namely you have no recovery capability. Once an exception occurs or the socket is gracefully closed for some reason, your thread exits. If you want this to work continually, then you'll need to add logic that attempts to reconnect in the cases where something goes wrong. In other words, the only reason that DoWork() exits is due to a shutdown event. Otherwise, it will need to loop, attempting to reconnect when errors occur. As #nobugz said, this will also require you to reset the ManualResetEvent so that your receive loop will work as expected when a reconnect occurs. However, you do not need to call Reset() in the OnStart() callback because you've initialized _shutdownEvent to false, which is what Reset() does.
HTH.
EDIT:
This is off the cuff, so I won't verify its accuracy. I'll fix any problem you (or others) may find.
Define a ManualResetEvent object for shutdown notification. Change the OnStart() callback to this:
using System.Threading;
ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
Thread _thread;
protected override void OnStart(string[] args)
{
// Create the thread and start it.
_thread = new Thread(DoWork);
_thread.Start();
}
Since you're wanting a TCP connection, I would strongly recommend using TcpClient as opposed to Socket. Change your DoWork() callback to something like this:
using System.Net.Sockets;
private void DoWork()
{
while (!_shutdownEvent.WaitOne(0))
{
TcpClient client = new TcpClient();
try
{
// This is a blocking call. You might want to consider one of the
// asynchronous methods...
client.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.100"), 8181));
}
catch (Exception ex)
{
// Log the error here.
client.Close();
continue;
}
try
{
using (NetworkStream stream = client.GetStream())
{
byte[] notify = Encoding.ASCII.GetBytes("Client here");
stream.Write(notify, 0, notify.Length);
byte[] data = new byte[1024];
while (!_shutdownEvent.WaitOne(0))
{
int numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead > 0)
{
string msg = Encoding.ASCII.GetString(data, 0, numBytesRead);
}
}
}
}
catch (Exception ex)
{
// Log the error here.
client.Close();
}
}
}
Finally, in the OnStop() callback, trigger the thread to shutdown:
protected override void OnStop()
{
_shutdownEvent.Set(); // trigger the thread to stop
_thread.Join(); // wait for thread to stop
}
Now, it is very important to understand that TCP communication is stream-based. What this means is that you are not guaranteed to receive a complete message each time you do a read of the socket (e.g., TcpClient). If the server sends back the message "Client notification received", reading the socket initially might only get "Client noti". A subsequent read might get "fication recei". A third read might get "ved". It's up to you to buffer the reads together and then process the message. Most protocols will use some kind of header that indicates the type and length of the message. If you're simply using strings, the type of message won't matter since everything's a string. Further, knowing where the string ends could be done using a null terminator instead of prepending the length for example. Just know that reading a TCP socket may only get a portion of the message you're expecting to receive.
AcceptTcpClient() prevents app from exit after I called thrd.Abort().
How to exit application when in listening?
You should be able to interrupt the call to AcceptTcpClient() by closing the TcpListener (this will result in an exception being thrown by the blocking AcceptTcpClient(). You should not be aborting the thread, which is generally a very bad idea in all but a few very specific circumstances.
Here's a brief example:
class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Any, 12343);
var thread = new Thread(() => AsyncAccept(listener));
thread.Start();
Console.WriteLine("Press enter to stop...");
Console.ReadLine();
Console.WriteLine("Stopping listener...");
listener.Stop();
thread.Join();
}
private static void AsyncAccept(TcpListener listener)
{
listener.Start();
Console.WriteLine("Started listener");
try
{
while (true)
{
using (var client = listener.AcceptTcpClient())
{
Console.WriteLine("Accepted client: {0}", client.Client.RemoteEndPoint);
}
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("Listener done");
}
}
The code above starts a listener on a separate thread, pressing Enter on the console window will stop the listener, wait for the listener thread to complete, then the application will exit normally, no thread aborts required!
You could:
Use BeginAcceptTcpClient() and End.. instead:
See:https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.beginaccepttcpclient(v=vs.110).aspx
Or your could:
Create a TcpClient and send your listener message:
therefore (I guess you have a loop in your thread):
break the loop wherein the listener.AcceptTcpClient() is running.
(i.e. CancelAsync()) from outside and
Loop While (!Tread.CancellationPending);
Create a TcpClient and send your listener a message (and discard data);
TcpClient see: https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.110).aspx
Now your thread can go on with:
client.close() and listener.stop()
I have this code...
internal static void Start()
{
TcpListener listenerSocket = new TcpListener(IPAddress.Any, 32599);
listenerSocket.Start();
listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}
Then my call back function looks like this...
private static void AcceptClient(IAsyncResult asyncResult)
{
MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
ThreadPool.QueueUserWorkItem((object state) => handler.Process());
listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}
Now, I call BeginAcceptTcpClient, then some time later I want to stop the server. To do this I have been calling TcpListener.Stop(), or TcpListener.Server.Close(). Both of these however execute my AcceptClient function. This then throws an exception when I call EndAcceptTcpClient. What is the best practice way around this? I could just put a flag in to stop the execution of AcceptClient once I have called stop, but I wonder if I am missing something.
Update 1
Currently I have patched it by changing the code to look like this.
private static void AcceptClient(IAsyncResult asyncResult)
{
if (!shutdown)
{
MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
ThreadPool.QueueUserWorkItem((object state) => handler.Process());
listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}
}
private static bool shutdown = false;
internal static void Stop()
{
shutdown = true;
listenerSocket.Stop();
}
Update 2
I changed it to impliment the answer from Spencer Ruport.
private static void AcceptClient(IAsyncResult asyncResult)
{
if (listenerSocket.Server.IsBound)
{
MessageHandler handler = new MessageHandler(listenerSocket.EndAcceptTcpClient(asyncResult));
ThreadPool.QueueUserWorkItem((object state) => handler.Process());
listenerSocket.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}
}
I just ran into this issue myself, and I believe your current solution is incomplete/incorrect. There is no guarantee of atomicity between the check for IsBound and the subsequent call to EndAcceptTcpClient(). You can still get an exception if the listener is Stop()'d between those two statements. You didn't say what exception you're getting but I assume it's the same one I'm getting, ObjectDisposedException (complaining that the underlying socket has already been disposed).
You should be able to check this by simulating the thread scheduling:
Set a breakpoint on the line after the IsBound check in your callback
Freeze the thread that hits the breakpoint (Threads window -> right click, "Freeze")
Run/trigger the code that calls TcpListener.Stop()
Break in and step through the EndAcceptTcpClient() call. You should see the ObjectDisposedException.
IMO the ideal solution would be for Microsoft to throw a different exception from EndAcceptTcpClient in this case, e.g. ListenCanceledException or something like that.
As it is, we have to infer what's happening from the ObjectDisposedException. Just catch the exception and behave accordingly. In my code I silently eat the exception, since I have code elsewhere that's doing the real shutdown work (i.e. the code that called TcpListener.Stop() in the first place). You should already have exception handling in that area anyway, since you can get various SocketExceptions. This is just tacking another catch handler onto that try block.
I admit I'm uncomfortable with this approach since in principle the catch could be a false positive, with a genuine "bad" object access in there. But on the other hand there aren't too many object accesses in the EndAcceptTcpClient() call that could otherwise trigger this exception. I hope.
Here's my code. This is early/prototype stuff, ignore the Console calls.
private void OnAccept(IAsyncResult iar)
{
TcpListener l = (TcpListener) iar.AsyncState;
TcpClient c;
try
{
c = l.EndAcceptTcpClient(iar);
// keep listening
l.BeginAcceptTcpClient(new AsyncCallback(OnAccept), l);
}
catch (SocketException ex)
{
Console.WriteLine("Error accepting TCP connection: {0}", ex.Message);
// unrecoverable
_doneEvent.Set();
return;
}
catch (ObjectDisposedException)
{
// The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
// so just return.
Console.WriteLine("Listen canceled.");
return;
}
// meanwhile...
SslStream s = new SslStream(c.GetStream());
Console.WriteLine("Authenticating...");
s.BeginAuthenticateAsServer(_cert, new AsyncCallback(OnAuthenticate), s);
}
No you're not missing anything. You can check the IsBound property of the Socket object. At least for TCP connections, while the socket is listening this will be set to true and after you call close it's value will be false. Though, your own implementation can work just as well.
try this one. it works fine for me without catching exceptions.
private void OnAccept(IAsyncResult pAsyncResult)
{
TcpListener listener = (TcpListener) pAsyncResult.AsyncState;
if(listener.Server == null)
{
//stop method was called
return;
}
...
}
i think that all tree things are needed and that the restart of BeginAcceptTcpClient should be placed outside the tryctach of EndAcceptTcpClient.
private void AcceptTcpClientCallback(IAsyncResult ar)
{
var listener = (TcpListener)ar.AsyncState;
//Sometimes the socket is null and somethimes the socket was set
if (listener.Server == null || !listener.Server.IsBound)
return;
TcpClient client = null;
try
{
client = listener.EndAcceptTcpClient(ar);
}
catch (SocketException ex)
{
//the client is corrupt
OnError(ex);
}
catch (ObjectDisposedException)
{
//Listener canceled
return;
}
//Get the next Client
listener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), listener);
if (client == null)
return; //Abort if there was an error with the client
MyConnection connection = null;
try
{
//Client-Protocoll init
connection = Connect(client.GetStream());
}
catch (Exception ex)
{
//The client is corrupt/invalid
OnError(ex);
client.Close();
}
}
This is a simple example how to start listening, how to process requests asynchronously, and how to stop listening.
Full example here.
public class TcpServer
{
#region Public.
// Create new instance of TcpServer.
public TcpServer(string ip, int port)
{
_listener = new TcpListener(IPAddress.Parse(ip), port);
}
// Starts receiving incoming requests.
public void Start()
{
_listener.Start();
_ct = _cts.Token;
_listener.BeginAcceptTcpClient(ProcessRequest, _listener);
}
// Stops receiving incoming requests.
public void Stop()
{
// If listening has been cancelled, simply go out from method.
if(_ct.IsCancellationRequested)
{
return;
}
// Cancels listening.
_cts.Cancel();
// Waits a little, to guarantee
// that all operation receive information about cancellation.
Thread.Sleep(100);
_listener.Stop();
}
#endregion
#region Private.
// Process single request.
private void ProcessRequest(IAsyncResult ar)
{
//Stop if operation was cancelled.
if(_ct.IsCancellationRequested)
{
return;
}
var listener = ar.AsyncState as TcpListener;
if(listener == null)
{
return;
}
// Check cancellation again. Stop if operation was cancelled.
if(_ct.IsCancellationRequested)
{
return;
}
// Starts waiting for the next request.
listener.BeginAcceptTcpClient(ProcessRequest, listener);
// Gets client and starts processing received request.
using(TcpClient client = listener.EndAcceptTcpClient(ar))
{
var rp = new RequestProcessor();
rp.Proccess(client);
}
}
#endregion
#region Fields.
private CancellationToken _ct;
private CancellationTokenSource _cts = new CancellationTokenSource();
private TcpListener _listener;
#endregion
}