发送许多Protobuf消息时NetworkStream关闭

时间:2013-10-18 11:09:54

标签: c# .net protobuf-net networkstream

我使用protobuf-net和.NET的TCPClient& NetworkStream用于一台服务器和多台客户机之间的通信。对于发送消息,我在双方都使用以下方法:

   public static bool WriteProtocolBufferToStream(System.IO.Stream stream, object protoBufObject)
    {            
            // ... check parameters ...                
            // ... Determine the 'fieldNumber' of the 'protoBufObject' via a helper dictionary ...  
            if (fieldNumber > -1)
            {
                try { Serializer.NonGeneric.SerializeWithLengthPrefix(stream, protoBufObject, ProtoBuf.PrefixStyle.Base128, fieldNumber); }
                catch (Exception ex)
                {
                    Logger.Instance.Error("Exception: " + ex.Message);
                    return false;
                }
            }
            else
            {
                Logger.Instance.Error("unknown message type");
                return false;
            }
            return true;            
    }

在只有一些客户端和少量消息的小场景中,一切都很好。但是在有大约40个客户端和许多交换消息的场景中我遇到了问题。消息非常小(包含1到5个小字符串)但服务器可能同时发送多个(最多200个)这些消息。

在一段时间(几分钟到几个小时)之后抛出以下异常:

ArgumentException: Cannot write to stream. Parameter name: dest

源是protobuf-net的ProtoWriter类构造函数。它会抛出此异常,因为CanWrite dest NetworkStream属性为false。我的问题是:为什么CanWrite在一段时间后会从true变为false?它是否与溢出缓冲区有关(因为我同时发送了许多消息)?我怎么能解决它?

修改

正如@ [Marc Gravell]已经指出的那样,NetworkStream已被处理,因此将CanWrite从true更改为false。例如,如果我尝试访问 stream 对象的WriteTimeout属性,我会得到这个:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
    at System.Net.Sockets.Socket.GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName)
    at System.Net.Sockets.NetworkStream.get_WriteTimeout()
    at Utilities.CommunicationHelper.WriteProtocolBufferToStream(NetworkStream stream, Object protoBufObject)
    ...

我仍在寻找代码中的某些内容,这可能导致Socket的处理。在一段时间(几个小时)之后还有什么可能导致Socket处理?

2 个答案:

答案 0 :(得分:2)

对于NetworkStream,一点IL分析显示CanWrite推迟到m_Writeable。反过来,m_Writeable会以三种方式变为false

  • 何时处理
  • 使用access参数FileAccess.Read
  • 创建时
  • 分配Writeable属性(protected)时(我看不到框架中实际使用此属性的证据,请注意)

所以:如果你说这种情况发生在一段时间已经存在的已经建立的NetworkStream上,那么最可能的答案就是它在某个时候被处理掉了,可能是因为被关闭了

答案 1 :(得分:0)

也许您正在耗尽服务器的TCP / IP端口?

您没有提到您的服务器正在运行哪个操作系统,但假设它是2008服务器,您有appx 16000个可用端口(49152-65535)。关闭连接时,端口将保持TIME_WAIT状态4分钟,然后再次可用。这意味着如果您在不到4分钟内有超过16000个连接,您的服务器将开始拒绝连接。

遇到错误时,请尝试在服务器上运行此命令:

netstat -p TCP -ano > netstat.txt

netstat.txt中的行数告诉您已建立或time_wait状态的TCPv4连接数。

你可以调整它。通过增加动态端口数量,减少time_wait间隔或两者。

查看您的实际值:

  • Time_wait(TcpTimedWaitDelay)(默认值为240,如果未找到该值,则为4分钟)

    reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\TcpTimedWaitDelay
    
  • 动态端口范围:

    netsh int ipv4 show dynamicportrange tcp
    

一些有用的资源: