Skip to content

Commit

Permalink
Merge pull request #1373 from ValentijnMakkenze/issue-1371-2
Browse files Browse the repository at this point in the history
Backport #1183 to version315: Eliminate hang due to TcpListener thread
  • Loading branch information
OsirisTerje committed Dec 24, 2023
2 parents 1e61090 + aa4714d commit c52aa3d
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 9 deletions.
73 changes: 73 additions & 0 deletions src/NUnitEngine/nunit.engine.tests/Transport/Tcp/TcpServerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt

#if NETFRAMEWORK
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NUnit.Framework;

namespace NUnit.Engine.Communication.Transports.Tcp
{
public class TcpServerTests
{
private TcpServer _server;
private List<Socket> _serverConnections;

[SetUp]
public void StartServer()
{
_serverConnections = new List<Socket>();
_server = new TcpServer();
_server.ClientConnected += (c, g) => _serverConnections.Add(c);
_server.Start();
}

[TearDown]
public void StopServer()
{
_server.Stop();
}

[Test]
public void SingleClientConnection()
{
using (TcpClient client = new TcpClient())
{
client.Connect(_server.EndPoint);
client.Client.Send(new Guid().ToByteArray());

Thread.Sleep(1); // Allow the connection event to run
Assert.That(_serverConnections.Count, Is.EqualTo(1), "Should have received 1 connection event");
Assert.That(_serverConnections[0].Connected, "Server is not connected to client");

Assert.True(client.Connected, "Client is not connected to server");
}
}

[Test, Platform(Exclude = "Linux")]
public void MultipleClientConnections()
{
TcpClient[] clients = new[] { new TcpClient(), new TcpClient(), new TcpClient() };
int num = clients.Length;

foreach (var client in clients)
{
client.Connect(_server.EndPoint);
client.Client.Send(new Guid().ToByteArray());
}

Thread.Sleep(1); // Allow the connection events to run
Assert.That(_serverConnections.Count, Is.EqualTo(num), $"Should have received {num} connection events");

for (int i = 0; i < num; i++)
{
Assert.That(_serverConnections[i].Connected, $"Server is not connected to client {i+1}");
Assert.True(clients[i].Connected, $"Client {i+1} is not connected to server");
}
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class TcpServer

private const int GUID_BUFFER_SIZE = 16;

TcpListener _listenerSocket;
TcpListener _tcpListener;
Thread _listenerThread;
volatile bool _running;

Expand All @@ -24,17 +24,21 @@ public class TcpServer

public TcpServer(int port = 0)
{
_listenerSocket = new TcpListener(IPAddress.Loopback, port);
_tcpListener = new TcpListener(IPAddress.Loopback, port);
}

public IPEndPoint EndPoint => (IPEndPoint)_listenerSocket.LocalEndpoint;
public IPEndPoint EndPoint => (IPEndPoint)_tcpListener.LocalEndpoint;

public void Start()
{
_listenerSocket.Start();
_tcpListener.Start();
_running = true;

_listenerThread = new Thread(WaitForClientConnections);
_listenerThread = new Thread(WaitForClientConnections)
{
Name = "TcpListenerTread",
IsBackground = true
};
_listenerThread.Start();
}

Expand All @@ -43,7 +47,7 @@ public void Stop()
try
{
_running = false;
_listenerSocket.Stop();
_tcpListener.Stop();
}
catch (Exception exception)
{
Expand All @@ -57,7 +61,7 @@ private void WaitForClientConnections()
{
try
{
var clientSocket = _listenerSocket.AcceptSocket();
var clientSocket = _tcpListener.AcceptSocket();
if (clientSocket.Connected)
{
// Upon connection, remote agent must immediately send its Id as identification.
Expand All @@ -74,7 +78,7 @@ private void WaitForClientConnections()
// 1. We were trying to stop the socket
// 2. The connection was dropped due to some external event
// In either case, we stop the socket and wait a while
_listenerSocket.Stop();
_tcpListener.Stop();

// If we were trying to stop, that's all
if (!_running)