7
votes

Tcpclient.frose () fonctionne uniquement avec fil.sleep ()

J'ai un serveur simple qui reçoit une chaîne du client et l'imprime à l'écran. J'ai aussi un client simple, envoi de données et fermeture: xxx

et avec une chaîne non motivée 'thread.sleep (100) "ça marche ok. Mais lorsque le client commentant, parfois (1 sur 5-10 exécutions), le client n'envoie pas la chaîne. Regarder WireShark et netstat J'ai remarqué que le client envoie Syn, ACK Package, établit la connexion et les sorties sans rien envoyer et sans fermer la prise.

Est-ce que quelqu'un pourrait expliquer ce chef? Pourquoi dormir aide? Qu'est-ce que je fais mal?

upd upd:

avec cet exemple de code Ajout de flush () Avant la fermeture fonctionne vraiment, merci Fox32.

mais après son retour à mon code initial: xxx

et cela ne fonctionne pas, même avec NODELAY. C'est mauvais - à l'aide de StreamWriter sur le flux de réseau?

upd:

Voici le code du serveur: xxx

dans la classe serveur: < / p> xxx

upd upd:

Ajout même dormir (1) Déplacez des accidents dans 1 des 30 à 50 clients courants en même temps. Et ajouter le sommeil (10) semble résoudre totalement cela, je ne peux attraper aucun crash. Ne comprenez pas, pourquoi la prise a besoin de ces multiples millisecondes pour fermer correctement.


9 commentaires

Vous devez probablement voler le flux d'abord et / ou définir la prise TCP sur NODELAY ou ATTERGER


Vous ne pouvez pas affleurer un flux de réseau, mais la fermeture des données tamponnées / non délaises causées par Nagles Algorithm.


Remarqué que l'erreur avec l'envoi apparaît rarement lors de l'exécution de clients uniques avec un grand délai. Et apparaît presque toujours lors de la gestion de nombreux clients en même temps.


Pouvez-vous montrer une quantité minimale du code côté serveur, en particulier la façon dont vous lisez et fermez les flux?


@Despertar, j'ai ajouté du code serveur pour publier.


J'ai essayé beaucoup de méthodes: déconnecter, fermer, affleurer, arrêt. Tout se bloque parfois. Et le sommeil fonctionne bien. Étrange.


Pouvez-vous publier votre code actuel, avec tous ces méthodes appliquées? Le code posté ne fait pas l'arrêt. En outre, votre serveur est buggy. Vous supposez que votre première lecture reviendra réellement tous les octets envoyés. Ceci n'est pas garanti. Est-ce que tu comprends ce que je veux dire?


@USR, oui, bien sûr - le serveur de droite doit lire jusqu'à ce qu'il y ait des données dans le flux de réseau. C'est juste un serveur de test pour la lecture de petites chaînes) sur le code de publication - Despertar a posté la réponse avec le code de travail - aucune exception. Je vais bientôt écrire ce qui n'allait pas avec mon code, j'espère. Si je ne traite pas de cela, je posterai mon code complet sans travail avec toutes les méthodes. D'accord?


Bien sûr, je serais intéressé par ce que vous avez trouvé. (Sachez que le code affiché est incorrect car il utilise NetworkStream.DataVailable qui n'est pas fiable).


5 Réponses :


4
votes

Dans presque tous les cas, vous êtes censé appeler shutdown sur un socket ou tcpclient avant de le disposer. Les disposer de la connexion brutalement tuent la connexion.

Votre code contient essentiellement une condition de course avec la pile TCP.

paramètre NODELay est également une solution pour cela, mais fait mal à la performance. Calling Flush IMHO résulte toujours d'une fermeture désordonnée. Ne le faites pas parce qu'ils sont juste des hacks qui peignent sur le problème en cachant les symptômes. Appeler shutdown .

Je veux souligner que shutdown être appelé sur la prise est le est le seulement Solution valide que je connaisse. Même flush force les données sur le réseau. Il peut encore être perdu en raison d'un réseau Hickup. Il ne sera pas retransmis après fermer a été appelé parce que fermeture est un tuer grossier sur la prise.

malheureusement TCPCLIENT a un bug qui vous oblige à aller à la prise sous-jacente pour la fermer: xxx

Selon le réflecteur, si vous avez déjà accédé à getStream ce problème se pose et fermer ne ferme pas la prise sous-jacente. Dans mon estimation, ce bogue a été produit car le développeur ne connaissait pas vraiment l'importance de l'arrêt . Peu de gens savent et de nombreuses applications sont buggy à cause de cela. une question connexe.


5 commentaires

Il n'y a pas d'arrêt sur un tcpclient, il n'y a que ferme ?


S'il vous plaît voir la réponse mise à jour. J'ai fait de nouvelles recherches. Cela explique parfaitement les symptômes de la question.


@USR de l'article MSDN sur le bogue Il est indiqué pour résoudre ce problème, fermez l'objet NetworkStream associé que la méthode TCPClient GetStretream retourne. que l'OP est en cours, il n'ya donc pas besoin d'appeler Arrêt ()


IMHO L'article est faux, c'est pourquoi j'ai donné une solution différente dans le code. Si de près est appelé ou pas importe beaucoup ici. La clé est d'appeler l'arrêt (je me fiche de la façon dont). Êtes-vous d'accord avec cela? Sinon, quels vous posez-vous spécifiquement de questions sur cette réponse?


Dans tous les cas, aucune autre solution ne fonctionnera à l'exception de la fermeture. Consultez mon commentaire sur l'autre réponse très votée qui, à mon avis, est dangereuse.



5
votes

Le tcpclient utilise le algorithme de Nagle et attend pour plus de données avant de l'envoyer sur le fil. Si vous fermez le socket à rapide, aucune donnée n'est trasmatisée.

Vous avez plusieurs façons de résoudre ce problème:

Le NetworkStream a une méthode flush pour rincer la teneur en flux (je ne sais pas si cette méthode fait quelque chose du commentaire sur MSDN)

Désactiver l'algorithme de Nagle: set NODELAY du tcpclient à true.

La dernière option consiste à définir le < Code> lingerstate du tcpclient . Fermer Méthode Documentation STATS, que le lingerstate est utilisé lors de l'appelant Fermer


1 commentaires

Bien que toutes ces solutions fonctionnent, je crains que tous ne garantissent pas la livraison. Même affleurant force les données sur le réseau. Il peut encore être perdu en raison d'un réseau Hickup. Il ne sera pas retransmis après étroite a été appelé car la fermeture est un tuer grossier sur la prise.



1
votes

Dans votre code côté serveur, vous n'appelez que lisez () une fois, mais vous ne pouvez pas supposer que les données seront disponibles lorsque vous appelez Lecture. Vous devez continuer à lire dans une boucle jusqu'à ce que plus aucune donnée soit disponible. Voir l'exemple complet ci-dessous.

J'ai essayé de reproduire votre problème avec la quantité minimale de code et n'a pas été capable de. Le serveur imprime le message des clients à chaque fois. Aucun paramètre spécial tel que nodelay et non explicite ferme () ou flush () , juste en utilisant des déclarations Toutes les ressources sont correctement disposées. xxx


5 commentaires

Pouvez-vous essayer de minimiser le sommeil entre les clients en cours d'exécution? J'ai attrapé plus d'accidents avec de nombreux clients courants en même temps. De plus, vous exécutez des clients un par un, j'ai exécuté plusieurs clients en même temps. Mais de toute façon, c'est étrange. Je vais essayer de reproduire ce bogue sur un autre ordinateur. Peut-être qu'il s'agit d'un bogue d'ordinateur local ..


@ Vorobey1326 J'ai mis à jour l'exemple à la queue 1000 clients sur la piscine de thread à la fois. À partir de la sortie, il semble que tous les 1000 clients arrivent sur le serveur et sont imprimés.


Votre code fonctionne bien sur mon ordinateur. Aucune exception. Merci pour cela, je vais essayer de découvrir, quelle est la différence entre votre code et la mienne et écrivez à ce sujet plus tard.


Bien sûr, faites-moi savoir si vous avez des questions sur le code.


J'ai ajouté une réponse à cette question avec du code qui reproduira probablement ce bogue. Si vous le pouvez, essayez-le sur votre ordinateur.



0
votes

upding:

J'ai testé ce bogue sur plusieurs ordinateurs et rien ne s'est écrasé. On dirait que c'est un bug local sur mon ordinateur. P>

endofupd p>

Donc, ce que j'ai trouvé sur la reproduction de ce bogue. @Desperttar - Votre code fonctionne bien. Mais cela ne reproduit pas les conditions de ce bogue. Sur le client, vous devez envoyer des données et cesser de fumer après. Et dans votre code, de nombreux clients envoient des données et après la fermeture de toutes les applications. P>

Voici comment je le teste sur mon ordinateur: J'ai serveur (simplement accepter la connexion et imprimer des données entrantes), le client (il envoie simplement des données une fois les sorties de fin) et l'utilitaire exécutant (exécute le client Exe plusieurs fois). P>

Donc, je commence à exécuter utilitaire de serveur à le dossier des clients et le gère. L'utilisation de l'ulilité démarre 150 clients se connectant au serveur et 5-10 d'entre eux meurent (je vois une erreur dans la console du serveur). Et noter le thread.sleep () sur le client fonctionne bien, pas d'erreurs. P>

Peut essayer de reproduire cette version du code? P>

Code client: P>

class Program
{

    static int port = 26140;
    static AutoResetEvent waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        StartServer();
        waitHandle.WaitOne();
        Console.ReadLine();
    }

    static void StartServer()
    {
        Task.Factory.StartNew(() =>
        {
            try
            {
                TcpListener listener = new TcpListener(port);
                listener.Start();

                Console.WriteLine("Listening...");
                waitHandle.Set();

                while (true)
                {
                    TcpClient theClient = listener.AcceptTcpClient();

                    Task.Factory.StartNew(paramClient =>
                    {
                        try
                        {
                            TcpClient client = (TcpClient) paramClient;
                            byte[] buffer = new byte[32768];
                            MemoryStream memory = new MemoryStream();
                            using (NetworkStream networkStream = client.GetStream())
                            {
                                networkStream.ReadTimeout = 2000;
                                do
                                {
                                    int read = networkStream.Read(buffer, 0, buffer.Length);
                                    memory.Write(buffer, 0, read);
                                } while (networkStream.DataAvailable);
                                string text = Encoding.UTF8.GetString(memory.ToArray());
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("ERROR: " + e.Message);
                        }
                    }, theClient);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }, TaskCreationOptions.LongRunning);
    }
}


3 commentaires

Quelle est la exception exacte que vous obtenez?


Oh, désolé je n'ai pas écrit. Une exception est lancée dans la ligne de serveur 'NetworkStream.Lead' après 2 secondes délais. La chaîne d'exception est 'une tentative de connexion échoua car la partie connectée n'a pas répondu correctement après une période de temps, ou une connexion établie a échoué car l'hôte connecté n'a pas réussi à répondre ».


@Despertar, j'ai essayé ce code sur plusieurs ordinateurs - rien ne s'est écrasé. On dirait que c'est mon bug local. Étrange.



0
votes

J'ai essayé le code, reproduisant ce bogue sur plusieurs ordinateurs. Personne ne se bloque. On dirait que c'est mon bogue d'ordinateur local.

Merci pour tout le monde pour avoir essayé de m'aider.

Quoi qu'il en soit, c'est tellement étrange. Si je découvrirai pourquoi ce bug existe sur mon ordinateur, j'écrirai à ce sujet.


0 commentaires