场景是我在家庭网络上通过TCP将数据从一台机器传输到另一台机器。发件人实例化TCPClient
并将数据写入NetworkStream
返回的GetStream()
。我的理解是NetworkStream
中的数据最终被发送到NIC上的缓冲区并通过物理媒体传输。
但是,如果连接中断,MemoryStream
中的数据和NIC缓冲区中的数据将丢失,但在我的应用程序中,数据被写入流中,我可以天真地认为数据被发送到侦听套接字,但显然不是这样。重新建立连接后,应用程序将继续发送数据,据其所知,传输已中断,但这并未考虑NIC缓冲区和MemoryStream
对象中丢失的数据。
除了编写我自己的应用层协议之外,还有什么方法可以解决这个问题吗?
答案 0 :(得分:2)
假设写入套接字的数据将保证到达。这意味着TCP堆栈必须在每个数据包之后等待进行确认。
显然,这不是它的工作方式。
除非您成功关闭连接,否则TCP不保证到达。只有这样,你才知道收到的一切。
您可能需要在应用程序层中建立恢复协议。问另一方你在第一次连接中有多远。
评论中的大量讨论使我得到以下澄清:如果连接中断,发送方无法可靠地知道另一方接收了多少字节。他会可靠地知道 并非所有内容都已收到,但收到了。
答案 1 :(得分:0)
有几个选择。一种方法可能是在每批数据写入后,在NetworkStream
上手动清除数据。
虽然这可能被视为性能问题,但根据您的应用类型,它将是最容错的方法。通过手动调用.Flush()
,您将知道数据是否已成功发送到管道中。
如果您从MemoryStream
批量添加到NetworkStream
,则可以存储“最后写入的字节”,并将它们放回MemoryStream
,在重新建立连接时准备重新尝试。
另一种选择是在连接时实现自己的协议,立即向客户端发送服务器迄今收到的字节数(即自上次连接以来)。获得此信息后,您可以清除MemoryStream
的前X个字节(已发送的那些字节),并继续按下其余数据。
你可以做的最后一件事,可能是最好的方式,是两者的结合。基本上实现发送/确认协议。这意味着,您将发送一大块数据,并等待服务器发回ACK / OK。如果是这样,您可以确定服务器已收到此数据,但如果没有,您可以继续重试,直到您收到该确认。