TcpClient - An existing connection was forcibly closed by the remote host

rodit picture rodit · Jul 15, 2014 · Viewed 49.6k times · Source

The Info

I have been developing a web http server in c# and decided to add a remote console feature. The console can be used from any location and uses a TcpListener (web server) and a TcpClient (remote console) to send commands and functions through.

The Code

This is what my server looks like:

TcpListener consoleListener = new TcpListener(consolePort);
consoleListener.Start();
byte[] bytes = new Byte[256];
string data = null;
while (true)
{
    TcpClient client = consoleListener.AcceptTcpClient();
    data = null;
    byte[] msg = { 0 };
    int i;
    while ((i = client.GetStream().Read(bytes, 0, bytes.Length)) != 0)
    {
        data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
        if (data == "shutdown")
        {
            //Server shutdown logic.
        }
        //Other commands here...
        else
        {
            msg = Encoding.ASCII.GetBytes("Invalid command. Type 'help' or '?' to get a list of commands.");
        }
        client.GetStream().Write(msg, 0, msg.Length); //sends return message to console
    }
    client.Close(); //closes connection between client and server after EVERY command. Connection is reopened when a new command is sent.
}

Note - The server is run on a separate thread to both the webserver and main console application thread.

This is my client:

public static string Communicate(string text)
{
    try
    {
        TcpClient client = new TcpClient(ip, port); //initializes tcpclient (ip and port are correct)

        byte[] data = System.Text.Encoding.ASCII.GetBytes(text); //converts text to bytes for stream writing

        NetworkStream stream = client.GetStream();

        stream.Write(data, 0, data.Length);

        Console.WriteLine("Sent data: " + text);

        data = new Byte[256];

        string responseData = String.Empty; //initializes responsData string

        Int32 bytes = stream.Read(data, 0, data.Length);
        responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
        client.Close();
        return responseData; //returns what server writes
    }
    catch (Exception ex)
    {
        return "An error occured\n" + ex.ToString();
    }
}

The Problem

I can send one command to the server with a successful return. However, when I try and send another command, the server throws the error below:

System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   --- End of inner exception stack trace ---
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at ---.Server.ConsoleListener() in X:\Users\---\Documents\Visual Studio 2013\Projects\---\---\Program.cs:line x

I know it is not firewall or administrator elevation problems as I can send one command through successfully. It is only on the second command sent that it throws this error.

Here is a screenshot describing the problem: The remote console and server communication and error reporting.

EDIT: By doing a little research, I found that the problem is most likely a result of a small error in my for loop. However, I do not know any way of fixing this as I do not know the exact problem :). Please help me identify it so I can fix it.

Thanks again

Answer

Stilgar picture Stilgar · Jul 15, 2014

It seems that your client closes the connection after one message.

responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
client.Close();
return responseData; //returns what server writes

If you want to persist the connection you should have a loop on the client similar to the one you have on the server. If you want to establish a new connection every time you should close the stream gracefully on the server and not have a loop like this. You will still need to loop in case the message is longer or you need to specify max length for the command.