繁体   English   中英

阻止TCP套接字写然后立即关闭 - 这是一个问题吗?

[英]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和其他材料的问题:

  1. stream.Close()调用Socket.Close()。 据说Socket.Close()立即关闭,丢弃未发送的网络缓冲区数据。 这是不好的。 但是,Socket.Write文档说如果套接字阻塞(默认情况下),Socket.Write将阻塞,直到所有数据都被发送。 所以没有问题,对吧?
  2. 一般情况下,是否存在上述代码会导致接收方无法接收Write中发送的所有内容的情况? (假设网络100%可靠)

在Socket.Write之后调用Close是绝对可以的。 这是TCP RFC 793所说的

“关闭连接是一个优雅的操作,因为流量控制允许传输(和重新传输)未完成的SEND,直到所有服务都被服务。 因此,可以接受多个SEND呼叫,然后是关闭,并期望将所有数据发送到目的地。

混淆部分来自MSDN文档

“如果使用面向连接的协议,发送将阻塞,直到所有在缓冲器中的字节被发送

实际上,阻止Write只将数据复制到传出缓冲区并返回。 根据RFC 793,网络提供商有责任在调用Socket.Close之后完成数据的传递(当然,只要连接没有死亡)。

如果接收器未达到Read,则可能会丢失部分数据,这取决于较低层中使用的缓冲区的大小。

但是如果读取器上的接收器被阻塞,那么在读取时提供足够的缓冲区时没有问题。

假设一个可靠的网络并在“写入”之前执行“读取”并在接收器侧执行足够的缓冲区,则此代码绝对可靠!

但解决方案!:

正如我所说的接收器端缓冲区的大小真的很重要。 如果缓冲区没有足够的空间,你将失去这个代码!

所以你有三个选择:

  1. 只需分配足够大小的缓冲区即可。
  2. 使用库或更高协议传输数据(例如,我们在Win32 API中具有用于传输文件的TransmitFile函数)
  3. 在发送数据之前实现您的协议,发送所需缓冲区的大小,然后发送实际数据。

如果您知道最大数据量,那么我会为您提供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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM