2

我使用 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类构造函数。它抛出这个异常是因为destCanWrite的属性是假的。我的问题是:为什么一段时间后从真变为假?是否与缓冲区溢出有关(因为我同时发送了许多消息)?我该如何解决?NetworkStream CanWrite

编辑:

正如@[Marc Gravell] 已经指出的那样,NetworkStream已处置,因此CanWrite从真变为假。例如,如果我尝试访问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 被丢弃?

4

2 回答 2

3

对于NetworkStream,一点IL分析表明,CanWrite遵从m_Writeable。反过来,m_Writeable变成false三种方式:

  • 当它被丢弃时
  • 当它使用access参数创建时FileAccess.Read
  • Writeable分配属性 ( )时protected(我看不到该属性实际在框架中使用的证据,请注意)

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

于 2013-10-21T08:47:26.373 回答
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
    

一些有用的资源:

于 2013-10-18T11:58:06.653 回答