Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport #1183 to version315: Eliminate hang due to TcpListener thread #1373

Merged
merged 2 commits into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Expand All @@ -84,7 +88,7 @@ private void WaitForClientConnections()
Thread.Sleep(500);
try
{
_listenerSocket.Start();
_tcpListener.Start();
}
catch (Exception exception)
{
Expand Down