尽管有文档,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 :(得分:11)
我不是C#程序员,但你提出这个问题的方式有点误导。对于“已接收”的任何有用定义,知道何时“接收”数据的唯一方法是在协议中有一条特定的确认消息,表明数据已经完全处理。
数据不会完全“离开”您的网卡。考虑您的计划与网络的关系的最佳方式是:
您的计划 - >很多令人困惑的东西 - >同行计划
可能出现在“很多令人困惑的东西”中的事情清单:
因此,如果您使用的是托管在不同操作系统下的虚拟机,该虚拟机具有控制虚拟机网络行为的软件防火墙 - 何时数据“真的”离开了您的网卡?即使在最好的情况下,许多这些组件也可能丢弃一个数据包,您的网卡需要重新传输。第一次(不成功)尝试时,它是否“离开”了您的网卡?大多数网络API都会说不,在另一端发送TCP确认之前,它还没有被“发送”。
那就是说,the documentation for NetworkStream.Write似乎表明它至少在发起'发送'操作之后才会返回:
Write方法将一直阻塞,直到发送请求的字节数或抛出SocketException为止。
当然,由于我上面提到的原因,“发送”有些模糊。还有可能数据将由您的程序“真正”发送并由对等程序接收,但是对等体将崩溃或者实际上不会实际处理数据。因此,您应该执行Write
后跟Read
消息,该消息只有在您的对等方实际处理完消息时才会发出。
答案 1 :(得分:5)
TCP是一种“可靠”协议,这意味着如果没有套接字错误,将在另一端接收数据。我已经看到许多努力在二次猜测TCP上使用更高级别的应用程序确认,但恕我直言,这通常是浪费时间和带宽。
通常,您描述的问题是通过普通的客户端/服务器设计来处理的,最简单的形式就是这样......
客户端向服务器发送请求,并在套接字上执行阻塞读取,等待某种响应。如果TCP连接出现问题,则该读取将中止。客户端还应使用超时来检测服务器的任何非网络相关问题。如果请求失败或超时,则客户端可以重试,报告错误等
一旦服务器处理了请求并发送了响应,它通常不再关心发生了什么 - 即使套接字在事务期间消失 - 因为由客户端来启动任何进一步的交互。就个人而言,我觉得成为服务器非常令人欣慰。 : - )
答案 2 :(得分:2)
一般来说,无论如何,我建议客户发送确认。这样,您就可以100%确定收到的数据并正确接收。
答案 3 :(得分:1)
如果我不得不猜测,NetworkStream会在将缓冲区移到Windows Socket后将数据视为已发送。所以,我不确定是否有办法通过TcpClient完成你想要的东西。
答案 4 :(得分:1)
我想不出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()可以帮助您解决超时问题。它可以传递一个超时参数。这是一个懒惰的等待 - 在结果完成或超时到期之前不会再次安排线程。
答案 5 :(得分:1)
我试图理解.NET NetworkStream设计者的意图,他们必须以这种方式设计它。写入后,.NET不再处理要发送的数据。因此,Write立即返回是合理的(并且数据将很快从NIC发出)。
因此,在您的应用程序设计中,您应该遵循此模式,而不是尝试使其按照您的方式工作。例如,在收到NetworkStream中的任何数据之前使用较长的时间可以补偿命令离开NIC之前所消耗的时间。
总之,在源文件中硬编码超时值是不好的做法。如果超时值在运行时是可配置的,那么一切都应该可以正常工作。
答案 6 :(得分:0)
如何使用Flush()方法。
ns.Flush()
这应确保在继续之前写入数据。
答案 7 :(得分:0)
Bellow .net是使用TCP的Windows套接字。 TCP使用ACK数据包通知发送方数据已成功传输。 因此,发件人机器知道数据何时被传输,但我无法(我知道)在.net中获取该信息。
编辑: 只是一个想法,从未尝试过: 仅当套接字缓冲区已满时,才写入()块。因此,如果我们将缓冲区大小(SendBufferSize)降低到一个非常低的值(8?1?0?),我们可能得到我们想要的东西:)
答案 8 :(得分:-1)
也许尝试设置
tcp.NoDelay = true