I'm using a Thread and TCPClient to Read/Write to a TCP server, where the client Write initializes the TCPConnect and Read immediately calls TCPDisconnect after the response has been validated. This has to be done to ensure available sockets for multiple async users (each client only calls every 5-10 seconds). However, the disconnect is throwing a series of errors when disposing the Thread.
The problem occurs when calling DisconnectFromTCPServer() => clientReceiveThread.Abort(), at the listener delegate ListenForData() { using (NetworkStream ... ) { ... while ((length = stream.Read ... ) throwing these errors;
ThreadAbortException: Thread was being aborted. System.ComponentModel.Win32Exception..ctor (System.Int32 error) (at <1720f652f31145af877fbb9e0d1ba65c>:0) System.Net.Sockets.SocketException..ctor (System.Net.Sockets.SocketError socketError) (at <1720f652f31145af877fbb9e0d1ba65c>:0) System.Net.Sockets.Socket.Receive (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.Net.Sockets.SocketFlags socketFlags) (at <1720f652f31145af877fbb9e0d1ba65c>:0) System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) (at <1720f652f31145af877fbb9e0d1ba65c>:0) Rethrow as IOException: Unable to read data from the transport connection: Thread was being aborted.. System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) (at <1720f652f31145af877fbb9e0d1ba65c>:0)
For whatever reason the isAlive flag in the DisconnestFromTCPServer() is not being picked up in the ListenForData() delegate leading to the above errors. This code had initially been working before introducing the validation of the NewData from the serverMessage before it is then set = null when the response is called from the main thread via; public Move AIResponse() when NewData = true.
private void ConnectToTcpServer()
{
try
{
Debug.Log("CONNECTING TO SERVER...");
clientReceiveThread = new Thread(new ThreadStart(ListenForData));
clientReceiveThread.IsBackground = true;
clientReceiveThread.Start();
isAlive = true;
}
catch (Exception e)
{
Debug.Log("On client connect exception " e);
}
}
private void DisconnectFromTcpServer()
{
isAlive = false;
if (socketConnection.Connected) ((IDisposable)socketConnection).Dispose();
StartCoroutine(ServerDisconnectedCoroutine(() => !socketConnection.Connected));
if (clientReceiveThread.IsAlive) clientReceiveThread.Abort();
clientReceiveThread.Join();
ServerConnected = false;
}
private IEnumerator ServerDisconnectedCoroutine(System.Func<bool> socketDisconnected)
{
yield return new WaitUntil(socketDisconnected);
}
public void ListenForData()
{
// SWITCH PORTS ON EACH CONNECT - BALANCE THE LOAD
// WRAP IN COUNTER
port = port == 12345 ? 12344 : 12345;
try
{
socketConnection = new TcpClient("65.21.xxx.xxx", 12345);
Byte[] bytes = new Byte[1024];
Thread.Sleep(100);
while (isAlive)
{
// Test socket connection
if (!socketConnection.Connected)
{
Debug.Log("DISCONNECTED LISTEN");
serverTimeout();
return;
}
else
ServerConnected = true;
// Get a stream object for reading
using (NetworkStream stream = socketConnection.GetStream())
{
int length;
// Read incomming stream into byte arrary.
while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
var incommingData = new byte[length];
Array.Copy(bytes, 0, incommingData, 0, length);
// Convert byte array to string message.
serverMessage = Encoding.UTF8.GetString(incommingData);
Debug.Log("server message received as: " serverMessage);
// ORIGINAL NewData = true;
}
stream.Close();
}
}
}
catch (SocketException socketException)
{
Debug.Log("Socket exception: " socketException);
}
catch (ThreadAbortException threadException)
{
Debug.Log("ThradException: " threadException);
}
}
public bool IfNewData()
{
if (serverMessage != null && !NewData) aiDataFromServer.LoadString(serverMessage);
if (aiDataFromServer.type != "Error") NewData = true;
return NewData;
}
public Move AIResponse()
{
NewData = false;
// ORIGINAL - serverMessage ! set to null
serverMessage = null;
DisconnectFromTcpServer();
return aiDataFromServer.moveData;
}
I've tried various approaches to delay the call to Thread.Abort(), as well as closing the stream and socket, yet nothing seems to prevent the delegate from attempting to read data, although the server response has already been logged in the Console and there being no further information sent. I'm at a bit of a loss for what to do next, unless I've missed something really obvious.
Thanks to anyone who can point me in the right direction.
UPDATE - Initial problem resolved - see below - New issue identified
Following the first answer and with some digging around the Thread error has been resolved (see snippet below), but the changes have caused a strange artefact in the behavior of the SocketConnection.
Once the stream.CanRead, stream.DataAvailable, stream.Close(); the loop returns to top and returns !serverConnection.Connected => Debug.Log("DISCONNECTED LISTEN"); causing the server to reconnect. At this point the main thread has called AIResponse() and the connection and thread are shutdown anyway, but I am unable to identify why the socketConnection is being closed after the stream has been read.
public void ListenForData()
{
// SWITCH PORTS ON EACH CONNECT - BALANCE THE LOAD
// WRAP IN COUNTER
port = port == 12345 ? 12344 : 12345;
try
{
socketConnection = new TcpClient("65.21.xxx.xxx", 12345);
Byte[] bytes = new Byte[1024];
while (isAlive)
{
Debug.Log("IS ALIVE");
// Test socket connection
if (!socketConnection.Connected && connectionRequired)
{
Debug.Log("DISCONNECTED LISTEN");
serverTimeout();
return;
}
else
ServerConnected = true;
using (NetworkStream stream = socketConnection.GetStream())
{
if (stream.CanRead)
{
int _length;
do
{
_length = stream.Read(bytes, 0, bytes.Length);
var _incommingData = new byte[_length];
Array.Copy(bytes, 0, _incommingData, 0, _length);
serverMessage = Encoding.UTF8.GetString(_incommingData);
Debug.Log("server message received as: " serverMessage);
// ALLOW REFERENCES TO BE UPDATED
if (!stream.DataAvailable)
Thread.Sleep(1);
}
while (stream.DataAvailable);
}
stream.Close();
Debug.Log("STREAM CLOSED");
}
}
}
catch (SocketException socketException)
{
Debug.Log("Socket exception: " socketException);
}
catch (ThreadAbortException threadException)
{
Debug.Log("ThradException: " threadException);
}
}
While it's not a major issue it seems very strange that a connection would be closed after these operations, unless there is something in the documentation I have missed.
Many thanks again for any insight.
CodePudding user response:
It looks like you aborted in the middle of your Read. Remember that NetworkStream.Read will only return 0 if the other side closed, otherwise will always either return avaliable data or block waiting indefinitely.
This is per contract of Stream.Read:
The implementation will block until at least one byte of data can be read, in the event that no data is available. Read returns 0 only when there is no more data in the stream and no more is expected (such as a closed socket or end of file).
I recommend instead of whiling your Read, while something else like a bool 'is alive' flag. And before calling Read, call NetworkStream.DataAvailable, otherwise will block.
while(is_alive){
if(stream.DataAvailable){ stream.Read(...))
Thread.Sleep(100);
}
(You could also move this into a coroutine instead of thread, and keep your stream allocated until you exit, instead of creating/disposing all the time you need to read)