我有一个很大的问题。试图创建一个必须包含两部分的应用程序:服务器端和客户端。这两个部分必须以某种方式进行交流并交换对象。我决定使用套接字,因为我不熟悉WCF,我可以在同一台计算机上测试这两个部分(只是让它们在127.0.0.1地址监听)。
现在,当我尝试从客户端发送一些“自定义”可序列化对象时,我在服务器端获得了“OutOfMemory”异常!我读了关于套接字,发送/接收对象的方法,我已经尝试了一些我在网上找到但没有运气的代码!我不知道我的代码有什么问题。 这是我的代码:
这是双方代码中定义的测试类:
[Serializable]
class MyClass
{
public string msg="default";
}
客户端发送代码(工作正常):
private void cmdSendData_Click(object sender, System.EventArgs e)
{
try
{
MyClass test = new MyClass();
NetworkStream ns = new NetworkStream(m_socWorker); //m_socWorker is socket
BinaryWriter bw = new BinaryWriter(ns);
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, test);
bw.Write(ms.ToArray());
MessageBox.Show("Length is: " + ms.ToArray().Length); //length is 152!
ns.Close();
}
catch(System.Net.Sockets.SocketException se)
{
MessageBox.Show (se.Message );
}
}
服务器端代码(导致问题的代码):
public void OnDataReceived(IAsyncResult asyn)
{
try
{
CSocketPacket theSockId = (CSocketPacket)asyn.AsyncState ;
NetworkStream ns = new NetworkStream(m_socWorker);
byte[] buffer = new byte[1024];
ns.Read(buffer, 0, buffer.Length);
BinaryFormatter bin = new BinaryFormatter();
MemoryStream mem = new MemoryStream(buffer.Length);
mem.Write(buffer, 0, buffer.Length);
mem.Seek(0, SeekOrigin.Begin);
MyClass tst = (MyClass)bin.Deserialize(mem); //ERROR IS THROWN HERE!
MessageBox.Show(tst.msg);
theSockId.thisSocket.EndReceive(asyn);
WaitForData(m_socWorker);
}
catch (ObjectDisposedException )
{
System.Diagnostics.Debugger.Log(0,"1","\nOnDataReceived: Socket has been closed\n");
}
catch(SocketException se)
{
MessageBox.Show (se.Message );
}
}
所以,当我尝试反序列化时,我得到了例外。不知道出了什么问题。
我已经威胁我的代码“如果你继续造成问题我会向StackOverflow报告你们”所以我在这里:)
答案 0 :(得分:1)
那里有一些奇怪的代码:
IMO有你的错误;您应该从流中读取正确的字节数(通常在循环中)。通常,您将循环,检查Read的返回值,直到 我们已经读取了我们想要的数据量,或我们得到EOF(返回< = 0)
或更好;使用为您执行此操作的序列化程序...例如,protobuf-net具有SerializeWithLengthPrefix和DeserializeWithLengthPrefix,可以为您处理所有长度问题。
因为你提到“自定义”序列化 - 如果你正在实现ISerializable,那么问题也可能在那里 - 但是如果没有代码我们就看不到它。此外,当前的缓冲区/流是如此破碎(抱歉,但确实如此),我怀疑它是否已经走得那么远。
答案 1 :(得分:1)
首先,虽然我不确定这是否是您问题的直接原因,但您的阅读逻辑存在严重问题。
MemoryStream
。而不是这样做,只需要采用缓冲区的MemoryStream
构造函数的重载。你不会那样复制数据。EndReceive
;虽然这实际上可能不会导致错误,但它不符合Begin
/ End
旧式异步模式的精神。您应该在回调开始时致电EndXXX
以获得结果。我意识到这不是您问题的直接答案,但您真的需要重新考虑您不使用WCF的决定。
几个月前,我和你在同一条船上;我之前没有使用过WCF,我也没有费心去看看它是如何工作的。对我来说这是一个非常大的黑盒子,我在其他平台上做过基于套接字的通信,所以这是一个众所周知的数量。回顾过去,我选择投入WCF是我能做出的最好决定。一旦你掌握了一些概念(绑定,合同以及如何使用各种属性),服务的开发就很简单,你不必花时间编写管道。 NetTcpBinding
提供了一个基于TCP的绑定,可以支持长期连接和会话(这就是我使用它的方式),甚至可以处理保持活动的消息,通过Reliable保持连接打开会话。这就像打开旗帜一样简单。如果您需要更多可互操作的东西(意味着跨平台),您可以编写自己的绑定来执行此操作并保持代码原样。
查看一些TCP WCF示例;启动并运行起来不会花费太长时间,一旦达到这一点,修改就像在界面中添加功能一样简单,然后在服务类上添加相应的功能。