web-dev-qa-db-ja.com

TcpClient-既存の接続がリモートホストによって強制的に閉じられました

情報

私はc#でWeb httpサーバーを開発していて、リモートコンソール機能を追加することにしました。コンソールはどこからでも使用でき、TcpListener(Webサーバー)とTcpClient(リモートコンソール)を使用してコマンドと関数を送信します。

コード

これは私のサーバーがどのように見えるかです:

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.
}

注-サーバーは、Webサーバーとメインコンソールアプリケーションスレッドの両方とは別のスレッドで実行されます。

これは私のクライアントです:

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();
    }
}

問題

サーバーに1つのコマンドを送信して、正常に戻ることができます。ただし、別のコマンドを送信しようとすると、サーバーは次のエラーをスローします。

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

1つのコマンドを正常に送信できるため、ファイアウォールや管理者の昇格の問題ではないことがわかります。このエラーをスローするのは、送信された2番目のコマンドのみです。

問題を説明するスクリーンショットは次のとおりです。 The remote console and server communication and error reporting.

編集:少し調べてみると、問題はforループの小さなエラーの結果である可能性が高いことがわかりました。ただし、正確な問題がわからないため、これを修正する方法がわかりません:)。修正できるように、特定するのを手伝ってください。

再度、感謝します

4
rodit

1つのメッセージの後で、クライアントが接続を閉じているようです。

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

接続を維持したい場合は、サーバー上にあるのと同様のループをクライアント上に持つ必要があります。毎回新しい接続を確立したい場合は、サーブでストリームを正常に閉じ、このようなループを持たないようにする必要があります。メッセージが長い場合や、コマンドの最大長を指定する必要がある場合は、ループする必要があります。

4
Stilgar

問題を修正したかどうかはわかりませんが、少なくとも他の人が確認できるように、回避策を投稿する必要があると思います。

私はあなたの問題を完全には理解していませんが、同じ例外がありましたが、クライアントが切断され、サーバーがストリーム(networkStream)を読み取ろうとしたときにトリガーされました。

私は読むための単一のコマンドを持っていました

networkstream.Read(mybuffer, 0, mybuffer.length);

チェックされた答えが示唆したように、私はそれを次のように変更しました:

do
{
 byte[] buff = new byte[1];
 networkstream.Read(buff, 0, 1);
 myreceivedbuff.Add(buff);
} while (networkstream.DataAvailable)

これもクライアントディスク中に問題を引き起こしたので、私はこれをしなければなりませんでした

do
{
 byte[] buff = new byte[1];
 try
 {
  networkstream.Read(buff, 0, 1);
 }
 catch(exception ex)
 {
  throw new exception("The dam client disconnected in the middle of a transaction.");
 }
 myreceivedbuff.Add(buff);
} while (networksteam.DataAvailable)

クライアントとサーバーのどちらで例外が同じかは問題ではないので、これを行う必要がありました。その間、ホストの切断はクライアントの切断であり、この一般的なメッセージは私を解決策に誤って導きました。

コードがvsから貼り付けられていない場合は申し訳ありませんが、ここに入力したので、大文字と小文字を修正してコンパイルできるようにします。

これが誰かを助けることを願っています。

4
Alex RG

私は同じ解決策を持っていました。これは通常、クライアントが切断された場合に発生します。 Alex RGのソリューションは、残念ながら機能していません。別の例外が発生します。最良の解決策が説明されています ここ マイクロソフト

CanReadを使用して確認する必要があります

TcpClient tcpClient = new TcpClient ();

// Uses the GetStream public method to return the NetworkStream.
NetworkStream netStream = tcpClient.GetStream ();

if (netStream.CanRead)
{
    // Reads NetworkStream into a byte buffer.
    byte[] bytes = new byte[tcpClient.ReceiveBufferSize];

    // Read can return anything from 0 to numBytesToRead. 
    // This method blocks until at least one byte is read.
    netStream.Read (bytes, 0, (int)tcpClient.ReceiveBufferSize);

    // Returns the data received from the Host to the console.
    string returndata = Encoding.UTF8.GetString (bytes);

    Console.WriteLine ("This is what the Host returned to you: " + returndata);

}
else
{
    Console.WriteLine ("You cannot read data from this stream.");
    tcpClient.Close ();

    // Closing the tcpClient instance does not close the network stream.
    netStream.Close ();
    return;
}
0
batmaci