首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TcpClient读OutOfMemoryException

TcpClient读OutOfMemoryException
EN

Stack Overflow用户
提问于 2012-01-21 15:22:13
回答 1查看 1.7K关注 0票数 1

我有间歇性OutOfMemoryException的问题,在网上

缓冲器=新的bytemetaDataSize;

(在//下面读取命令的元数据。)

这是否意味着,我试着阅读完整的信息,而它的一部分已经收到?以防万一,什么是解决这一问题的可靠方法?顺便说一下,我需要可变长度的消息,因为大多数消息都很短,而偶尔的消息非常大。我应该在邮件前面附加完整的邮件大小吗?但是,在尝试读取流之前,我如何知道流包含了多少内容呢?(就像我目前所做的那样,在尝试读取特定长度时,读取有时会失败)

代码语言:javascript
复制
    public static Command Read(NetworkStream ns)
    {
        try
        {
                //Read the command's Type.
                byte[] buffer = new byte[4];
                int readBytes = ns.Read(buffer, 0, 4);
                if (readBytes == 0)
                    return null;
                CommandType cmdType = (CommandType)(BitConverter.ToInt32(buffer, 0));

                //Read cmdID
                buffer = new byte[4];
                readBytes = ns.Read(buffer, 0, 4);
                if (readBytes == 0)
                    return null;
                int cmdID = BitConverter.ToInt32(buffer, 0);

                //Read MetaDataType
                buffer = new byte[4];
                readBytes = ns.Read(buffer, 0, 4);
                if (readBytes == 0)
                    return null;
                var metaType = (MetaTypeEnum)(BitConverter.ToInt32(buffer, 0));

                //Read the command's MetaData size.
                buffer = new byte[4];
                readBytes = ns.Read(buffer, 0, 4);
                if (readBytes == 0)
                    return null;
                int metaDataSize = BitConverter.ToInt32(buffer, 0);

                //Read the command's Meta data.
                object cmdMetaData = null;
                if (metaDataSize > 0)
                {
                    buffer = new byte[metaDataSize];

                    int read = 0, offset = 0, toRead = metaDataSize;
                    //While 
                    while (toRead > 0 && (read = ns.Read(buffer, offset, toRead)) > 0)
                    {
                        toRead -= read;
                        offset += read;
                    }
                    if (toRead > 0) throw new EndOfStreamException();

                    // readBytes = ns.Read(buffer, 0, metaDataSize);
                    //if (readBytes == 0)
                    //    return null;
                    // readBytes should be metaDataSize, should we check? 

                    BinaryFormatter bf = new BinaryFormatter();
                    MemoryStream ms = new MemoryStream(buffer);
                    ms.Position = 0;
                    cmdMetaData = bf.Deserialize(ms);
                    ms.Close();
                }
                //Build and return Command
                Command cmd = new Command(cmdType, cmdID, metaType, cmdMetaData);

                return cmd;
        }
        catch (Exception)
        {

            throw;
        }

    }

书写方法:

代码语言:javascript
复制
    public static void Write(NetworkStream ns, Command cmd)
    {
        try
        { 

            if (!ns.CanWrite)
                return;

            //Type [4]
            // Type is an enum, of fixed 4 byte length. So we can just write it.
            byte[] buffer = new byte[4];
            buffer = BitConverter.GetBytes((int)cmd.CommandType);
            ns.Write(buffer, 0, 4);
            ns.Flush();

            // Write CmdID, fixed length [4]
            buffer = new byte[4];                    // using same buffer
            buffer = BitConverter.GetBytes(cmd.CmdID);
            ns.Write(buffer, 0, 4);
            ns.Flush();

            //MetaDataType [4]
            buffer = new byte[4];
            buffer = BitConverter.GetBytes((int)cmd.MetaDataType);
            ns.Write(buffer, 0, 4);
            ns.Flush();

            //MetaData (object) [4,len]
            if (cmd.MetaData != null)
            {
                BinaryFormatter bf = new BinaryFormatter();
                MemoryStream ms = new MemoryStream();
                bf.Serialize(ms, cmd.MetaData);

                ms.Seek(0, SeekOrigin.Begin);

                byte[] metaBuffer = ms.ToArray();
                ms.Close();

                buffer = new byte[4];
                buffer = BitConverter.GetBytes(metaBuffer.Length);
                ns.Write(buffer, 0, 4);
                ns.Flush();

                ns.Write(metaBuffer, 0, metaBuffer.Length);
                ns.Flush();

                if (cmd.MetaDataType != MetaTypeEnum.s_Tick)
                    Console.WriteLine(cmd.MetaDataType.ToString() + " Meta: " + metaBuffer.Length);
            }
            else
            {
                //Write 0 length MetaDataSize
                buffer = new byte[4];
                buffer = BitConverter.GetBytes(0);
                ns.Write(buffer, 0, 4);
                ns.Flush();
            }

        }
        catch (Exception)
        {

            throw;
        }
    }

VB.NET:

代码语言:javascript
复制
Private tcp As New TcpClient 
Private messenger As InMessenger    
Private ns As NetworkStream 

Public Sub New(ByVal messenger As InMessenger)
    Me.messenger = messenger
End Sub

Public Sub Connect(ByVal ip As String, ByVal port As Integer)

    Try
        tcp = New TcpClient


        Debug.Print("Connecting to " & ip & " " & port)

        'Connect with a 5sec timeout
        Dim res = tcp.BeginConnect(ip, port, Nothing, Nothing)
        Dim success = res.AsyncWaitHandle.WaitOne(5000, True)

        If Not success Then
            tcp.Close()

        Else
            If tcp.Connected Then
                ns = New NetworkStream(tcp.Client)

                Dim bw As New System.ComponentModel.BackgroundWorker
                AddHandler bw.DoWork, AddressOf DoRead
                bw.RunWorkerAsync()

            End If
        End If


    Catch ex As Exception
        Trac.Exception("Connection Attempt Exception", ex.ToString)
        CloseConnection()
    End Try
End Sub


Private Sub DoRead()

    Try
        While Me.tcp.Connected

            ' read continuously : 
            Dim cmd = CommandCoder.Read(ns)

            If cmd IsNot Nothing Then
                HandleCommand(cmd)
            Else
                Trac.TraceError("Socket.DoRead", "cmd is Nothing")
                CloseConnection()
                Exit While
            End If

            If tcp.Client Is Nothing Then
                Trac.TraceError("Socket.DoRead", "tcp.client = nothing")
                Exit While
            End If
        End While
    Catch ex As Exception
        Trac.Exception("Socket.DoRead Exception", ex.ToString())
        CloseConnection()
        EventBus.RaiseErrorDisconnect()
    End Try

End Sub

编辑:

我放了一些WriteLine的,发现在接收端,有些包裹的大小是错误的。因此,对于某个消息,应该为9544的metaDataSize被读取为5439488,或类似的不正确值。我想,在很少的情况下,这个数字是如此之大,以至于导致了OutOfMemoryException。

看来道格拉斯的答案可能在(?)上,我会测试的。有关信息:服务器(发送者)程序构建为“任意CPU",运行在Windows7 x64 pc上。当客户端(接收方)构建为x86时,并且(在此测试期间)运行在XP上。但也必须对其进行编码,以便在其他windows x86或x64上工作。

EN

回答 1

Stack Overflow用户

发布于 2012-01-21 16:26:06

您说的是数据包,但这不是TCP公开的概念。TCP公开字节流,仅此而已。它不在乎有多少Send调用。它可以将一个Send调用拆分为多个读取,并合并多个发送或混合这些。

Read的返回值告诉您读取了多少字节。如果此值大于0,但小于传递给Read的长度,则得到的字节较少,则传递它。您的代码假定读取了0length字节。这是一个无效的假设。

您的代码也存在endian问题,但我认为您的两个系统都是小endian,所以这不太可能导致您当前的问题。

如果您不关心阻塞(您现有的代码已经在循环中阻塞了,所以这不是额外的问题),那么您可以在流上使用BinaryReader

它有像ReadInt32这样的助手方法,可以自动处理部分读取,并且使用固定的endianness(总是很少)。

代码语言:javascript
复制
buffer = new byte[4];
readBytes = ns.Read(buffer, 0, 4);
if (readBytes == 0)
    return null;
int cmdID = BitConverter.ToInt32(buffer, 0);

变成:

代码语言:javascript
复制
int cmdId = reader.ReadInt32();

如果它意外地遇到流的末尾,它将抛出一个EndOfStreamException,而不是返回null

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8954115

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档