我使用 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类构造函数。它抛出此异常是因为 NetworkStream
dest 的 CanWrite
属性为 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)
...
我仍在我的代码中寻找可能导致套接字被丢弃的内容。还有什么可能导致套接字在一段时间(几个小时)后被处理?
最佳答案
对于 NetworkStream
,一点 IL 分析表明 CanWrite
遵循 m_Writeable
。反过来,m_Writeable
通过三种方式变为 false
:
- 何时处置
- 当使用
access
参数作为FileAccess.Read
创建时 - 当分配
Writeable
属性(protected
)时(注意,我看不到框架中实际使用该属性的证据)
所以:如果您说这种情况发生在已经建立的 NetworkStream 上,并且已经运行了一段时间,那么最可能的答案是它在某个时候被处置,可能是由于被关闭
关于c# - 发送大量 Protobuf 消息时 NetworkStream 关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19448237/