【问题标题】:NetworkStream doesn't always send data unless I Thread.Sleep() before closing the NetworkStream. What am I doing wrong?NetworkStream 并不总是发送数据,除非我在关闭 NetworkStream 之前使用 Thread.Sleep()。我究竟做错了什么?
【发布时间】:2012-11-05 23:11:39
【问题描述】:

我在将一小段数据(日期)发送到同一台计算机上运行的另一个 TcpClient 时遇到问题。

在下面的代码中,您将看到它在关闭 NetworkStream 之前处于休眠状态。如果我删除了Sleep,它会导致数据在另一端没有出现的间歇性问题。

我在这里做错了吗?

using (TcpClient client = new TcpClient())
{
    client.Connect(new IPEndPoint(IPAddress.Loopback, _tcpServerPort));
    NetworkStream clientStream = client.GetStream();

    byte[] buffer = new ASCIIEncoding().GetBytes(theDateObject.ToString("yyyy-MM-dd HH:mm:ss"));                            
    clientStream.Write(buffer, 0, buffer.Length);
    clientStream.Flush();
    System.Threading.Thread.Sleep(1000); // Remove this line and the data may not arrive at the other end
    clientStream.Close();
}

其他一些信息:

  • 在某些计算机上,您可以删除 Sleep 而不会造成问题(可能是速度较慢的计算机?)
  • 我尝试使用 Close(int timeout) 关闭 NetworkStream(而不是休眠),但这没有帮助。
  • 1000 毫秒的值是任意的 - 我怀疑其他值也可以。问题是:为什么首先需要它?

【问题讨论】:

  • 只是为了检查 - 您是否期望所有数据都将通过对另一端的 Read 的一次调用到达?因为这不是 TCP 套接字的保证。
  • 是的,我想我是。但是我相信,如果在第一次读取时没有收到完整的消息(因为它会立即尝试 DateTime.Parse() 消息),另一端的代码会出错。我没有看到另一端抛出任何异常。
  • @shambulator - 不,即使您正在使用流,它也是相关的。如果您使用它,而不是更高级别的抽象(例如 WCF、Web 服务等),那么您有责任自己重建消息。请注意NetworkStream.Read 的文档:“Read 操作读取尽可能多的数据,最多为 size 参数指定的字节数”
  • @Damien_The_Unbeliever 抱歉,我误读了您关于写入所有可用数据的第一条评论,这就是为什么我匆忙删除了我的:P
  • 我认为这不太可能是问题是否正确?鉴于我尝试 DateTime.Parse() 从我的第一个(也是唯一一个)NetworkStream.Read() 收到的内容?

标签: c# tcpclient


【解决方案1】:

在 NetworkStream 上使用超时调用 close 意味着网络流将在后台保持打开 X 时间,以允许发送剩余的数据。在后台需要注意,因为您的代码不会被此阻止。因为您将 tcpClient 封装在 using 语句中,所以客户端和内部 NetworkStream 将在您调用关闭后直接释放。在这种情况下,睡眠是有效的,因为它允许发送数据并在此期间阻止您的方法,以防止它强制释放其资源。

更简洁的解决方案是在 TcpClient 上设置一个活动的 LingerState,并具有足够长的超时时间。见:http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.lingerstate.aspx

【讨论】:

  • 你说的有道理。但如果是这样的话,那么你怎么知道什么时候关闭 TcpClient 是安全的呢?编辑:看到你的编辑链接到 LingerState。看起来这可能是一个解决方案,我一定会尝试的。
  • 不幸的是,这似乎没有奏效。我的一位用户仍然遇到数据无法到达另一端的间歇性问题。
【解决方案2】:

如果您的接收队列不为空(您从服务器的接收缓冲区中获得了尚未读取的数据,处理 Socket(客户端)发送 TCP RST(重置连接))和发送中的数据缓冲区被丢弃,即如果尚未发送丢失,否则它会等待发送缓冲区为空,然后终止 tcp 连接。

一种解决方法是关闭发送通道:

    client.Client.Shutdown(SocketShutdown.Send)
    clientStream.Close();
}

在关闭客户端之前(而不是 thread.sleep),在这种情况下连接不会被重置。

.Flush() 也对网络流不执行任何操作。

见:http://cs.baylor.edu/~donahoo/practical/JavaSockets/RecvQClose.pdf

【讨论】:

    猜你喜欢
    • 2013-08-06
    • 1970-01-01
    • 2014-09-13
    • 2014-05-14
    • 2016-07-18
    • 2014-08-20
    • 2016-01-29
    • 2019-12-23
    • 2022-08-18
    相关资源
    最近更新 更多