【问题标题】:Non-blocking TCP socket and flushing right after send?发送后立即进行非阻塞 TCP 套接字和刷新?
【发布时间】:2012-07-14 12:34:18
【问题描述】:

我正在为我的应用程序使用 Windows 套接字 (winsock2.h)。由于阻塞套接字不允许我控制连接超时,因此我使用的是非阻塞套接字。在发送命令之后,我正在使用关闭命令进行刷新(我必须)。我的超时时间是 50 毫秒,我想知道的是,如果要发送的数据这么大,是否存在只发送一部分数据或什么都不发送的风险?提前谢谢...

    hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    u_long iMode=1;
    ioctlsocket(hSocket,FIONBIO,&iMode);
    connect(hSocket, (sockaddr*)(&sockAddr),sockAddrSize);
    send(hSocket, sendbuf, sendlen, 0);
    shutdown(hSocket, SD_BOTH);
    Sleep(50);
    closesocket(hSocket);

【问题讨论】:

  • 嗯,我很确定连接超时在阻塞套接字时也能正常工作——我一直在客户端线程中这样做——如果断开连接,它们会循环一次 connect() 尝试并睡眠(2000),不断重试连接,直到成功。无论如何,连接超时与连接()成功后的套接字写入无关,所以我假设您的意思是一种写入超时形式?如果是这样,我从来没有尝试过这样的事情——我不使用连接/断开协议,(除了浏览器/HTTP!),因为性能很差,(尤其是在高延迟链接上)。
  • 您可以使用 select() 使阻塞模式连接超时。您不必调用 shutdown() 来刷新。 TCl 不会丢失数据,否则 Internet 将无法工作,例如这个论坛,亚马逊,谷歌等。你的问题是基于错误的前提。
  • 您正在“选择”套接字状态并检查是否断开连接?我想尽可能快地发送数据。另外两种可能的情况是:服务器等待太长时间以接受连接或接收。所以连接和写入都应该在最多 50 毫秒内完成,因为在我的情况下时间非常重要。我必须刷新写入和读取流,因为服务器不断向我发送不必要的大数据并且我的互联网连接有限。实际上我什至不想要来自服务器的单个字节:)
  • @EJP 通过从另一个线程检查?因此,假设套接字在连接线中被阻塞。如果另一个线程在超时时关闭了套接字,那么该块是否被释放?
  • @GunDenizAkkoc 是的,是的,但即使您使用非阻塞连接,也没有什么说您必须保持非阻塞模式才能接收。当您关闭关闭时,关闭是 100% 冗余的,同上睡眠。

标签: c++ sockets tcp nonblocking flush


【解决方案1】:

非阻塞 TCP 套接字并在发送后立即刷新?

没有刷新 TCP 套接字这样的事情。

由于阻塞套接字不允许我控制连接超时

错误。您可以在阻塞套接字上使用select()

我使用的是非阻塞式的。

不合逻辑。

在发送命令后我正在使用关闭命令刷新(我必须)。

您不必这样做,shutdown() 不会刷新任何内容。

我的超时时间是 50 毫秒

为什么?发送数据的时间取决于数据的大小。明显地。对发送使用固定超时没有任何意义。

而我想知道的是,如果要发送的数据这么大,是否存在只发送一部分数据或什么都不发送的风险?

在阻塞模式下,您提供给send() 的所有数据将尽可能发送。在非阻塞模式下,如果可能,将发送send() 的返回值所代表的数据量。无论哪种情况,如果发送失败,连接将被重置。无论您叠加什么超时机制,都不可能改变其中的任何一个:具体来说,在超时后异步关闭套接字只会导致关闭附加到正在发送的数据中。它不会导致发送中止。

您的代码不会通过任何已知的代码审查。零错误检查;睡眠完全没有意义;关闭之前关闭是多余的。如果 sleep 旨在实现超时,则不会。

我想尽快发送数据。

你不能。 TCP 实现流量控制。您对此无能为力。您受到接收器的速率限制。

另外两种可能的情况是:服务器等待太长时间以接受连接

没有这种情况。客户端可以在服务器调用 accept(). 之前完成连接

或接收。

对此您无能为力:见上文。

所以连接和写入都应该在最多 50 毫秒内完成,因为在我的情况下,时间非常重要。

见上文。为需要可变时间的操作实现固定超时是没有意义的。而且 50 毫秒对于连接超时来说太短了。如果这是一个真正的问题,您应该保持连接打开,以便连接延迟只发生一次:事实上,您应该尽可能长时间地保持 TCP 连接打开。

我必须同时刷新写入和读取流

你不能。 TCP 中没有操作会刷新读取流或写入流。

因为服务器不断向我发送不必要的大数据,而且我的互联网连接有限。

另一个不合逻辑的。如果服务器向您发送数据,您必须读取它,否则您将停止服务器,这与刷新您自己的写入流没有任何关系。

其实我什至不想要服务器的一个字节

运气不好。你必须阅读它。 [如果你在 BSD Unix 上,你可以关闭用于输入的套接字,这会导致来自服务器的数据被丢弃,但这在 Windows 上不起作用:它会导致服务器重置连接。]

【讨论】:

  • 哦,代码不完整,我发布的代码仅用于第二个线程。如果 3 次握手完成并且要发送的数据从我的计算机(到电线)中发出,我只需将计数器重置为零。所以它确实有效。另一件事是,选择接受以秒而不是毫秒为单位的超时。最后,据我所知,我的意思是从缓冲区中刷新数据。我现在只是使用无延迟选项...
  • 您的方法在没有失败的意义上是有效的,但它并没有做您声称需要的事情:强制写入超时。它所做的只是无偿关闭一个工作套接字。重新'另一件事',你错了。 select() 接受timeval 结构形式的超时,该结构具有秒和微秒字段。 shutdown() 仍然不刷新套接字,这可能意味着刷新套接字发送缓冲区。只有正常传输或重置才能做到这一点。
【解决方案2】:

感谢 EJP 和 Martin,现在我已经创建了第二个线程来检查。同样在我在问题中发布的代码中,我添加了“counter=0;” “发送”行之后的行并删除了关机。它就像我现在想要的那样工作。它永远不会等待超过 50 毫秒 :) 非常感谢

unsigned __stdcall SecondThreadFunc( void* pArguments )

{

while(1)
{
    counter++;
    if (counter > 49)
    {
        closesocket(hSocket);
        counter = 0;
        printf("\rtimeout");
    }

    Sleep(1);
}
return 0;

}

【讨论】:

  • 我真的拒绝为此受到指责。它没有回答你的问题。它的行为不像你想的那样。仍在发送的所有数据仍将发送,然后将发送关闭信号。它不实现任何类型的超时。
猜你喜欢
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 2014-11-01
  • 1970-01-01
  • 2013-06-04
  • 2013-07-28
  • 2015-04-24
相关资源
最近更新 更多