套接字和OutOfMemory错误的问题

时间:2011-07-02 18:06:28

标签: c# sockets networking

我有一个很大的问题。试图创建一个必须包含两部分的应用程序:服务器端和客户端。这两个部分必须以某种方式进行交流并交换对象。我决定使用套接字,因为我不熟悉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报告你们”所以我在这里:)

2 个答案:

答案 0 :(得分:1)

那里有一些奇怪的代码:

  • 假设我们在不检查
  • 的情况下读取1024个字节
  • 复制1024缓冲区
  • 假设序列化数据为1024字节,不多于
  • 反序列化

IMO有你的错误;您应该从流中读取正确的字节数(通常在循环中)。通常,您将循环,检查Read的返回值,直到 我们已经读取了我们想要的数据量,我们得到EOF(返回< = 0)

或更好;使用为您执行此操作的序列化程序...例如,protobuf-net具有Seri​​alizeWithLengthPrefix和DeserializeWithLengthPrefix,可以为您处理所有长度问题。

因为你提到“自定义”序列化 - 如果你正在实现ISerializable,那么问题也可能在那里 - 但是如果没有代码我们就看不到它。此外,当前的缓冲区/流是如此破碎(抱歉,但确实如此),我怀疑它是否已经走得那么远。

答案 1 :(得分:1)

首先,虽然我不确定这是否是您问题的直接原因,但您的阅读逻辑存在严重问题。

  1. 你创建一个1024字节的缓冲区并读入它,而不检查实际读取了多少;如果传入缓冲区只有56个字节,那么你只能读取56个字节(除非你在套接字本身上使用阻塞读取,这可能会超时)。同样,你的缓冲区中可能有2000个字节,这意味着你在缓冲区中剩下976个字节,直到你收到更多数据才会得到处理。这可能是一个完整的对象,客户端可能会在发送之前等待对它的响应。
  2. 然后,您将该缓冲区再次复制到MemoryStream。而不是这样做,只需要采用缓冲区的MemoryStream构造函数的重载。你不会那样复制数据。
  3. 在处理完传入数据后,您致电EndReceive ;虽然这实际上可能不会导致错误,但它不符合Begin / End旧式异步模式的精神。您应该在回调开始时致电EndXXX以获得结果。
  4. 我意识到这不是您问题的直接答案,但您真的需要重新考虑您不使用WCF的决定。

    几个月前,我和你在同一条船上;我之前没有使用过WCF,我也没有费心去看看它是如何工作的。对我来说这是一个非常大的黑盒子,我在其他平台上做过基于套接字的通信,所以这是一个众所周知的数量。回顾过去,我选择投入WCF是我能做出的最好决定。一旦你掌握了一些概念(绑定,合同以及如何使用各种属性),服务的开发就很简单,你不必花时间编写管道。

    NetTcpBinding提供了一个基于TCP的绑定,可以支持长期连接和会话(这就是我使用它的方式),甚至可以处理保持活动的消息,通过Reliable保持连接打开会话。这就像打开旗帜一样简单。如果您需要更多可互操作的东西(意味着跨平台),您可以编写自己的绑定来执行此操作并保持代码原样。

    查看一些TCP WCF示例;启动并运行起来不会花费太长时间,一旦达到这一点,修改就像在界面中添加功能一样简单,然后在服务类上添加相应的功能。