NetworkStream read abends application

Brown, Matt 106 Reputation points
2025-03-11T21:22:27.76+00:00

I am running into an issue where a read abends my application. Is there a way to handle it in a more soft way so I get the error data, but have it not stop my application. Is it a simply case of changing the LogError functions to a LogInfo? It is slightly odd that the write seems to work and not the read. Code and error message is below.

Code

private string SendReceive(string message, string clientAddress, int id)
{
    try
    {
        // Create a TcpClient. 
        // Note, for this client to work you need to have a TcpServer  
        // connected to the same address as specified by the server, Port 
        // combination.
        if (clientAddress.Equals("localhost"))
            clientAddress = "127.0.0.1";
        var client = new TcpClient(clientAddress, Port);
        byte[] data = Encoding.ASCII.GetBytes(message);
        NetworkStream stream = client.GetStream();
        stream.ReadTimeout = 250;
        stream.Write(data, 0, data.Length);
        Log.LogInfo("Sent for note: " + id + " with " + message);
        data = new byte[256];
        // Read the first batch of the TcpServer response bytes.
        int bytes = stream.Read(data, 0, data.Length);
        string responseData = Encoding.ASCII.GetString(data, 0, bytes);
        client.Close();
        // stream.Close(); 
        Log.LogInfo("Note: " + id + " reply from server " + responseData);
        responseData = responseData.ToUpper();
        return responseData;
    }
    catch (ObjectDisposedException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }
    catch (ArgumentOutOfRangeException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }
    catch (InvalidOperationException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }
    catch (ArgumentNullException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }
    catch (IOException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }            
    catch (SocketException e)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
    }
    catch (Exception ex)
    {
        Log.LogError(MethodBase.GetCurrentMethod().Name, "Exception in ESPComm.SendReceive: {0}", ex);
        Log.LogInfo("Exception returned: " + ex.Message);
    }

Error Message


3/11/2025 3:45:17 PM: Error: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at AppService.Util.Comm.SendReceive(String message, String clientAddress, Int32 id) in C:\services\App_Service\Utils\Comm.cs:line 52
3/11/2025 3:45:21 PM: Error: No connection could be made because the target machine actively refused it (IP Address)
No connection could be made because the target machine actively refused it (IP Address)
   at System.Net.Sockets.TcpClient..ctor(String hostname, Int32 port)
   at AppService.Util.Comm.SendReceive(String message, String clientAddress, Int32 id) in C:\services\App_Service\Utils\Comm.cs:line 44
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,339 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Michael Taylor 57,216 Reputation points
    2025-03-11T21:51:38.35+00:00

    Log.LogError isn't going to terminate your app unless it also throws an exception. You didn't post all the code in that function so it is hard to say what is going on here. It seems like your code is handling all the errors, although I don't know that this is a good idea. What you need to look at is what exception is actually causing the app to crash. If you're running in the debugger then it'll show you the actual line crashing your app.

    As for your code, I think you can simplify it down quite a bit. It is unclear to me why you're handling very specific exceptions and logging them but generating a different log for everything else. If you really need this behavior then consider creating an exception filter instead. This would make it very easy to add new exceptions while keeping the code small.

    try
    {
       ... 
    } catch (Exception error) when (IsSpecialError(e))
    {
       //Log special exceptions here
    } catch (Exception error)
    {
       //Everything else lands here
    };
    
    private static bool IsSpecialError ( Exception error )
    {
       //Nothing special...
       return false;
    }
    

    Looking at what could cause the issue I see the following things that raise some concerns:

    1. Creating a new TcpClient each time can be wasteful. If you are talking to the same client then create the TcpClient once and reuse it. Otherwise wrap it in a using statement otherwise you are leaking a TcpClient every time your code throws an exception.
    2. You are setting the ReadTimeout to 250ms. That is pretty fast. Interestingly the first error you log is related to the timeout of the read. Unless you have specific requirements then don't set the read timeout from its default value.
    3. You should never handle exceptions unless you can do something about them. This, of course, excludes logging the exception and then rethrowing. In your case you're handling exceptions that should crash your app as they are true error cases. Referencing a disposed object indicates a lifetime issue. Argument exception indicates bad code, etc. Only handle exceptions that you can do something about. In all other cases log the exception and rethrow it. Maybe the caller can better handle the error. This also plays into what you're going to return if an error does occur.

    Here is a first pass at how I might rewrite your code.

    private string SendReceive(string message, string clientAddress, int id)
    {
        // Create a TcpClient. 
        // Note, for this client to work you need to have a TcpServer  
        // connected to the same address as specified by the server, Port 
        // combination.
        if (clientAddress.Equals("localhost"))
            clientAddress = "127.0.0.1";
    
        byte[] data = Encoding.ASCII.GetBytes(message);
        
        try
        {
           using var client = new TcpClient(clientAddress, Port);
           NetworkStream stream = client.GetStream();
    
           stream.Write(data, 0, data.Length); 
           Log.LogInfo("Sent for note: " + id + " with " + message);
    
           data = new byte[256];
           // Read the first batch of the TcpServer response bytes.
           int bytes = stream.Read(data, 0, data.Length);
           string responseData = Encoding.ASCII.GetString(data, 0, bytes);
       
           Log.LogInfo("Note: " + id + " reply from server " + responseData);
           responseData = responseData.ToUpper();
           return responseData;
        } catch (IOException e)
        {
            Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e);
            return "IO ERROR";
        } catch (SocketException e)
        {
            Log.LogError(MethodBase.GetCurrentMethod().Name, e.Message, e); 
            return e.Message;
        } catch (Exception ex)
        {
            Log.LogError(MethodBase.GetCurrentMethod().Name, "Exception in ESPComm.SendReceive: {0}", ex);
            Log.LogInfo("Exception returned: " + ex.Message);
    
            //Nothing to do here so fail
            throw;
        }
    
        //Will never get here...
    }
    

    I would probably go even further and refactor the read/write into separate functions if they are more complex than what is shown here.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.