2016-04-29 1 views
2

J'ai un client simple et un serveur en C#. L'objectif est que le client envoie un document XML au serveur, puis le serveur doit répondre avec un document XML différent. Le serveur bloque lorsque j'essaie de recevoir l'entrée/requête XML avec XmlDocument.Load (NetworkStream).Pourquoi le serveur bloque-t-il lors du chargement de XML à partir d'un NetworkStream?

Serveur:

 TcpListener t = new TcpListener(IPAddress.Any, 4444); 
     t.Start(); 
     TcpClient c = t.AcceptTcpClient(); 
     NetworkStream s = c.GetStream(); 
     XmlDocument req = new XmlDocument(); 
     req.Load(s); 

     string respString = "<response><data>17</data></response>"; 
     XmlDocument resp = new XmlDocument(); 
     resp.LoadXml(respString); 
     resp.Save(s); 

Client:

 TcpClient t = new TcpClient("localhost", 4444); 
     NetworkStream s = t.GetStream(); 

     string reqStr = "<request><parameters><param>7</param><param>15</param></parameters></request>"; 
     XmlDocument req = new XmlDocument(); 
     req.LoadXml(reqStr); 

     req.Save(s); 

     XmlDocument resp = new XmlDocument(); 
     resp.Load(s); 

     Console.WriteLine(resp.OuterXml); 

J'ai essayé d'ajouter une quinte flush() dans le client après qu'il enregistre le XmlDocument demande au flux, mais cela ne semble pas aider . À l'origine, j'ai essayé que le serveur lise toute l'entrée du client sur un MemoryStream, mais j'ai ensuite trouvé qu'il n'y avait aucun moyen de signifier au serveur que toute l'entrée était faite sans déconnexion, ce qui signifiait que le client ne pouvait pas lire son entrée.

Je peux envoyer une entrée XML au serveur à partir d'un fichier avec netcat et tout fonctionne correctement. Cela fonctionne si j'utilise XmlDocument.Load (NetworkStream) dans le serveur, ou lire toute l'entrée dans un MemoryStream. Qu'est-ce que netcat fait dans ce cas que je ne fais pas dans mon client C#, et comment puis-je le faire en C#? Devrais-je m'y prendre différemment?

+0

Qu'en est-il du pare-feu? – qub1n

+0

Il peut y avoir plusieurs raisons pour lesquelles le flux est bloqué comme un pare-feu ou un antivirus bloquant le numéro de port. Je voudrais d'abord essayer de voir si vous pouvez télécharger xml en utilisant un navigateur web IE manuellement. Si vous le pouvez, ce n'est pas un problème de pare-feu. Je voudrais alors utiliser un renifleur comme wireshark ou violon pour capturer le téléchargement IE et comparer les résultats avec votre application C#. Habituellement, le problème est l'en-tête http dans l'application C# doit être modifié pour correspondre aux résultats IE. – jdweng

+0

Ce n'est pas le pare-feu ou un problème de réseau - Netcat de la même machine peut envoyer l'entrée requise. Je ne vois pas comment je pourrais tester ce scénario avec IE, car IE n'enverra pas de requête XML à mon serveur. – skiphoppy

Répondre

5

La connexion Tcp est bidirectionnelle et vous pouvez en fermer la moitié tout en laissant l'autre moitié ouverte. Dans ce cas, vous pouvez fermer la moitié du client au serveur après l'envoi de toutes les données, et vous pouvez ensuite recevoir la réponse sur le serveur à la moitié du client. Vous pouvez le faire pour votre exemple comme ceci:

TcpClient t = new TcpClient("localhost", 4444); 
NetworkStream s = t.GetStream(); 
string reqStr = "<request><parameters><param>7</param><param>15</param></parameters></request>"; 
XmlDocument req = new XmlDocument(); 
req.LoadXml(reqStr); 
req.Save(s); 
// important line here! shutdown "send" half of the socket connection. 
t.Client.Shutdown(SocketShutdown.Send); 
XmlDocument resp = new XmlDocument(); 
resp.Load(s); 
Console.WriteLine(resp.OuterXml); 

Ne pas oublier flux de réseau disposer sur le côté d'un serveur après avoir envoyé toutes les données:

TcpListener t = new TcpListener(IPAddress.Loopback, 4444); 
t.Start(); 
TcpClient c = t.AcceptTcpClient(); 
using (NetworkStream s = c.GetStream()) { 
    XmlDocument req = new XmlDocument(); 
    req.Load(s);     
    Console.WriteLine("Got request: {0}", req.OuterXml); 
    string respString = "<response><data>17</data></response>"; 
    XmlDocument resp = new XmlDocument(); 
    resp.LoadXml(respString); 
    resp.Save(s); 
} 

En fait, si la communication entière est seule demande suivie réponse unique - vous pouvez utiliser cette technique à la place des protocoles personnalisés sur tcp (n'oubliez pas d'utiliser les délais d'attente et de disposer correctement vos flux et vos clients tcp). Sinon, rappelez-vous que le flux réseau est une sorte de connexion ouverte entre le client et le serveur - il n'a pas de "fin" explicite. Quand vous faites XmlDocument.Load - il lira jusqu'à ce que cela soit possible (jusqu'à ce que Read revienne avec 0 octets lus), donc il bloque dans votre cas. Vous devriez définir votre propre protocole sur tcp, de sorte que vous puissiez définir vous-même les limites du message. Une manière simple serait - les 4 premiers octets définissent la longueur du message suivant. Donc, vous lisez d'abord 4 octets, puis lisez jusqu'à ce que cette longueur soit atteinte ou que le délai expire.

+0

@skiphoppy voir la mise à jour, je pense qu'il répond à votre question d'une manière plus complète. – Evk

+0

Merci, @Evk, je pense que le Client.Shutdown (SocketShutdown.Send) peut être ce que j'ai manqué. Je suis tombé sur Shutdown() hier mais je n'ai pas réalisé qu'il y avait des paramètres pour lui dire de ne fermer qu'un côté. – skiphoppy

+0

Rats. Lorsque j'essaie Client.Shutdown (SocketShutdown.Send), cela m'amène un peu plus loin, mais lorsque le client essaie de lire ce que le serveur envoie, il obtient "Impossible de lire les données de la connexion de transport: une connexion existante était forcée fermé par l'hôte distant. " Ce qui sonne comme les deux côtés se sont fermés. – skiphoppy