c#在同一个Socket上发送多个对象
本文关键字:对象 同一个 Socket | 更新日期: 2023-09-27 17:50:05
我试图从一个套接字发送多个不同的对象(好吧,它们是相同的对象类型,只是具有不同的值)到另一个。我可以用下面的代码发送一个对象:
客户:var buffer = new byte[1024];
var binFormatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
int bytesRead;
while ((bytesRead = _clientReceive.Receive(buffer)) > 0);
{
ms.Write(buffer, 0, bytesRead);
}
ms.Position = 0;
message = (Message)binFormatter.Deserialize(ms);
}
服务器:var gm = new Message { Message = "ObjectType", Object = Data };
using (var mem = new MemoryStream())
{
var binFormatter = new BinaryFormatter();
binFormatter.Serialize(mem, gm);
var gmData = mem.ToArray();
client.Send(gmData, gmData.Length, SocketFlags.None);
client.Close();
}
但是如果我想发送多个消息(将完整的客户端代码放在循环中而不调用client.Close()
),我如何能够确定客户端何时接收到完整的对象?像这样将客户端代码放入循环中:
while (_isReceivingStarted)
{
var buffer = new byte[1024];
var binFormatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
int bytesRead;
while ((bytesRead = _clientReceive.Receive(buffer)) > 0)
{
ms.Write(buffer, 0, bytesRead);
}
ms.Position = 0;
message = (Message) binFormatter.Deserialize(ms);
}
// Do stuff then wait for new message
}
客户端将在_clientReceive.Receive(buffer)
处挂起,因为它没有从客户端接收Close()并且从未获得接收到的0字节。如果我关闭服务器上的连接,它将循环,然后在Deserialize MemoryStream出错,而不是在_clientReceive.Receive(buffer)
处阻塞,因为预计会发送另一个对象。
我希望这有意义。指针吗?
我强烈推荐大家看看Windows Communication Foundation。它将为您处理所有这些管道,包括通过多个可配置通道对类型进行序列化和反序列化等。
当使用这样的套接字时,我会使用类似protobuf-net的东西而不是BinaryFormatter
(警告/披露:我写了它)特别是,如果你写每个项目与Serializer.SerializeWithLenthPrefix(...)
使用Base128前缀样式(其中一个选项),和一些已知的标签(另一个选项),如1
,然后在另一端你可以使用DeserializeItems<Foo>(...)
(再次指定Base128和1
)。
这将正确地挑选条目,而不会尝试过度读取,直到流关闭,当它将yield break
。每个对象都是单独返回的(yield return
),所以在给你数据之前,它不会试图消耗整个流。
如果您愿意,它也可以用于异构数据,但同构数据是最简单的。
您可以发送一个头消息,通知接收端期望多少字节。这类似于HTTP中的Content-Length指令。另一种选择是,在末尾发送一个自定义终止字符串(显然,它不能在序列化对象的二进制有效负载中出现,这就是为什么前一种解决方案是我要做的)。
查看TcpListener类:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx或者采纳Reed的建议,看看WCF。
如果你想这么做,这里有一些建议:
- 发送X字节数并不能保证您将在客户端的一次receive()中接收到它们。您可以在客户端上有一个MemoryStream和一个缓冲区。使用缓冲区接收并写入内存流,直到Receive返回0。
- 当你发送完数据后,使用client。在调用Close()之前关闭(SocketShutdown.Send)(或。both)。这将阻止客户端的TCP RST。
- 如果你想序列化多个对象,只需在服务器中一个接一个地序列化它们。然后客户端将缓冲所有传入的数据,当Receive()返回0时,将客户端MemoryStream中的位置移到0,并开始逐个反序列化对象,直到ms.Position == ms.Length。