C# 为什么';";齐平;强制字节进入网络流?
我有一个项目,我试图向服务器发送一个序列化对象,然后等待“OK”或“ERROR”消息返回 我似乎有一个类似于海报的问题: 问题是,我似乎能够发送原始对象的唯一方法是关闭连接,但是(当然)我迫不及待地想看看服务器是否成功处理了该对象C# 为什么';";齐平;强制字节进入网络流?,c#,sockets,tcp,tcpclient,networkstream,C#,Sockets,Tcp,Tcpclient,Networkstream,我有一个项目,我试图向服务器发送一个序列化对象,然后等待“OK”或“ERROR”消息返回 我似乎有一个类似于海报的问题: 问题是,我似乎能够发送原始对象的唯一方法是关闭连接,但是(当然)我迫不及待地想看看服务器是否成功处理了该对象 private void button4_Click(object sender, EventArgs e) { RequestPacket req = new RequestPacket(); /// ... Fill out request pa
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.
}
}
是的。。。。就这么简单。您应该使用链接到网络流的StreamWriter/Reader。Flush在网络流上不执行任何操作,请参见此处: 哦,你可以责怪。不过,这与C无关,它是TCP/IP堆栈的默认行为。使用方法启用套接字选项。但是要小心,禁用Nagle的算法会降低吞吐量
我也不确定您在套接字上使用的流,因为我根本不是C#开发人员,但请尝试删除它的实例,这样它就可以肯定地编写:-)短版本显然是,当使用XmlSerializer(或任何其他大blob)将数据推送到网络流中时,它只是将线路无限期地保持打开状态,等待写入更多信息。它仅在您关闭连接后刷新连接。这就造成了这样一种情况:这种方法非常适合发送,但不适合接收。反之亦然。它变成了单向通信,对于在同一连接上继续来回通信是无用的 我不得不处理一些表面上看起来很优雅的事情,这有点糟糕,但回到我以前的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;
}
}
public class PacketTransit
{
公共静态void SendPacket(TcpClient C,object Packet)
{
MemoryStream ms=新的MemoryStream();
XmlSerializer xml=新的XmlSerializer(Packet.GetType());
序列化(ms,数据包);
ms.Position=0;
字节[]b=ms.GetBuffer();
Dispose女士();
byte[]sizePacket=BitConverter.GetBytes(b.Length);
//首先发送4字节大小的数据包。
C.Client.Send(sizePacket、sizePacket.Length、SocketFlags.None);
C.Client.Send(b,b.长度,SocketFlags.None);
}
///字符串是需要转换的XML文件。
公共静态字符串ReceivePacket(TcpClient C,类型PacketType)
{
字节[]FirstTen=新字节[1024];
int size=0;
byte[]sizePacket=BitConverter.GetBytes(大小);
//获取数据包的大小
int sp=C.Client.Receive(sizePacket、sizePacket.Length、SocketFlags.None);
如果(sp 0)
{
字节[]b=新字节[1024];
int x=大小;
如果(x>1024)x=1024;
int r=C.Client.Receive(b,x,SocketFlags.None);
尺寸-=r;
sb.Append(UTF8Encoding.UTF8.GetString(b));
}
使某人返回字符串();
}
///需要转换回相应类型的XML数据。
公共静态对象解码(字符串PacketData,类型PacketType)
{
MemoryStream ms=新的MemoryStream(UTF8Encoding.UTF8.GetBytes(PacketData));
XmlSerializer xml=新的XmlSerializer(PacketType);
object obj=xml.Deserialize(ms);
Dispose女士();
返回obj;
}
公共静态RequestPacket GetRequestPacket(TcpClient C)
{
string str=ReceivePacket(C,typeof(RequestPacket));
如果(str==“”)返回新的RequestPacket();
RequestPacket req=(RequestPacket)解码(str,typeof(RequestPacket));
返回请求;
}
公共静态响应包GetResponsePacket(TcpClient C)
{
string str=ReceivePacket(C,typeof(ResponsePacket));
如果(str==“”)返回新的ResponsePacket();
responsepacketres=(ResponsePacket)解码(str,typeof(ResponsePacket));
返回res;
}
}
要使用这个类,我只需要调用PacketTransit.SendPacket(myTcpClient,SomePacket)
来发送任何给定的XML可序列化对象。然后,我可以使用PacketTransit.GetResponsePacket
或PacketTransit.GetRequestPacket
在另一端接收它
对我来说,这非常有效,但这比最初预期的要困难得多。我认为真正的问题可能是XmlDeserializer在从流中读取EOS之前可能不会返回。您可能需要关闭输出的发送流,以强制执行此操作 如果流上的flush是惰性的,那么添加一个额外的层(读写器)不会改变任何事情。我也尝试过。。。还是没有这样的运气。这也是我最初的想法。@Jerry我在与工业自动化设备通信时遇到了几乎相同的问题,设置
节点延迟
属性对我很有效,尽管我也没有使用GetStream
方法。你能在同一条流上来回进行双向通信吗??即使使用NoDelay选项,我也无法做到这一点。Nagle的算法不会延迟数据包,直到套接字关闭。最大延迟约为200毫秒。不足以解释这个问题。这个包叫什么名字?2.你有没有抛出异常