简体   繁体   English

有没有办法阻塞套接字send(),直到我们得到该数据包的确认?

[英]Is there a way to block on a socket send() until we get the ack for that packet?

或者我是否必须在应用程序级别实现它?

TCP will in general require you to synchronize the receiver and sender at the application level. TCP通常要求您在应用程序级别同步接收方和发送方。 Combinations of SO_SNDBUF tweaking or TCP_NODELAY alone are not likely solve the problem completely. SO_SNDBUF调整或TCP_NODELAY组合不可能完全解决问题。 This is because the amount of data that can be "in flight" before send() will block is more or less equal to the sum of: 这是因为在send()之前可以“在飞行中”的数据量将阻塞或多或少等于:

  1. The data in the transmit side's send buffer, including small data fragments being delayed by Nagle's algorithm , 发送方发送缓冲区中的数据,包括由Nagle算法延迟的小数据片段,
  2. The amount of data carried in unacknowledged in-flight packets, which varies with the congestion window ( CWIN ) and receive window ( RWIN ) sizes. 未确认的正在进行的数据包中携带的数据量,它随拥塞窗口CWIN )和接收窗口( RWIN )大小而变化。 The TCP sender continuously tunes the congestion window size to network conditions as TCP transitions between slow-start, congestion avoidance, fast-recovery, and fast-retransmit modes. 当TCP在慢启动,拥塞避免,快速恢复和快速重传模式之间转换时,TCP发送器不断地将拥塞窗口大小调整到网络条件。 And, 和,
  3. Data in the receive side's receive buffer, for which the receiver's TCP stack will have already sent an ACK , but that the application has not yet seen. 接收方的接收缓冲区中的数据,接收方的TCP堆栈已经发送了ACK ,但应用程序尚未看到。

To say it another way, after the receiver stops reading data from the socket, send() will only block when: 换句话说,在接收器停止从套接字读取数据之后, send()将仅在以下情况下阻塞:

  1. The receiver's TCP receive buffer fills and TCP stops ACK ing, 接收器的TCP接收缓冲区填满并停止TCP ACK ING,
  2. The sender transmits un ACK ed data up to the congestion or receive window limit, and 发送者发送未ACK ED数据到拥塞或接收窗口限制,并
  3. The sender's TCP send buffer fills or the sender application requests a send buffer flush. 发送方的TCP发送缓冲区填充或发送方应用程序请求发送缓冲区刷新。

The goal of the algorithms used in TCP is to create the effect of a flowing stream of bytes rather than a sequence of packets. TCP中使用的算法的目标是创建流动的字节流而不是数据包序列的效果。 In general it tries to hide as much as possible the fact that the transmission is quantized into packets at all, and most socket APIs reflect that. 一般来说,它试图尽可能地隐藏传输量化为数据包的事实,而大多数套接字API反映了这一点。 One reason for this is that sockets may not be implemented on top TCP (or indeed even IP) at all: consider a Unix domain socket, which uses the same API. 这样做的一个原因是套接字可能根本不在顶级TCP(或甚至IP)上实现:考虑使用相同API的Unix域套接字。

Attempting to rely on TCP's underlying implementation details for application behavior is generally not advisable. 尝试依赖TCP的底层实现细节来实现应用程序行为通常是不可取的。 Stick to synchronizing at the application layer. 坚持在应用层同步。

If latency is a concern in the situation where you're doing the synchronization, you may also want to read about interactions between Nagle's algorithm and delayed ACK that can introduce unnecessary delays in certain circumstances. 如果在您正在进行同步的情况下延迟是一个问题,您可能还想了解Nagle算法和延迟ACK之间的交互,这可能会在某些情况下引入不必要的延迟。

I also faced the same problem few weeks ago when implementing a VoIP server. 几周前我在实施VoIP服务器时也遇到了同样的问题。 After spending several days I could come up with a solution. 花了好几天后,我可以想出一个解决方案。 As many others mentioned, there is no any direct system call to do the job. 正如许多其他人所提到的那样,没有任何直接的系统调用来完成这项工作。 Instead, 代替,

  1. You can check if we have received the ACK after sending a packet with TCP_INFO option. 您可以在发送带有TCP_INFO选项的数据包后检查我们是否收到了ACK
  2. If we haven't received the ACK , wait for few milliseconds and check again. 如果我们没有收到ACK ,请等待几毫秒再检查一下。

This may continue until a time out reaches. 这可能会持续到超时为止。 You have to implement it as a wrapper function to send() call. 您必须将其实现为send()调用的包装函数。 You will need tcp_info struct from <netinet/tcp.h> . 您将需要来自<netinet/tcp.h> tcp_info <netinet/tcp.h> tcp_info结构。 It is the data structure for holding information about your tcp connection . 它是用于保存有关tcp连接的信息的数据结构。

Here is the pseudo code 这是伪代码

int blockingSend(const char *msg, int msglen, int timeout) {

    std::lock_guard<std::mutex> lock(write_mutx);

    int sent = send(sock_fd, msg, msglen,  0); 

    tcp_info info;
    auto expireAt = chrono::system_clock::now() + chrono::milliseconds(timeout);
    do {
        this_thread::sleep_for(milliseconds(50));
        getsockopt(sock_fd,SOL_TCP, TCP_INFO, (void *) &info, sizeof(info));

      //wait till all packets acknowledged or time expires
    } while (info.tcpi_unacked > 0 && expireAt > system_clock::now());

    if(info.tcpi_unacked>0) {
        cerr << "no of unacked packets :" << info.tcpi_unacked << endl;
        return -1;
    }
    return sent;
}

Here tcpi_unacked member holds the number of packets unacknowledged of your connection. 这里tcpi_unacked成员保存未确认连接的数据包数。 If you read it soon after the send() call, it will contain number of unacked packets which is equal to number of packets sent. 如果您在send()调用后不久读取它,它将包含分组数据包的数量,该数量等于发送的数据包数量。 With time, number of unacked packets will be decremented to zero. 随着时间的推移,未打包的数量将减少到零。 Therefore you need to periodically check the value of tcpi_unacked till it reaches zero. 因此,您需要定期检查tcpi_unacked的值,直到它达到零。 If the connection is half opened, you will never receive ACK s while causing a endless loop. 如果连接打开一半,则在导致无限循环时永远不会收到ACK For such scenarios you may need to add a timeout mechanism as implemented above. 对于这种情况,您可能需要添加上面实现的超时机制。

Even though this question has been asked long ago, this answer may help some one who has faced the same problem. 尽管很久以前就提出过这个问题,但这个答案可能会帮助那些遇到同样问题的人。 I must mention that there could be more accurate solutions to this problem than this solution. 我必须提一下,除了这个解决方案,这个问题可能有更准确的解决方案。 Since I am a newbie to system programming and C/C++ this is what I could come up with. 因为我是系统编程和C / C ++的新手,所以我可以想出来。

If you are talking about TCP, then no - no socket API I've seen allows you to do this. 如果你在谈论TCP,那么没有 - 我见过的套接字API允许你这样做。

You need to implement the ack in your application protocol if you need to be sure that the other end had received(and possibly processed) your data. 如果需要确保另一端已收到(并可能已处理)您的数据,则需要在应用程序协议中实现确认。

The ack for the packet is at the transport layer (well below the application layer). 数据包的确认位于传输层(远低于应用层)。 You are not even guaranteed to have your entire buffer belong to its own packet on the network. 您甚至不能保证您的整个缓冲区都属于网络上自己的数据包。 What is it you are trying to do? 你想做什么?

Why not just use a blocking socket? 为什么不使用阻塞套接字?

This may be a bit dated, but here is some explanation on blocking/non-blocking and overlapping IO. 这可能有点过时,但这里有关于阻塞/非阻塞和重叠IO的一些解释。

http://support.microsoft.com/kb/181611 http://support.microsoft.com/kb/181611

It would help if we knew which language and OS you were using, BTW, to better show code snippets. 如果我们知道您正在使用哪种语言和操作系统BTW,那么它将有助于更好地显示代码片段。

If you use setsockopt() to lower SO_SNDBUF to a value only large enough to send one packet, then the next send() on that socket should block until the previous packet is acknowledged. 如果使用setsockopt()SO_SNDBUF降低到仅足以发送一个数据包的值,则该套接字上的下一个send()应该阻塞,直到确认前一个数据包。 However, according to tcp(7) , the socket buffer size must be set prior to listen() / connect() . 但是,根据tcp(7) ,必须在listen() / connect()之前设置套接字缓冲区大小。

The whole point of using TCP is to hide that individual ACK from applications. 使用TCP的全部意义是隐藏来自应用程序的单个ACK。 If you need to detect every ACK, then implement your own protocol using UDP or IP. 如果需要检测每个ACK,请使用UDP或IP实现自己的协议。 TCP is probably an overkill. TCP可能是一种矫枉过正。 Or you can go up the stack and use a protocol like HTTP as a transport. 或者你可以上堆栈并使用HTTP之类的协议作为传输。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么TCP套接字在收到错误的ACK时发送RST数据包而不是重新发送数据? - Why TCP socket send a RST packet instead of resending data when receives an incorrect ACK? 通过原始套接字接收“ syn”数据包时,如何响应“ syn ack”数据包? - How do I respond with a “syn ack” packet when receiving a “syn” packet through a raw socket? 使用RakNet发送数据包的最佳方法 - Best way to send packet with RakNet TCP套接字数据以XML数据包发送 - tcp socket data send in xml packet boost asio套接字异步读取方法将阻塞,直到收到换行符为止 - boost asio socket async read methods block until a newline is received 让系统调用select()块,直到套接字读取某些内容 - let the system call `select()` block until a socket gets something to read Java-&gt; C ++ TCP套接字直到关闭才发送 - Java -> C++ TCP Socket does not send until close 做 avcodec_receive_frame 和 avcodec_send_packet 块/如何设计 ffmpeg 解码循环? - Do avcodec_receive_frame and avcodec_send_packet block / how to design ffmpeg decoding loop? 有没有办法在信号量或输入事件之前阻塞单个线程? - Is there a way to block a single thread until a semaphore or input event? 从 TCP 套接字接收包数据? - Receive packet by packet data from TCP socket?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM