As everyone may already know, the simplest way to accept incoming TCP connections in C# is by looping over TcpListener.AcceptTcpClient(). Additionally this way will block code execution until a connection is obtained. This is extremely limiting to a GUI, so I want to listen for connections in either a seperate thread or task.
I have been told, that threads have several disadvantages, however nobody explained me what these are. So instead of using threads, I used tasks. This works great, however since the AcceptTcpClient method is blocking execution, I can't find any way of handling a task cancellation.
Currently the code looks like this, but I have no idea how I would want to cancel the task when I want the program to stop listening for connections.
First off the function executed in the task:
static void Listen () {
// Create listener object
TcpListener serverSocket = new TcpListener ( serverAddr, serverPort );
// Begin listening for connections
while ( true ) {
try {
serverSocket.Start ();
} catch ( SocketException ) {
MessageBox.Show ( "Another server is currently listening at port " + serverPort );
}
// Block and wait for incoming connection
if ( serverSocket.Pending() ) {
TcpClient serverClient = serverSocket.AcceptTcpClient ();
// Retrieve data from network stream
NetworkStream serverStream = serverClient.GetStream ();
serverStream.Read ( data, 0, data.Length );
string serverMsg = ascii.GetString ( data );
MessageBox.Show ( "Message recieved: " + serverMsg );
// Close stream and TcpClient connection
serverClient.Close ();
serverStream.Close ();
// Empty buffer
data = new Byte[256];
serverMsg = null;
}
}
Second, the functions starting and stopping the listening service:
private void btnListen_Click (object sender, EventArgs e) {
btnListen.Enabled = false;
btnStop.Enabled = true;
Task listenTask = new Task ( Listen );
listenTask.Start();
}
private void btnStop_Click ( object sender, EventArgs e ) {
btnListen.Enabled = true;
btnStop.Enabled = false;
//listenTask.Abort();
}
I just need something to replace the listenTask.Abort() call (Which I commented out because the method doesn't exist)
The following code will close/abort AcceptTcpClient when isRunning variable becomes false
public static bool isRunning;
delegate void mThread(ref book isRunning);
delegate void AccptTcpClnt(ref TcpClient client, TcpListener listener);
public static main()
{
isRunning = true;
mThread t = new mThread(StartListening);
Thread masterThread = new Thread(() => t(this, ref isRunning));
masterThread.IsBackground = true; //better to run it as a background thread
masterThread.Start();
}
public static void AccptClnt(ref TcpClient client, TcpListener listener)
{
if(client == null)
client = listener.AcceptTcpClient();
}
public static void StartListening(ref bool isRunning)
{
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, portNum));
try
{
listener.Start();
TcpClient handler = null;
while (isRunning)
{
AccptTcpClnt t = new AccptTcpClnt(AccptClnt);
Thread tt = new Thread(() => t(ref handler, listener));
tt.IsBackground = true;
// the AcceptTcpClient() is a blocking method, so we are invoking it
// in a separate dedicated thread
tt.Start();
while (isRunning && tt.IsAlive && handler == null)
Thread.Sleep(500); //change the time as you prefer
if (handler != null)
{
//handle the accepted connection here
}
// as was suggested in comments, aborting the thread this way
// is not a good practice. so we can omit the else if block
// else if (!isRunning && tt.IsAlive)
// {
// tt.Abort();
//}
}
// when isRunning is set to false, the code exits the while(isRunning)
// and listner.Stop() is called which throws SocketException
listener.Stop();
}
// catching the SocketException as was suggested by the most
// voted answer
catch (SocketException e)
{
}
}