关于c#:NetworkStream.Write立即返回-如何知道何时完成发送数据?

关于c#:NetworkStream.Write立即返回-如何知道何时完成发送数据?

NetworkStream.Write returns immediately - how can I tell when it has finished sending data?

尽管有文档,但NetworkStream.Write似乎不会等到发送数据后再进行。相反,它将等待直到将数据复制到缓冲区中然后返回。该缓冲区在后台传输。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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服务器),但是我不想永远等待我的数据格式错误并且响应永远不会到来,这就是为什么我需要知道是否m等待数据发送,或者我正在等待服务器响应。

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


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

数据不会完全"离开"您的网卡。考虑程序与网络关系的最佳方法是:

your program -> lots of confusing stuff -> the peer program

在"很多令人困惑的东西"中可能出现的事情的列表:

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

因此,如果您所在的虚拟机位于不同的操作系统下,则该虚拟机具有控制该虚拟机网络行为的软件防火墙-何时"真正"离开您的网卡?即使在最佳情况下,这些组件中的许多组件也可能会丢弃一个数据包,您的网卡将需要重新传输该数据包。第一次(不成功)尝试后,它是否"遗失"了您的网卡?大多数网络API都会拒绝,直到另一端发送TCP确认后才"发送"。

就是说,NetworkStream.Write的文档似乎表明,直到至少启动了"发送"操作,它才会返回:

The Write method blocks until the requested number of bytes is sent or a SocketException is thrown.

当然,由于我上面给出的原因,"发送"有点模糊。也有可能数据将由您的程序"真正"发送并由对等程序接收,但是对等方将崩溃或不进行实际处理。因此,您应该对消息进行Write后跟Read,该消息仅在对等方实际处理该消息时才会发出。


TCP是一种"可靠"的协议,这意味着如果没有套接字错误,则将在另一端接收数据。我已经看到了很多尝试以更高级别的应用程序确认来猜测TCP,但是恕我直言,这通常是浪费时间和带宽。

通常,您描述的问题是通过正常的客户端/服务器设计来解决的,其最简单的形式就是这样...

客户端向服务器发送请求,并在套接字上进行阻塞读取,以等待某种响应。如果TCP连接有问题,则读取将中止。客户端还应该使用超时来检测服务器的任何与网络无关的问题。如果请求失败或超时,则客户端可以重试,报告错误等。

服务器处理完请求并发送响应后,它通常不再关心发生的事情-即使套接字在事务处理过程中消失了-因为由客户端来决定是否进行进一步的交互。就个人而言,我觉得成为服务器感到非常安慰。 :-)


通常,我建议无论如何都要从客户端发送确认。这样,您可以100%确保已正确接收数据。


我试图了解.NET NetworkStream设计器的意图,他们必须以这种方式进行设计。写入后,.NET不再处理要发送的数据。因此,合理的做法是立即返回Write(并且不久后将从NIC发送数据)。

因此,在您的应用程序设计中,除了尝试使其按自己的方式工作外,您还应遵循此模式。例如,在从NetworkStream接收任何数据之前可以使用更长的超时时间,以补偿命令离开NIC之前所花费的时间。

总之,在源文件中硬编码一个超时值是一种不好的做法。如果超时值是可在运行时配置的,那么一切应该正常。


我无法想到NetworkStream.Write不会尽快将数据发送到服务器的情况。除非出现严重的网络拥塞或断开连接,否则它应在合理的时间内最终到达另一端。您是否有协议问题?例如,使用HTTP时,请求标头必须以空行结尾,并且服务器将不会发送任何响应,直到响应发生为止-使用的协议是否具有类似的消息结束特性?

这是比原始版本更干净的代码,删除了委托,字段和Thread.Sleep。它在功能上完全相同。

1
2
3
4
5
6
7
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()可能会帮助您解决超时问题。可以传递一个超时参数。这是一个懒惰的等待-在结果完成或超时到期之前,不会再次调度线程。


如果我不得不猜测,一旦将缓冲区交给Windows Socket,NetworkStream就会认为数据已发送。因此,我不确定是否可以通过TcpClient完成所需的方法。


Bellow .net是使用TCP的Windows套接字。
TCP使用ACK数据包通知发送方数据已成功传输。
因此,发送方计算机知道何时传输数据,但无法(据我所知)在.net中获取该信息。

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


如何使用Flush()方法。

1
ns.Flush()

那应该确保在继续操作之前已写入数据。


也许尝试设置
tcp.NoDelay = true


推荐阅读

    linux怎么返回命令?

    linux怎么返回命令?,暂停,电脑,系统,一致,状态,密码,地址,服务,命令,空格,在L

    linux命令chm文档?

    linux命令chm文档?,电脑,系统,文件,首页,百度,软件,电机,管理,产品,设备,谁知

    数据库导出linux命令?

    数据库导出linux命令?,密码,数据,数据库,情况,地址,系统,工具,网上,名字,命

    linux查看命令返回值?

    linux查看命令返回值?,状态,数据,系统,命令,工具,异常,工作,软件,数字,环境,l

    linux中编辑文档命令?

    linux中编辑文档命令?,状态,工作,命令,电脑,信息,第一,系统,编辑,终端,文件,L

    linux命令与数据流?

    linux命令与数据流?,工作,地址,系统,信息,命令,目录,标准,网络,管理,常用命

    linux保存返回命令?

    linux保存返回命令?,档案,单位,时间,命令,权威,编辑,文件,音乐,内容,模式,mv

    linux终端返回命令行?

    linux终端返回命令行?,密码,状态,平台,系统,电脑,环境,认证,地址,终端,命令,

    linux恢复数据库命令?

    linux恢复数据库命令?,工具,系统,软件,数据,盘中,密码,命令,备份,数据库,文

    linux储存命令数据?

    linux储存命令数据?,系统,工作,地址,信息,标准,命令,工具,实时,数据,分析,lin

    linux储存命令数据?

    linux储存命令数据?,系统,工作,地址,信息,标准,命令,工具,实时,数据,分析,lin

    linux终端返回命令行?

    linux终端返回命令行?,密码,状态,平台,系统,电脑,环境,认证,地址,终端,命令,

    linux保存返回命令?

    linux保存返回命令?,档案,单位,时间,命令,权威,编辑,文件,音乐,内容,模式,mv

    linux命令大全数据库?

    linux命令大全数据库?,服务,系统,平台,状态,软件,通用,环境,数据,神州,地址,

    linux上数据库的命令?

    linux上数据库的命令?,服务,系统,信息,地址,命令,密码,工具,管理,数据,单位,

    linux命令dm数据库?

    linux命令dm数据库?,地址,软件,时间,设备,名字,服务,位置,名称,公司,命令,lin

    linux常用命令文档?

    linux常用命令文档?,工作,系统,地址,管理,网络,命令,信息,目录,操作,文件,lin

    linux命令行返回桌面?

    linux命令行返回桌面?,系统,密码,状态,暂停,环境,终端,信息,命令,界面,字符,l

    linux的vi返回命令?

    linux的vi返回命令?,状态,电脑,档案,编辑,命令,文件,模式,键盘,冒号,内容,Lin

    linux存储数据命令?

    linux存储数据命令?,系统,管理,数据,设备,情况,地址,工作,命令,服务,平台,Lin