web-dev-qa-db-fra.com

TcpClient - Une connexion existante a été fermée de force par l'hôte distant

L'info

J'ai développé un serveur Web http en c # et j'ai décidé d'ajouter une fonctionnalité de console distante. La console peut être utilisée à partir de n'importe quel emplacement et utilise un TcpListener (serveur Web) et un TcpClient (console distante) pour envoyer des commandes et des fonctions.

Le code

Voici à quoi ressemble mon serveur:

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

Remarque - Le serveur est exécuté sur un thread distinct du thread du serveur Web et de l'application de la console principale.

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

Le problème

Je peux envoyer une commande au serveur avec un retour réussi. Cependant, lorsque j'essaie d'envoyer une autre commande, le serveur lève l'erreur ci-dessous:

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

Je sais que ce n'est pas un problème de pare-feu ou d'élévation d'administrateur, car je peux envoyer une commande avec succès. Ce n'est que sur la deuxième commande envoyée qu'il lève cette erreur.

Voici une capture d'écran décrivant le problème: The remote console and server communication and error reporting.

EDIT: En faisant un peu de recherche, j'ai trouvé que le problème est probablement dû à une petite erreur dans ma boucle. Cependant, je ne connais aucun moyen de résoudre ce problème car je ne connais pas le problème exact :). S'il vous plaît aidez-moi à l'identifier afin que je puisse le réparer.

Merci encore

4
rodit

Il semble que votre client ferme la connexion après un message.

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

Si vous souhaitez conserver la connexion, le client devrait avoir une boucle similaire à celle que vous avez sur le serveur. Si vous souhaitez établir une nouvelle connexion à chaque fois, vous devez fermer le flux correctement sur le serveur et ne pas avoir une boucle comme celle-ci. Vous devrez toujours boucler si le message est plus long ou si vous devez spécifier la longueur maximale de la commande.

4
Stilgar

Je ne sais pas si vous avez résolu votre problème ou non, mais je suppose que vous devriez au moins publier votre solution de contournement afin que les autres personnes puissent la vérifier.

Je ne comprends pas tout à fait votre problème, mais j'avais la même exception, mais la mienne a été déclenchée alors que le client était déconnecté et que le serveur essayait de lire le flux (networkStream).

J'ai eu une seule commande pour lire

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

Comme suggéré dans la réponse, j'ai changé cela pour:

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

cela a également produit le problème pendant que le disque client, je devais le faire

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)

Je devais le faire, peu importe qu’il s’agisse d’un client ou d’un serveur, l’exception est la même. La déconnexion d’hôte, tandis que mon exception était la déconnexion du CLIENT et que ce message générique m’avait induit en erreur.

Désolé si le code n'est pas collé à partir de vs mais j'ai tapé ici, corrigez la capitalisation pour qu'elle puisse être compilée.

J'espère que ça aide quelqu'un.

4
Alex RG

J'ai eu la même solution. c'est généralement le cas si le client est déconnecté. La solution d'Alex RG ne fonctionne malheureusement pas. vous obtiendrez une autre exception. Les meilleures solutions sont décrites ici par Microsoft

vous devez vérifier avec 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