Connexion client TCP

J’ai une application écrite pour mon application dissortingbuée dans toute l’entreprise pour m’envoyer des données via notre serveur Windows 2003 (exécutant IIS 6.0). Les petits messages texte sont acheminés, mais les messages plus volumineux contenant plus de données (environ 20 Ko) ne sont pas acheminés.

J’ai mis la mémoire tampon d’octets sur la taille de la mémoire tampon du client TCP. J’ai remarqué que mes données étaient en cours de réception sur le serveur. Cependant, il n’a parcouru la routine de réception qu’une seule fois, et mes gros fichiers avaient toujours exactement la taille de la taille de la mémoire tampon, soit 8 Ko sur notre serveur. En d’autres termes, mon code passe uniquement par une boucle avant que le serveur ne ferme la connexion socket.

Pensant qu’il pourrait y avoir un problème avec le remplissage de la mémoire tampon entière, j’ai essayé de limiter mes lectures / écritures à 1 Ko seulement, mais notre serveur a seulement fermé le socket après avoir reçu 1 Ko avant de fermer la connexion.

J’envoie le message d’erreur du serveur au client afin que je puisse l’afficher. Le message d’erreur spécifique que je reçois du client est le suivant:

“Impossible d’écrire des données sur la connexion de transport: une connexion établie a été abandonnée par le logiciel de votre ordinateur hôte.”

J’ai mis à jour mon application serveur pour que le socket TCP sous-jacent utilise «keep alives» avec cette ligne:

client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true); 

Maintenant, chaque fois que je tente d’envoyer un message, le client reçoit l’erreur:

“Impossible d’écrire des données sur la connexion de transport: une connexion existante a été fermée de force par l’hôte distant.”

Notre administrateur réseau m’a dit qu’il n’avait pas de pare-feu ni de port bloqué sur notre serveur interne.

Googler les erreurs, j’ai trouvé des messages suggérant que les gens essaient de telnet sur le serveur. J’ai utilisé leurs instructions pour établir une connexion telnet sur le serveur, mais je ne sais pas trop quoi faire de la réponse:

C:> telnet Bienvenue dans le client Microsoft Telnet

Le caractère d’échappement est ‘CTRL +]’

Microsoft Telnet> open cpapp 500 Connexion à cpapp…

C’est tout ce que j’ai. Je ne reçois jamais d’erreur, et l’écran Telnet de Microsoft finira par passer à «Appuyez sur n’importe quelle touche pour continuer…» – je suppose que le délai d’attente a expiré, mais mon code est en quelque sorte capable de se connecter.

J’ai essayé d’autres ports en code et via Telnet, notamment les ports 25, 80 et 8080. Telnet libère le port 25, mais mon application semble lire la première boucle, quel que soit le port sur lequel elle est exécutée.

Voici mon code qui fonctionne sur les clients:

 int sendUsingTcp(ssortingng location) { ssortingng result = ssortingng.Empty; try { using (FileStream fs = new FileStream(location, FileMode.Open, FileAccess.Read)) { using (TcpClient client = new TcpClient(GetHostIP, CpAppDatabase.ServerPortNumber)) { byte[] riteBuf = new byte[client.SendBufferSize]; byte[] readBuf = new byte[client.ReceiveBufferSize]; using (NetworkStream ns = client.GetStream()) { if ((ns.CanRead == true) && (ns.CanWrite == true)) { int len; ssortingng AOK = ssortingng.Empty; do { len = fs.Read(riteBuf, 0, riteBuf.Length); ns.Write(riteBuf, 0, len); int nsRsvp = ns.Read(readBuf, 0, readBuf.Length); AOK = Encoding.ASCII.GetSsortingng(readBuf, 0, nsRsvp); } while ((len == riteBuf.Length) && (-1 < AOK.IndexOf("AOK"))); result = AOK; return 1; } return 0; } } } } catch (Exception err) { Logger.LogError("Send()", err); MessageBox.Show(err.Message, "Message Failed", MessageBoxButtons.OK, MessageBoxIcon.Hand, 0); return -1; } } 

Voici mon code qui tourne sur le serveur:

 SvrForm.Server = new TcpListener(IPAddress.Any, CpAppDatabase.ServerPortNumber); void Worker_Engine(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; ssortingng path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.CompanyName); if (Directory.Exists(path) == false) Directory.CreateDirectory(path); Thread.Sleep(0); ssortingng eMsg = ssortingng.Empty; try { SvrForm.Server.Start(); do { using (TcpClient client = SvrForm.Server.AcceptTcpClient()) { // waits until data is avaiable if (worker.CancellationPending == true) return; client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true); ssortingng location = Path.Combine(path, ssortingng.Format("Acp{0:yyyyMMddHHmmssff}.bin", DateTime.Now)); byte[] buf = new byte[client.ReceiveBufferSize]; try { using (NetworkStream ns = client.GetStream()) { if ((ns.CanRead == true) && (ns.CanWrite == true)) { try { int len; byte[] AOK = Encoding.ASCII.GetBytes("AOK"); using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write)) { do { len = ns.Read(buf, 0, client.ReceiveBufferSize); fs.Write(buf, 0, len); ns.Write(AOK, 0, AOK.Length); } while ((0 < len) && (ns.DataAvailable == true)); } byte[] okBuf = Encoding.ASCII.GetBytes("Message Received on Server"); ns.Write(okBuf, 0, okBuf.Length); } catch (Exception err) { Global.LogError("ServerForm.cs - Worker_Engine(DoWorkEvent)", err); byte[] errBuf = Encoding.ASCII.GetBytes(err.Message); ns.Write(errBuf, 0, errBuf.Length); } } } } worker.ReportProgress(1, location); } } while (worker.CancellationPending == false); } catch (SocketException) { // See MSDN: Windows Sockets V2 API Error Code Documentation for detailed description of error code e.Cancel = true; } catch (Exception err) { eMsg = "Worker General Error:\r\n" + err.Message; e.Cancel = true; e.Result = err; } finally { SvrForm.Server.Stop(); } } 

Pourquoi mon application ne continue-t-elle pas à lire à partir du client TCP? Ai-je négligé de définir quelque chose qui indique au Socket de restr ouvert jusqu’à la fin? Le code serveur ne voit jamais d’exception car le client TCP n’est jamais arrêté. Je sais donc qu’il n’y a pas d’erreur.

Notre administrateur réseau n’a pas encore reçu son diplôme d’associé. Si cela s’avère être un problème avec le serveur, veuillez décrire en détail la façon de le résoudre, car nous pourrions ne pas comprendre ce que vous dites.

Je m’excuse d’avoir été si long, mais je veux m’assurer que vous sachiez ce que je fais – et peut-être même faire rayonner quelques informations issues de ma technique!

Merci pour ton aide! ~ Joe

Vous devez faire précéder le contenu envoyé de la longueur de ce contenu. Votre boucle suppose que toutes les données sont envoyées avant que la boucle ne s’exécute, alors qu’en réalité, votre boucle s’exécute lors de l’envoi des données. Il y aura des moments où il n’y a pas de données en attente sur le fil, donc la boucle se termine; pendant ce temps, le contenu est toujours envoyé sur le réseau. C’est pourquoi votre boucle ne fonctionne qu’une fois.

Si je lis correctement votre code, vous avez en gros (désolé pour le style c – je ne suis pas doué avec c #:

 faire
 {
   socket = accept ();
   read (socket, buffer);
 } while (not_done);

Si je ne me trompe pas, cela signifie que vous avez besoin… d’un peu plus. Si vous voulez qu’il soit sérialisé, en lisant chaque téléchargement en séquence, vous aurez besoin d’une seconde boucle:

 faire
 {
   socket = accept ();
   do {read (socket, buffer);  not_done_reading = ...;  } while (not_done_reading);
 } while (not_done);

Si vous voulez lire plusieurs envois simultanément, vous aurez besoin de quelque chose de plus semblable à:

 faire
 {
   socket = accept ();
   si (! fork ())
   {
     do {read (socket, buffer);  not_done_reading = ...;  } while (not_done_reading);
   }
 } while (not_done);

Votre exemple telnet est quelque peu contradictoire avec le comportement du code que vous décrivez – si vous pouvez obtenir quoi que ce soit sur le serveur, le “telnet ” devrait vous permettre d’accéder assez rapidement à un écran vide (sous Windows). machine dans l’invite CMD c’est-à-dire). Donc, c’est la première chose étrange – il vaut mieux déboguer avec wireshark, cependant .

En termes de code, je pense que cela peut poser problème avec cette ligne interne sur le serveur:

… while ((0

Vous dites que vous voulez faire une boucle tant que vous avez pu lire quelque chose et qu’il ya quelques données disponibles.

Cependant, il se peut que le deuxième segment ne soit pas encore parvenu au serveur. Par conséquent, aucune donnée n’est disponible pour le moment, vous êtes donc en train de tomber de cette boucle.

Vous devriez recevoir les données en boucle pendant que vous lisez quelque chose et qu’il n’ya pas eu d’erreur de lecture – cela garantit que même sur les liaisons lentes, vous recevrez les données de manière fiable.

Une note de côté:

Je remarque que votre protocole est de type demande-réponse-demande-réponse. Cela fonctionne bien sur le LAN, mais si vous avez besoin à l’avenir de le faire fonctionner sur les liaisons à temps d’aller-retour, cela deviendra un goulot d’étranglement de performances (le protocole MS SMB utilisé pour les transferts de fichiers ou les travaux TFTP de cette façon) .

(Disclaimer: je n’ai pas beaucoup codé en C #, alors je me suis peut-être trompé dans l’interprétation de la méthode “DataAvailable ()”, prenez cette FWIW).

Edit: probablement ma réponse ci-dessus aurait besoin d’être corrigée selon votre protocole – c’est-à-dire que vous deviez d’abord lire la longueur du fichier, puis lire le fichier – car si vous le prenez mot pour mot, cela rompra la façon dont vous l’avez conçu complètement.

Cela dit, avec TCP, il ne faut jamais présumer que le nombre d’opérations write () du côté de l’expéditeur est identique au nombre d’opérations de read () du côté du destinataire – dans certains cas, cela peut être le cas (pas de perte de paquet, pas de Nagle). ) – mais dans le cas général, ce ne serait pas vrai.