[英]Blocking TCP socket write then immediatelly close - is it a problem?
我有一个发送器,TCP连接,发送数据块,并关闭套接字。 我想写一个最简单但可靠的程序来完成上述工作。 首先想到的是(例如在.NET中,尽管通常与套接字相关的问题):
// assuming LingerOption(false), NoDelay set to whatever
var client = new TcpClient().Connect(server, port);
var stream = client.GetStream();
stream.Write(data, 0, data.Length);
stream.Close();
client.Close();
现在来看一些基于阅读各种MSDN和其他材料的问题:
在Socket.Write之后调用Close是绝对可以的。 这是TCP RFC 793所说的 :
“关闭连接是一个优雅的操作,因为流量控制允许传输(和重新传输)未完成的SEND,直到所有服务都被服务。 因此,可以接受多个SEND呼叫,然后是关闭,并期望将所有数据发送到目的地。 “
混淆部分来自MSDN文档 :
“如果使用面向连接的协议,发送将阻塞,直到所有在缓冲器中的字节被发送 ”
实际上,阻止Write只将数据复制到传出缓冲区并返回。 根据RFC 793,网络提供商有责任在调用Socket.Close之后完成数据的传递(当然,只要连接没有死亡)。
如果接收器未达到Read,则可能会丢失部分数据,这取决于较低层中使用的缓冲区的大小。
但是如果读取器上的接收器被阻塞,那么在读取时提供足够的缓冲区时没有问题。
假设一个可靠的网络并在“写入”之前执行“读取”并在接收器侧执行足够的缓冲区,则此代码绝对可靠!
但解决方案!:
正如我所说的接收器端缓冲区的大小真的很重要。 如果缓冲区没有足够的空间,你将失去这个代码!
所以你有三个选择:
TransmitFile
函数) 如果您知道最大数据量,那么我会为您提供1 Else如果您没有足够的时间而且对协议不感兴趣,那么我会为您提供2 Else我为您提供3
祝好运 ;)
接收方可以向发送方发送等待或重发消息。 如果在接收到整个数据块之前接收器的recv缓冲区已满,或者网络上丢失了数据包,则可能发生这种情况。 因此,如果在交换所有数据之前退出客户端,则会丢失数据。
看看本书的一般解决方案:W。Richard Stevens的UNIX网络编程第一卷。
是的,它是UNIX,但它最终处理TCP / IP通信,这是上述的根源。 对不起,这不是很简单,但也不是很难。 这是一个状态机,其状态多于您的提案所包含的状态。
TcpClient.Close
说“Close方法将实例标记为已处理,并请求关联的Socket关闭TCP连接。基于LingerState属性,在调用Close
方法后,当数据仍然存在时,TCP连接可能会保持打开一段时间当底层连接完成关闭时,没有提供任何通知。“
简而言之,除非您使用LingerState,否则调用Close
将不会丢弃数据的OS缓冲区 - TCP堆栈将尽力传递您编写的数据。
确保捕获异常但是,如果事情变坏,任何Write
或2 Close
调用都可能抛出异常 - 并确保在这些情况下处置套接字和流。
另请注意, Write()
调用可能在数据发送之前不会阻塞,它只会阻塞,直到数据被复制到TCP堆栈(并且由于太多东西,TCP可能部分地将一些数据发送到Write
回之前的网络)
这是回避您原来的问题,但您没有说明您发送的是哪种数据。 你为什么决定首先使用TCP连接? 你多久写一次这些数据块? 如果经常这样,我会推荐两件事之一:
1)保持TCP.Stream打开。 否则,如果在不到4分钟内发送超过64000个块,则可能耗尽可用源端口的TCP堆栈(1025-65535)。 如果在源计算机上打开了其他应用程序,情况会变得更糟。 一旦发生这种情况,您的应用程序将挂起或出错,直到超过4分钟的第一个端口可用。
2)使用UDP而不是TCP。 无法保证交付,但如果这是实时数据,那么您不希望使用TCP,因为它可能会显着延迟实时数据。
如果您使用TCP,则Client.Close()应通过TCP堆栈发送FIN,除非其他主机崩溃或它们之间的网络出现故障,否则应保证交付。
无论您是使用TCP还是UDP,如果您担心到达目的地的每个数据块,您都应该构建一些应用程序级别检查,确保所有块按顺序到达且未损坏...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.