Quelle est la bonne manière de lire à partir de NetworkStream .NET
J'ai eu du mal avec cela et ne peut pas trouver une raison pourquoi mon code ne s'est pas correctement lues à partir d'un serveur TCP j'ai également écrit. Je suis à l'aide de la TcpClient
classe et de son GetStream()
méthode, mais quelque chose ne fonctionne pas comme prévu. Soit l'opération bloque indéfiniment (la dernière opération de lecture n'a pas de délai d'attente comme prévu), ou les données sont tronquées (pour une raison quelconque, une opération de Lecture renvoie 0 et sort de la boucle, peut-être que le serveur ne répond pas assez vite). Ces trois tentatives de mise en place de cette fonction:
//this will break from the loop without getting the entire 4804 bytes from the server
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
bytes = 0;
if (stm.DataAvailable)
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
//this will block forever. It reads everything but freezes when data is exhausted
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
//inserting a sleep inside the loop will make everything work perfectly
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
Thread.Sleep(20);
bytes = 0;
if (stm.DataAvailable)
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
La dernière "marche", mais il est certainement plus laid mettre codés en dur pour dormir à l'intérieur de la boucle considérant que les sockets déjà lu délais d'attente! Dois-je configurer quelque propriété(s) sur le TcpClient
de la NetworkStream
? Le problème réside dans le serveur? Le serveur ne ferme pas les connexions, c'est au client de le faire. Le ci-dessus est également en cours d'exécution à l'intérieur de la thread de l'INTERFACE utilisateur contexte (programme de test), peut-être qu'il a quelque chose à faire avec ça...
Ce que quelqu'un sait comment utiliser correctement NetworkStream.Read
de lire les données jusqu'à ce que plus de données sont disponibles? Je suppose que ce que je souhaite, c'est quelque chose comme l'ancien Win32 winsock propriétés de délai d'expiration... ReadTimeout
, etc. Il essaie de lire jusqu'à ce que le délai est atteint, et ensuite de retour à 0... Mais il semble parfois return 0 lorsque les données doivent être disponibles (ou sur le chemin.. peut Lire retourner 0 si est-il disponible?) et puis, il bloque indéfiniment sur la dernière lecture lorsque les données n'est pas disponible...
Oui, je suis à une perte de!
using (var client = new System.Net.Sockets.TcpClient(ip, port)) using (var stm = client.GetStream())
puis placez le reste de la méthode dans des accolades. Cela permettra de garantir que sur la méthode de sortie, quelle que soit la raison, les connexions sont fermées, les ressources sont récupérés.Avez-vous vu le code de la classe TcpClient? Je conseille de prendre un coup d'oeil... Si vous avez encore envie de vous répondre sur le point de terminaison, vous ne devez pas fermer le flux de données, ni le tcpclient (qui ferme le flux, si je ne me trompe pas). Mais le code que j'utilise est différent, je vais creuser et mettre à jour les réponses ici aussi.
merci pour la suggestion. J'ai trouvé TCPClient.cs. En effet, la fermeture ou de l'élimination de
TCPClient
dispose le flux. En fait, j'ai vu des échecs de connexion lorsqu'un programme fait des milliers de connexions sans beaucoup de soin (pour les autres, les mauvais, les raisons). Pourquoi devrait-on s'écarter de l'habituel IDisposable modèle ici ? La mise en œuvre de IDisposable est sujette à erreur, using()
c'est facile et sûr.OriginalL'auteur Loudenvier | 2012-10-27
Vous devez vous connecter pour publier un commentaire.
Code réseau est notoirement difficile à écrire, tester et déboguer.
Vous avez souvent beaucoup de choses à considérer, tels que:
ce "endian" allez-vous utiliser pour les données échangées (Intel x86/x64 est basé sur little-endian) - les systèmes qui utilisent le big-endian pouvez toujours lire des données qui est en little-endian (et vice versa), mais ils ont pour réorganiser les données. Lors de la fixation de votre "protocole" juste ce que vous utilisez.
existe-il des "paramètres" qui ont été définies sur les sockets qui peut affecter la façon dont le "flux" se comporte (par exemple SO_LINGER) - vous pourriez avoir besoin à son tour de certains d'entre eux sur on ou off si votre code est très sensible
comment la congestion dans le monde réel, ce qui entraîne des retards dans le flux d'affecter votre lecture/écriture de la logique de
Si le "message" d'être échangées entre un client et un serveur (dans les deux sens) la taille peut varier alors, souvent, vous avez besoin d'utiliser une stratégie pour que le "message" qui peuvent être échangés de manière fiable (aka Protocole).
Voici différentes façons de gérer l'échange:
ont la taille de message codé dans un en-tête qui précède les données, ce pourrait être tout simplement un "numéro" dans la première 2/4/8 octets envoyés (en fonction de votre taille de message maximale), ou pourrait être beaucoup plus exotiques comme "en-tête"
utiliser un spécial "fin du message" marqueur (sentinelle), avec le réel, les données codées/échappé si il y a la possibilité de réel les données de la confondre avec une "fin de marqueur"
utiliser un délai d'attente....c'est à dire une certaine période de réception aucun octet n'y a pas plus de données pour le message - cependant, cela peut être source d'erreurs avec de courts délais d'attente, qui peut facilement être frappé sur encombrée de flux.
ont une "commande" et "données" de canal sur des "connexions"....c'est l'approche que le protocole FTP utilise (l'avantage, c'est la séparation claire des données de commandes...au détriment d'une 2ème connexion)
Chaque approche a ses avantages et inconvénients de la "décision correcte".
Le code ci-dessous utilise le "délai d'attente" de la méthode, qui semble être celui que vous voulez.
Voir http://msdn.microsoft.com/en-us/library/bk6w7hs8.aspx. Vous pouvez obtenir l'accès à la
NetworkStream
sur leTCPClient
de sorte que vous pouvez changer laReadTimeout
.Comme une note de bas de page pour d'autres variations sur le présent de l'écriture de code de réseau...lorsque l'on fait un
Read
où vous souhaitez éviter un "bloc", vous pouvez vérifier laDataAvailable
drapeau et ensuite SEULEMENT de lire ce qui est dans la mémoire tampon de vérification de la.Length
de propriété par exemplestm.Read(resp, 0, stm.Length);
Quelle méthode utilisez-vous pour permettre la lecture pour savoir si c'est de lire toutes les données de dire le droit montant/taille? La méthode classique consiste à utiliser un taille d'en-tête qui est codé dans la première partie de la réponse.
J'ai un Contenu-durée (le protocole est presque identique à l'adresse HTTP), mais pour simplifier, je voudrais utiliser un "données entrantes timeout" et j'ai pensé que la Lecture serait de les bloquer pour un temps et puis délai d'attente et de ne pas attendre une éternité pour que, pour y arriver! Avec l' "à l'intérieur de la boucle" dormir, je vais payer les 20ms prix à chaque itération, avec un "données entrantes" dépassement de délai, je souhaite payer en cas de besoin et à la dernière lecture. Je peux l'optimiser à l'aide de la ContentLenght, mais en jouant sur la longueur, seule, est aussi risqué que le serveur peut mal se conduire et ne pas tout envoyer!
Vous pouvez définir la ReadTimeout.
ReadTimeout seulement après une opération de lecture réellement commencé. Si aucune donnée n'arrive jamais ReadTimeout n'a aucun effet. Je suis à la recherche dans la Douille.ReceiveTimeout...
OriginalL'auteur Colin Smith
Réglage de la socket sous-jacente
ReceiveTimeout
bien fait le tour. Vous pouvez y accéder comme ceci:yourTcpClient.Client.ReceiveTimeout
. Vous pouvez lire les docs pour plus d'informations.Maintenant le code ne "sommeil" aussi longtemps que nécessaire pour certains de données pour arriver dans la prise, ou elle va lever une exception si aucune donnée n'arrive, au début d'une opération de lecture, pour plus de 20ms. Je peux modifier ce délai d'attente en cas de besoin. Maintenant, je ne vais pas payer les 20ms prix à chaque itération, je suis le seul à payer lors de la dernière opération de lecture. Depuis que j'ai la longueur du contenu du message dans les premiers octets lus à partir du serveur, je peux l'utiliser pour ajuster encore plus et de ne pas essayer de lire si toutes les données attendues ont déjà été reçues.
- Je trouver de l'aide ReceiveTimeout beaucoup plus facile que la mise en œuvre de lecture asynchrone... Voici le code de travail:
Je ne suis pas réutiliser le support après le délai d'attente... je ne vais pas perdre des données, mais je peux avoir un faux "positifs" délai d'attente (si le réseau est lent données peut prendre plus de temps que la intercharacter délai d'attente à l'arrivée), mais dans ce cas, l'autre côté sera tout simplement une poignée de déconnexion et, espérons-le, essayez de nouveau. Ce n'est pas vraiment un hack, le protocole, que je ne peux pas changer, envoie inconnue de taille des messages, je dois donc compter sur des délais d'attente. En fait, j'ai fait le code plus robuste avec le temps, mais n'a pas de mise à jour de l'article. Il est maintenant un bon moment.
OriginalL'auteur Loudenvier
Selon votre condition,
Thread.Sleep
est parfait à utiliser car vous n'êtes pas sûr de quand les données seront disponibles, donc vous pourriez avoir besoin d'attendre que les données deviennent disponibles. J'ai légèrement changé la logique de votre fonction de ce qui pourrait vous aider peu plus loin.Espérons que cette aide!
stm.ReadIfDataBecomesAvailableInUpTo(timeout=2000)
je sais 20ms est un petit prix à payer, mais il sera payé à chaque interaction! Pour préparer un MO taille de réponse, il sera une SURCHARGE ÉNORME! Je ne voulais pas de recourir à l'écriture de mon propre délai d'attente logique... 🙁Vous devez être prudent avec Dormir sur fils. Si le temps est trop petite, vous êtes inutile de forcer des changements de contexte sur les OS, ainsi surcharge du PROCESSEUR. Un meilleur mécanisme serait probablement par interruption ou "événement". Pour cela, vous pouvez utiliser de la stm.BeginRead() qui vous permettrait d'avoir un événement appelée lorsque les données sont prêtes, de cette façon, votre processus peut être dans un état bloqué, où les OS ne réveiller lorsque la ressource est prêt. Sommeil le rendement jusqu'à l'OS à chaque fois que le processus réveillé.
OriginalL'auteur Furqan Safdar