【问题标题】:How to push (i.e. flush) data sent to a TCP stream如何推送(即刷新)发送到 TCP 流的数据
【发布时间】:2011-10-13 16:40:09
【问题描述】:

RFC 793 表示 TCP 定义了一个“推送”函数,以确保接收者得到数据:

有时用户需要确保他们拥有的所有数据 提交到 TCP 已被传输。为此目的推动 功能已定义。确保提交给 TCP 的数据是 实际传输的发送用户表示它应该是 推送给接收用户。推送会导致 TCP 及时转发并将数据传递到该点。

但是,我找不到push 系统调用。在文件描述符上使用fsync 会产生无效参数错误。

我用一个简单的服务器进行了一个实验,它接受来自客户端的连接,等待,然后向客户端发送 26 个字节:

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 1234

int main(void)
{
    int server_fd;
    int client_fd;

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return 1;
    }

    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        addr.sin_addr.s_addr = INADDR_ANY;

        if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
            perror("bind");
            return 1;
        }
    }

    if (listen(server_fd, 20) != 0) {
        perror("listen");
        return 1;
    }

    {
        struct sockaddr_in addr;
        socklen_t addrlen = sizeof(addr);

        printf("Waiting for connection on port %d\n", PORT);

        if ((client_fd = accept(server_fd, (struct sockaddr*)&addr, &addrlen)) < 0) {
            perror("accept");
            return 1;
        }

        printf("%s:%d connected\n",
               inet_ntoa(addr.sin_addr),
               ntohs(addr.sin_port));
    }

    printf("Giving client time to close connection.\n");
    sleep(10);

    {
        ssize_t sent_length;

        if ((sent_length =
             send(client_fd, "abcdefghijklmnopqrstuvwxyz", 26, 0)) < 0)
        {
            perror("send");
            return 1;
        }

        printf("Sent %Zd bytes.\n", sent_length);
    }

    printf("Closing connection to client\n");
    if (close(client_fd) != 0) {
        perror("close(client_fd)");
        return 1;
    }

    printf("Shutting down\n");
    if (close(server_fd) != 0) {
        perror("server: close(server_fd)");
        return 1;
    }

    printf("Done!\n");
    return 0;
}

我发现send 调用立即返回 26,即使在我关闭客户端连接或拔下网线后也是如此。在后一种情况下,当我重新插入电缆并等待几秒钟(服务器关闭后很长时间)时,数据会出现在客户端上。

如何确保接收并确认使用send 发送的数据?

【问题讨论】:

  • 这里可能有两个问题:一个是如何推送,另一个是如何确保客户端在套接字关闭之前接收到数据
  • PUSH 用于避免数据排队等事情(例如,您可以 send() 单个 HTTP 标头,您的 TCP/IP 堆栈只会将它们排队,直到您有足够的字节用于完整的段/足够时间已经过去/您已经结束发送标头并决定推送它们)

标签: c sockets unix tcp


【解决方案1】:

There is no push,已故的 W. Richard Stevens 说;标准套接字 API 没有提供它,RFC 1122 也不需要这样做。您可以设置TCP_NODELAY 选项,但这只是部分解决方案。

如果您想确保另一端收到您的数据,则让它通过 TCP 通道发送确认。

【讨论】:

  • 不回答“[我如何确保]收到并确认?”的 OP 问题
  • 这是典型的所以...看到它的光明面,至少你的答案确实得到了投票(见我的,neheh)
  • @yi_H,我只是为要增强的答案添加评论,是的,+larsmans 确实添加了定义协议以进行确认的额外声明——所以总体来说是 SO 的胜利 :-)
  • 我对它并不完全满意,想法是服务器必须等待确认,但总比没有好。
【解决方案2】:

尝试在套接字关闭之前添加关闭调用;

shutdown(client_fd,SHUT_RDWR);

然而,真正的解决方案是从客户端获得它已收到数据的确认 - 即你需要定义一个协议——最简单的简单协议是客户端负责在收到数据时关闭套接字。

【讨论】:

    【解决方案3】:

    据我所知,TCP 将确保将数据传输到另一台机器/套接字。

    但是另一端的程序是否读取/访问了数据,无法使用标准套接字 API 进行确认。您的另一端(在这种情况下为客户端)可能正忙于做其他事情,而不是等待数据显示。

    我认为,如果您在服务器/客户端之间实现某种握手以使用某种确认来跟踪已收到的所有内容,那么您的要求将得到满足。

    如果您的应用程序依赖于确认机制,则确认机制很重要。

    【讨论】:

      【解决方案4】:

      您可以通过禁用 Nagle 算法来强制立即发送小数据包,但这并不能保证客户端会收到它。

      如果您必须等待确认,则必须将其构建到协议中并等待客户端将某些内容写入套接字,以发出消息接收的信号。

      【讨论】:

        【解决方案5】:

        确保您的数据已发送的唯一方法是接收答复。经过多天的测试,这是确保它“冲”到另一边的唯一方法。

        // 接收直到对端关闭连接以确保所有数据都已发送

        do {
        
                iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
                if (iResult > 0){
                    printf("Bytes received: %d\n", iResult);
                }
                else if (iResult == 0){
                    printf("Connection closed\n");
                }
                else{
                    printf("recv failed with error: %d\n", WSAGetLastError());
                }
        
            } while (iResult > 0);
        

        【讨论】:

          猜你喜欢
          • 2016-10-16
          • 2012-12-17
          • 2018-12-17
          • 2015-04-16
          • 2019-03-15
          • 2023-03-26
          • 1970-01-01
          • 2014-10-12
          • 1970-01-01
          相关资源
          最近更新 更多