2016-04-29 6 views
2

Ich habe einen einfachen Client und Server in C#. Das Ziel besteht darin, dass der Client ein XML-Dokument an den Server sendet und der Server dann mit einem anderen XML-Dokument antworten sollte. Der Server blockiert, wenn ich versuche, die Eingabe-/Anfrage-XML mit XmlDocument.Load (NetworkStream) zu empfangen.Warum blockiert der Server beim Laden von XML von einem NetworkStream?

Server:

 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); 

Auftraggeber:

 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); 

Ich habe versucht, einen Flush() in dem Client hinzugefügt, nachdem er die Anfrage XmlDocument zum Strom spart, aber das schien nicht zu helfen . Ursprünglich versuchte ich, den Server alle Eingaben vom Client zu einem MemoryStream lesen zu lassen, aber dann stellte ich fest, dass es keine Möglichkeit gab, dem Server zu signalisieren, dass alle Eingaben ohne Trennung gemacht wurden, was dann der Client nicht konnte lies seine Eingabe.

Ich kann XML-Eingabe an den Server aus einer Datei mit Netcat und alles funktioniert gut. Das funktioniert, ob ich XmlDocument.Load (NetworkStream) auf dem Server verwende oder alle Eingaben in einen MemoryStream lese. Was macht Netcat in diesem Fall, den ich in meinem C# -Client nicht mache, und wie mache ich das in C#? Sollte ich das anders machen?

+0

Was ist mit Firewall? – qub1n

+0

Es kann viele Gründe geben, warum der Stream wie eine Firewall oder ein Viruschecker blockiert wird, der die Portnummer blockiert. Ich würde zuerst versuchen zu sehen, ob Sie xml manuell mit einem IE Web-Browser herunterladen können. Wenn Sie das können, ist das kein Firewall-Problem. Ich würde dann einen Sniffer wie wireshark oder fiddle verwenden, um IE Download zu erfassen und Ergebnisse mit Ihrer C# -Anwendung zu vergleichen. Normalerweise ist das Problem der http-Header in der C# -Anwendung, der geändert werden muss, um den IE-Ergebnissen zu entsprechen. – jdweng

+0

Es ist nicht die Firewall oder ein Netzwerkproblem - Netcat von der gleichen Maschine kann die erforderliche Eingabe senden. Ich sehe nicht, wie ich dieses Szenario mit IE testen könnte, weil IE keine XML-Anfrage an meinen Server senden wird. – skiphoppy

Antwort

5

Die TCP-Verbindung ist bidirektional und Sie können die Hälfte davon schließen, während die andere Hälfte noch offen ist. In diesem Fall können Sie die Client-Server-Hälfte nach dem Senden aller Daten schließen, und dann können Sie die Antwort über den Server an die Client-Hälfte erhalten. Sie können es so für Ihr Beispiel tun:

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); 

Vergessen Sie nicht, Netzwerk-Stream auf einem Server Seite verfügen, nachdem Sie alle Daten gesendet:

Eigentlich
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); 
} 

wenn ganze Kommunikation durch einzelne Anforderung folgt Einzelantwort - Sie können diese Technik anstelle von benutzerdefinierten Protokollen über TCP verwenden (denken Sie daran, Timeouts zu verwenden und Ihre Streams und TCP-Clients ordnungsgemäß zu entsorgen).

Andernfalls denken Sie daran, dass Netzwerk-Stream eine Art offene Verbindung zwischen Client und Server ist - es hat kein explizites "Ende". Wenn Sie XmlDocument.Load tun - es wird gelesen, bis es möglich ist (das heißt, bis Read mit 0 Byte gelesen wird), so blockiert es in Ihrem Fall. Sie sollten Ihr eigenes Protokoll über TCP definieren, damit Sie selbst die Nachrichtengrenze definieren können. Einfacher Weg wäre - die ersten 4 Bytes definieren die Länge der folgenden Nachricht. Sie lesen also die ersten 4 Bytes und lesen dann, bis diese Länge erreicht ist oder ein Timeout auftritt.

+0

@skiphoppy siehe das Update, ich denke, es beantwortet Ihre Frage auf eine vollständigere Art und Weise. – Evk

+0

Danke, @Evk, ich denke, die Client.Shutdown (SocketShutdown.Send) kann sein, was ich vermisst habe. Ich bin gestern in Shutdown() gestolpert, habe aber nicht bemerkt, dass es Parameter gibt, die sagen, dass man nur eine Seite herunterfahren soll. – skiphoppy

+0

Ratten. Wenn ich ClientShutdown (SocketShutdown.Send) versuche, bringt es mich etwas weiter, aber dann, wenn der Client versucht zu lesen, was der Server sendet, erhält es "Kann Daten von der Transportverbindung nicht lesen: Eine bestehende Verbindung wurde gewaltsam vom entfernten Host geschlossen. " Was nach beiden Seiten klingt, wurde abgeschaltet. – skiphoppy

Verwandte Themen