2009-08-10 6 views
6

J'ai une application que j'ai écrite pour mon application distribuée dans toute l'entreprise pour m'envoyer des données via notre serveur Windows 2003 (exécutant IIS 6.0). Les petits messages textuels passent, mais les messages plus volumineux contenant plus de données (environ 20 Ko) ne passent pas.TCP Client Connection

Je définis le tampon d'octets sur la taille du tampon du client TCP. J'ai remarqué que mes données étaient reçues sur le serveur; cependant, il a seulement bouclé la routine de réception une fois, et mes gros fichiers ont toujours exactement la taille de la taille du tampon, soit 8 Ko sur notre serveur. En d'autres termes, mon code ne fait que passer une boucle avant que le serveur ne ferme la connexion socket. En pensant qu'il pourrait y avoir un problème avec le remplissage de la mémoire tampon, j'ai essayé de limiter mes lectures/écritures à seulement 1 Ko, mais cela a eu pour résultat que notre serveur ferme le socket après avoir reçu 1 Ko avant de fermer la connexion.

Je renvoie le message d'erreur du serveur au client pour que je puisse l'afficher. Le message d'erreur spécifique que je reçois du client:

“Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.”

Je mis à jour mon application serveur afin que le socket TCP sous-jacente utiliserait « garder alive » 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:

“Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.”

administrateur notre réseau m'a dit qu'il ne dispose pas d'un pare-feu ou des ports bloqués sur notre serveur interne. En recherchant les erreurs, j'ai trouvé des messages suggérant que les gens essayent de se connecter au serveur par telnet. Je leur direction telnet sur le serveur, mais je ne suis pas sûr que faire de la réponse:

C:> telnet Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]’

Microsoft Telnet> open cpapp 500 Connecting To cpapp…

C'est tout ce que je reçois. Je ne reçois jamais une erreur, et l'écran Telnet de Microsoft finira par changer à "Appuyez sur n'importe quelle touche pour continuer ..." - Je suppose que cela expire, mais mon code est en quelque sorte capable de se connecter. J'ai essayé d'autres ports dans le code et via Telnet y compris 25, 80 et 8080. Telnet envoie le port 25, mais mon application semble lire la première boucle quel que soit le port que je lui dis de lancer.

Voici mon code qui fonctionne sur les clients:

int sendUsingTcp(string location) { 
    string result = string.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; 
      string AOK = string.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.GetString(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 fonctionne sur le serveur:

SvrForm.Server = new TcpListener(IPAddress.Any, CpAppDatabase.ServerPortNumber); 

void Worker_Engine(object sender, DoWorkEventArgs e) { 
    BackgroundWorker worker = sender as BackgroundWorker; 
    string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.CompanyName); 
    if (Directory.Exists(path) == false) Directory.CreateDirectory(path); 
    Thread.Sleep(0); 
    string eMsg = string.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); 
     string location = Path.Combine(path, string.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 pas ma demande continuer à lire à partir du TCP Client ? Ai-je négligé de mettre quelque chose qui dit au Socket de rester ouvert jusqu'à ce que j'aie fini? Le code serveur ne voit jamais une exception car le client TCP n'est jamais arrêté, donc je sais qu'il n'y a pas d'erreur. Notre administrateur réseau n'a pas encore reçu son diplôme d'associé, donc s'il s'avère que ce soit un problème avec le serveur, veuillez le décrire dans votre description de la façon de le résoudre, car nous pourrions ne pas comprendre ce que vous dites . Je m'excuse d'être si long, mais je veux m'assurer que les gens savent ce que je fais - et peut-être même éclaircir certaines informations de ma technique!

Merci de votre aide! ~ Joe

Répondre

6

Vous devez précéder le contenu envoyé avec la longueur de ce contenu. Votre boucle suppose que toutes les données sont envoyées avant l'exécution de la boucle, alors qu'en réalité, votre boucle est en cours d'exécution lorsque les données sont envoyé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é à travers le fil. C'est pourquoi votre boucle ne fonctionne qu'une seule fois.

+0

Sans blague? Je suppose que cela a du sens maintenant. J'ai continué à penser que quelque chose devait être installé sur le serveur. Cela aide un * lot *! Je vous remercie! – jp2code

+0

comment ferais-je cela? "précéder le contenu envoyé avec la longueur de ce contenu" je n'ai pas la longueur du contenu envoyé? –

+0

Si vous ne connaissez pas la longueur, alors évidemment vous n'envoyez pas la longueur sur le fil. – Amy

1

Si je lis votre code correctement, vous avez essentiellement obtenu (désolé pour le c style - Je ne suis pas bon avec C#:

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

Si je ne me trompe pas, il . Les moyens dont vous avez besoin ... un peu plus là-dedans si vous voulez qu'il soit publié en feuilleton, la lecture de chaque téléchargement dans l'ordre, vous voulez une deuxième boucle:

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

Si vous voulez lire plusieurs ajouts simultaniously, vous aurez besoin de quelque chose comme:

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

Votre exemple telnet est un peu en contradiction avec le comportement du code que vous décrivez - si jamais vous êtes en mesure d'obtenir quoi que ce soit sur le serveur, la « telnet < nom d'hôte > <portnumber> » devrait vous rendre à un blanc écran assez rapidement (sur Windows machine dans l'invite CMD qui est). Donc, c'est la première chose étrange - mieux débogué avec wireshark, though.

code-sage, je pense qu'il peut être un problème avec cette ligne intérieure sur le serveur:

... while ((0 < len) & & (ns.DataAvailable == true));

Vous dites que vous voulez faire une boucle pendant que vous étiez en mesure de lire quelque chose et que des données sont disponibles.

Cependant, il se peut que le deuxième segment ne soit pas encore arrivé sur le serveur, donc il n'y a pas encore de données disponibles, vous sortez de cette boucle.

Vous devriez recevoir en boucle des données pendant que vous lisez quelque chose et alors qu'il n'y a pas eu d'erreur de lecture - cela garantit que même sur les liaisons lentes vous recevrez les données de façon fiable.

Une note de côté:

je remarque votre protocole est de type requête-réponse-demande-réponse. Il fonctionne bien sur le LAN, mais si vous avez besoin à l'avenir de le faire fonctionner sur les liens à haut rendement, cela deviendra un gros goulot d'étranglement des performances (le protocole MS SMB utilisé pour les transferts de fichiers ou TFTP fonctionne de cette manière) . (Disclaimer: Je n'ai pas beaucoup codé en C#, donc je me suis trompé dans l'interprétation de la méthode "DataAvailable()", prenez ce FWIW). Editer: probablement ma réponse ci-dessus devrait être corrigée en fonction de votre protocole - c'est-à-dire que vous auriez besoin de lire d'abord la longueur du fichier, puis lire le fichier - puisque si vous le prenez textuellement, il cassera le comme vous l'avez conçu complètement. Cela dit, avec TCP on ne devrait jamais supposer que le nombre d'opérations write() du côté de l'expéditeur est le même que le nombre d'opérations read() du côté récepteur - dans certains cas cela peut être le cas (pas de paquet perte, pas Nagle) - mais dans le cas général ce ne serait pas vrai.