我使用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处理?
答案 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
一些有用的资源: