C#为什么不“刷新”强制网络流中的字节?

我有一个项目,我正在尝试将序列化对象发送到服务器,然后等待“OK”或“ERROR”消息返回。

我似乎遇到了类似的问题: TcpClient发送/关闭问题

问题是,我似乎能够发送原始对象的唯一方法是关闭连接,但然后(当然)我迫不及待地想看看服务器是否成功处理了对象。

private void button4_Click(object sender, EventArgs e) { RequestPacket req = new RequestPacket(); /// ... Fill out request packet ... /// Connect to the SERVER to send the message... TcpClient Client = new TcpClient("localhost", 10287); using (NetworkStream ns = Client.GetStream()) { XmlSerializer xml = new XmlSerializer(typeof(RequestPacket)); xml.Serialize(ns, req); /// NOTE: This doesn't seem to do anything.... /// The server doesn't get the object I just serialized. /// However, if I use ns.Close() it does... /// but then I can't get the response. ns.Flush(); // Get the response. It should be "OK". ResponsePacket resp; XmlSerializer xml2 = new XmlSerializer(typeof(ResponsePacket)); resp = (ResponsePacket)xml2.Deserialize(ns); /// ... EVALUATE RESPONSE ... } Client.Close() } 

更新:作为对一位评论者的回应,我认为客户不会有错。 它只是在等待对象,对象永远不会到来,直到我关闭套接字….但是,如果我错了,我会公开吃掉乌鸦。 =)这是客户:

  static void Main(string[] args) { // Read the port from the command line, use 10287 for default CMD cmd = new CMD(args); int port = 10287; if (cmd.ContainsKey("p")) port = Convert.ToInt32(cmd["p"]); TcpListener l = new TcpListener(port); l.Start(); while (true) { // Wait for a socket connection. TcpClient c = l.AcceptTcpClient(); Thread T = new Thread(ProcessSocket); T.Start(c); } } static void ProcessSocket(object c) { TcpClient C = (TcpClient)c; try { RequestPacket rp; //// Handle the request here. using (NetworkStream ns = C.GetStream()) { XmlSerializer xml = new XmlSerializer(typeof(RequestPacket)); rp = (RequestPacket)xml.Deserialize(ns); } ProcessPacket(rp); } catch { // not much to do except ignore it and go on. } } 

是的……就这么简单。

哦,你可以责怪Nagle的算法 。 它与C#无关,但它是TCP / IP堆栈的默认行为。 使用SetSocketOption方法启用NoDelay套接字选项。 但要小心,禁用Nagle的算法会降低吞吐量。

我也不确定你在套接字上使用的那个流,因为我根本不是C#开发人员,但是尝试删除它的实例,所以它确实写了:-)

显然,短版本在使用XmlSerializer(或任何其他大blob)将数据推送到NetworkStream时,它只会无限期地保持行打开,等待写入更多信息。 它只会在关闭连接后刷新连接。 这会产生一种情况,即此方法非常适合发送,但不能接收。 或相反亦然。 它变成单向通信,对于在同一连接上继续来回通信毫无用处。

我不得不解决表面上看起来如此优雅的东西,但是回到我原来的C天,我先是先发送一个“字节数”的数据包,然后是真正的数据包。 这使我能够在另一端读取确切的字节数,这样我就不会陷入阻塞模式。

为了简化我的生活,我创建了一个类,它包含一些静态方法,用于发送和接收。 这个类可以在网络上发送任何XML可序列化的类,因此它可以完成我需要它做的事情。

如果有人有更优雅的解决方案,我会乐于听到它。

 public class PacketTransit { public static void SendPacket(TcpClient C, object Packet) { MemoryStream ms = new MemoryStream(); XmlSerializer xml = new XmlSerializer(Packet.GetType()); xml.Serialize(ms, Packet); ms.Position = 0; byte[] b = ms.GetBuffer(); ms.Dispose(); byte [] sizePacket = BitConverter.GetBytes(b.Length); // Send the 4-byte size packet first. C.Client.Send(sizePacket, sizePacket.Length, SocketFlags.None); C.Client.Send(b, b.Length, SocketFlags.None); } /// The string is the XML file that needs to be converted. public static string ReceivePacket(TcpClient C, Type PacketType) { byte [] FirstTen = new byte[1024]; int size = 0; byte[] sizePacket = BitConverter.GetBytes(size); // Get the size packet int sp = C.Client.Receive(sizePacket, sizePacket.Length, SocketFlags.None); if (sp <= 0) return ""; size = BitConverter.ToInt32(sizePacket, 0); // read until "size" is met StringBuilder sb = new StringBuilder(); while (size > 0) { byte[] b = new byte[1024]; int x = size; if (x > 1024) x = 1024; int r = C.Client.Receive(b, x, SocketFlags.None); size -= r; sb.Append(UTF8Encoding.UTF8.GetString(b)); } return sb.ToString(); } /// The XML data that needs to be converted back to the appropriate type. public static object Decode(string PacketData, Type PacketType) { MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(PacketData)); XmlSerializer xml = new XmlSerializer(PacketType); object obj = xml.Deserialize(ms); ms.Dispose(); return obj; } public static RequestPacket GetRequestPacket(TcpClient C) { string str = ReceivePacket(C, typeof(RequestPacket)); if (str == "") return new RequestPacket(); RequestPacket req = (RequestPacket) Decode(str, typeof(RequestPacket)); return req; } public static ResponsePacket GetResponsePacket(TcpClient C) { string str = ReceivePacket(C, typeof(ResponsePacket)); if (str == "") return new ResponsePacket(); ResponsePacket res = (ResponsePacket)Decode(str, typeof(ResponsePacket)); return res; } } 

要使用这个类,我只需要调用PacketTransit.SendPacket(myTcpClient, SomePacket)来发送任何给定的XML-Serializable对象。 然后我可以使用PacketTransit.GetResponsePacketPacketTransit.GetRequestPacket在另一端接收它。

对我而言,这种方法运作良好,但它比原先预期的更多。

你应该使用链接到网络流的StreamWriter / Reader,.Flush在NetworkStream上什么也不做,请看这里:

http://www.c-sharpcorner.com/UploadFile/dottys/SocketProgDTRP11222005023030AM/SocketProgDTRP.aspx

我相信这里真正的问题可能是XmlDeserializer在从流中读取EOS之前可能不会返回。 您可能需要关闭发送流以进行输出以强制执行此操作。