您好我正在尝试在客户端/服务器应用程序之间发送和接收一些数据。
类
[Serializable]
public class ScanSessie
{
public string UserId { get; set; }
public int TotalScanned { get; set; }
public string Status { get; set; }
public string DeviceId { get; set; }
}
Serializer和Deserializer扩展方法:
public static class SerializerDeserializerExtensions
{
public static byte[] Serializer(this object _object)
{
byte[] bytes;
using (var _MemoryStream = new MemoryStream())
{
IFormatter _BinaryFormatter = new BinaryFormatter();
_BinaryFormatter.Serialize(_MemoryStream, _object);
bytes = _MemoryStream.ToArray();
}
return bytes;
}
public static T Deserializer<T>(this byte[] _byteArray)
{
T ReturnValue;
using (var _MemoryStream = new MemoryStream(_byteArray))
{
IFormatter _BinaryFormatter = new BinaryFormatter();
ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);
}
return ReturnValue;
}
}
我尝试发送和反序列化的示例数据是:
List<ScanSessie> scannerCl = new List<ScanSessie>();
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
使用以下代码发送:
NetworkStream broadcastStream = statusSocket.GetStream();
Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl);
broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
broadcastStream.Flush();
接收流代码。忽略n.DataAvailable循环。我在测试目的上发送非常小的数据包,在缓冲区下面转移一部分。
using (TcpClient client = new TcpClient())
{
await client.ConnectAsync("10.184.32.39", 8888);
ConnectionEstabilished?.Invoke();
byte[] message = new byte[1024 * 8];
int bytesRead;
using (NetworkStream n = client.GetStream())
{
while (true)
{
bytesRead = 0;
try
{
if(n.CanRead)
{
do
{
bytesRead = await n.ReadAsync(message, 0, message.Length);
}
while (n.DataAvailable);
}
//bytesRead = await n.ReadAsync(message, 0, 4096);
//bytesRead = await n.ReadAsync(message, 0, message.Length);
}
catch (Exception ex)
{
// some error hapens here catch it
Debug.WriteLine("DashboardClientConnect " + ex.Message + "\n" + ex.InnerException);
break;
}
}
接收代码将触发一个事件,其数据为byte [],定义为消息。 在我的处理事件中,我尝试使用以下命令反序列化:
private void Sc_NewDataReceived(byte[] scanner)
{
try
{
var result = scanner.Deserializer<List<ScanSessie>>();
}
catch(Exception ex)
{
Debug.WriteLine(ex.InnerException);
}
}
在反序列化步骤中,它将抛出异常(抛出异常:mscorlib.dll中的'System.Runtime.Serialization.SerializationException') Innerexception为null。
当我使用扩展方法而不通过网络发送一些示例数据时,反序列化工作完美无瑕。
我也尝试过使用接收缓冲区大小。这似乎也没有帮助。示例数据大小低于1024 * 8.
例如,发送数据长度为600字节。接收端缓冲区是否也需要相同的大小?通常这应该是一个问题,因为它读取块。
2天后我放弃了。任何帮助,将不胜感激。我试图通过放置正常运行的代码片段来使问题具有信息性。
答案 0 :(得分:1)
此代码有几个问题:
do
{
bytesRead = await n.ReadAsync(message, 0, message.Length);
}
while (n.DataAvailable);
首先,发件人可能会以块的形式发送消息字节。首先,您将读取一个块,然后您将读取另一个块,在缓冲区中首先覆盖一个块(因为您对所有读取使用相同的缓冲区,并且始终写入索引0)。
此外,您还不知道何时完全收到邮件。发件人可能会发送一个块,然后由于某种原因会有延迟,此时您的while (n.DataAvailable)
循环存在,并且您正在尝试反序列化不完整的消息。
在它的顶部 - 当你幸运地收到完整的消息时 - 缓冲区的其余部分(假设消息长度小于缓冲区大小)充满了零,这无论如何都会阻止成功的反序列化。
此外 - 使用BinaryFormatter
通过网络序列化对象不是一个好主意。而是使用像protobuf这样的东西。
修复网络代码 - 首先发送序列化邮件的长度。如果您有不同的消息类型 - 也发送消息类型。发送长度(并在必要时输入) - 发送消息本身:
NetworkStream broadcastStream = statusSocket.GetStream();
Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl);
var lengthBytes = BitConverter.GetBytes(broadcastBytes.Length);
if (!BitConverter.IsLittleEndian)
Array.Reverse(lengthBytes);
broadcastStream.Write(lengthBytes, 0, lengthBytes.Length);
broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
broadcastStream.Flush();
然后在接收方:
byte[] lengthBuffer = new byte[sizeof(int)];
var bytesRead = await n.ReadAsync(lengthBuffer, 0, lengthBuffer.Length);
// check if bytesRead equals buffer size
if (!BitConverter.IsLittleEndian)
Array.Reverse(lengthBuffer);
var length = BitConverter.ToInt32(lengthBuffer, 0);
// check if length is not too big, otherwise you will crash process with out of memory
var messageBuffer = new byte[length];
bytesRead = await n.ReadAsync(messageBuffer, 0, messageBuffer.Length);
// check if bytesRead equals buffer size
// now you can deserialize
请注意,此代码未经测试,请注意。
答案 1 :(得分:0)
我建议使用WCF,.NET Remoting或类似的东西。它们的设计正是为了这个目的。
如果您编写自己的传输通道,通常必须将序列化数据嵌入数据通信协议中。通常,这包含消息长度和有关传输数据的其他信息。这是必需的,例如让接收者知道数据/消息大小和数据类型。在接收方,通常你不知道,必须接收多少字节以及它们是什么。