NetworkStream.Write立即返回 - 如何知道它何时完成发送数据?

问题描述:

尽管有文档,NetworkStream.Write似乎没有等到数据发送完毕。而是等到数据被复制到缓冲区然后返回。该缓冲区在后台传输。NetworkStream.Write立即返回 - 如何知道它何时完成发送数据?

这是我现在的代码。无论我使用ns.Write还是ns.BeginWrite都无关紧要 - 都会立即返回。 EndWrite也立即返回(这是有道理的,因为它正在写入发送缓冲区,而不是写入网络)。

bool done; 
    void SendData(TcpClient tcp, byte[] data) 
    { 
     NetworkStream ns = tcp.GetStream(); 
     done = false; 
     ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns); 
     while (done == false) Thread.Sleep(10); 
    } 
      
    public void myWriteCallBack(IAsyncResult ar) 
    { 
     NetworkStream ns = (NetworkStream)ar.AsyncState; 
     ns.EndWrite(ar); 
     done = true; 
    } 

如何判断数据实际发送到客户端的时间?

我想在发送数据后等待10秒(例如)来自服务器的响应,否则我会认为出现了问题。如果发送我的数据需要15​​秒,那么它总是会超时,因为我只能从NetworkStream.Write返回时开始计数 - 这是数据发送之前的时间。我想从数据离开网卡时开始计数10秒。

数据量和发送时间可能会有所不同 - 发送它可能需要1秒,发送它可能需要10秒,发送它可能需要一分钟。服务器在收到数据(它是一个smtp服务器)时确实会发送一个响应,但是如果我的数据格式错误并且响应永远不会到来,我不会永远等待,这就是为什么我需要知道,正在等待数据发送,或者如果我在等待服务器响应。

我可能想向用户显示状态 - 我想显示“发送数据到服务器”和“等待服务器的响应” - 我怎么能这样做?

+0

您是否尝试过使用100MB数据包的NetworkStream.Write? – Calmarius 2011-04-05 11:57:38

如何使用Flush()方法。

ns.Flush() 

这应该确保在继续之前写入数据。

+1

Flush方法实现Stream.Flush方法;但是,由于NetworkStream未被缓冲,因此不会影响网络流。调用Flush方法不会引发异常。 http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.flush.aspx – GEOCHET 2008-09-15 23:00:33

总的来说,我建议无论如何都要从客户端发送确认。这样,您可以100%确定数据已收到并正确接收。

如果我不得不猜测,NetworkStream认为一旦将缓冲区交给Windows套接字,数据就会被发送。所以,我不确定有什么方法可以通过TcpClient完成你想要的。

+0

确实,CMD/ACK是实现他想要的唯一途径。 – GEOCHET 2008-09-15 23:19:36

也许尝试设置 tcp.NoDelay = true

+0

这告诉它不要使用Nagal。 Nagal是在发送任何数据之前等待约200ms的地方。这个想法是,等一下确保它将发送尽可能多的数据,从而减少网络使用。您仍然不知道数据何时发送。 – 2008-09-16 00:01:27

TCP是一种“可靠的”协议,这意味着将在另一端被接收的数据,如果没有套接字错误。我已经看到无数的努力在猜测TCP与更高级别的应用程序确认,但恕我直言,这通常是浪费时间和带宽。

通常你所描述的问题是通过正常的客户机/服务器设计,其最简单的形式是这样的处理...

客户端发送一个请求到服务器并执行套接字等待读阻塞对某种回应。如果TCP连接出现问题,则读取将中止。客户端还应该使用超时检测服务器的任何非网络相关问题。如果请求失败或超时,则客户端可以重试,报告错误等。

一旦服务器处理完请求并发送响应,它通常不再在意发生了什么 - 即使套接字在事务处理期间消失 - 因为由客户端发起任何进一步的交互。就我个人而言,我觉得成为服务器非常令人欣慰。 :-)

我想不出一个NetworkStream.Write不会尽快发送数据到服务器的场景。除非大规模网络拥塞或断开,否则它应该在合理的时间内到达另一端。有没有可能你有一个协议问题?例如,使用HTTP时,请求标头必须以空行结束,服务器在发生任何响应之前不会发送任何响应 - 正在使用的协议是否具有类似的消息结束特征?

下面是一些比原始版本更清晰的代码,删除委托,字段和Thread.Sleep。它在功能上完全相同的方式。

void SendData(TcpClient tcp, byte[] data) { 
    NetworkStream ns = tcp.GetStream(); 
    // BUG?: should bytWriteBuffer == data? 
    IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null); 
    r.AsyncWaitHandle.WaitOne(); 
    ns.EndWrite(r); 
} 

看起来像这个问题被修改,而我写了上面。 .WaitOne()可能会帮助您解决超时问题。它可以传递一个超时参数。这是一个懒惰的等待 - 在结果结束之前线程不会被再次调度,或者超时到期。

我试图理解.NET NetworkStream设计者的意图,他们必须这样设计它。写入后,要发送的数据不再由.NET处理。因此,写入立即返回是合理的(并且数据将很快从NIC发出)。

所以在你的应用程序设计中,除了试图让它按照你的方式工作外,你应该遵循这种模式。例如,在从NetworkStream接收到任何数据可以补偿命令离开NIC之前所消耗的时间之前,使用较长的超时时间。

总而言之,在源文件中对超时值进行硬编码是一种不好的做法。如果超时值在运行时可配置,则一切都应该正常工作。

我不是C#程序员,但您提出这个问题的方式有点误导。要知道您的数据何时“已收到”,对于“已收到”的任何有用定义,唯一的方法是在协议中具有特定的确认消息,以指示数据已完全处理。

数据完全不会“离开”您的网卡。想你的程序对网络的关系,最好的办法是:

程序 - >很多混乱的东西 - >同行程序

的东西,可能是在“批A名单令人困惑的东西“:

  • 的CLR
  • 操作系统内核
  • 虚拟网络接口
  • 开关
  • 软件防火墙
  • 硬件防火墙
  • 路由器进行网络地址转换
  • 对等的最终执行网络地址转换路由器

所以,如果你是在不同操作系统下托管的虚拟机上,该虚拟机具有控制虚拟机网络行为的软件防火墙 - 数据何时“真正”离开网卡?即使在最好的情况下,这些组件中的很多组件都可能会丢弃一个数据包,而您的网卡需要重新传输。当第一次(不成功)尝试完成后,是否“留下”了网卡?大多数网络API会说不,它没有被“发送”,直到另一端发送了TCP确认。

这就是说,the documentation for NetworkStream.Write似乎表明,它不会返回,直到它已至少发起了“发送”操作:

Write方法阻止,直到被发送请求的字节数或SocketException是抛出。

当然,由于上面给出的原因,“发送”有点模糊。也有可能数据会被你的程序“真正”发送并被对等程序接收,但对等数据将崩溃或以其他方式不实际处理数据。所以你应该做一个Write后跟一个Read的消息,这个消息只会在你的对端实际处理消息时发出。

+0

发送的TCP数据包总是有来自其对端的基础TCP ACK,这就是为什么我们使用TCP vs UDP:我们总是知道连接的状态。回到问题中,如果调用“Write(bytes)”,稍后如果从其他对等端收到TCP ACK,则会弹出通知,这是所有者想要的问题。 – Shawn 2015-02-09 07:45:19

Bellow .net是使用TCP的Windows套接字。 TCP使用ACK数据包通知发件人数据已成功传输。 因此,发件人机器知道数据何时传输,但没有办法(我知道)在.net中获取该信息。

编辑: 只是一个想法,从来没有尝试过: 只有当套接字缓冲区已满,写入()块。因此,如果我们将缓冲区大小(SendBufferSize)降低到非常低的值(8?1?0?),我们可能会得到我们想要的:)