【问题标题】:Unable to send whole file over TCP connection! (UNIX C)无法通过 TCP 连接发送整个文件! (UNIX C)
【发布时间】:2011-12-16 09:43:59
【问题描述】:

所以我编写了一个多线程 Web 服务器,这是程序中的一个功能。此函数获取输出文件描述符 (fd)、内容类型、指向要服务的数据的指针 (*buf) 和数据大小 (numbytes)。它总是卡在 5775 字节!我尝试使用write() 代替send(),但无济于事!我尝试一次发送整个buf,甚至尝试分块传输,但wget 显示它在5775 字节处获取stck!代码如下:

int return_result(int fd, char *content_type, char *buf, int numbytes)
{
    char out_buf[BUF_SIZE], numb[6];
    int buf_len, total = 0, buf_size;
    long int i = 0;
    sprintf(numb, "%d", numbytes);
    strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
    strcat(out_buf, content_type);
    strcat(out_buf, "\nContent-Length: ");
    strcat(out_buf, numb);
    strcat(out_buf, "\nConnection: Close\n  \n");
    printf("\nSending HTTP Header\n %d bytes sent!",
           send(fd, out_buf, strlen(out_buf), 0));
    char *start = NULL, *str = NULL, *temp = NULL;
    start = buf;
    printf("\n Start Pointer Val = %ld", &start);
    while (start != NULL) {
        printf("\n While Loop");
        if (i + 2048 * sizeof(char) < numbytes) {
            printf("\n If 1");
            str = (char *)malloc(sizeof(char) * 2048);
            memcpy(str, start, sizeof(char) * 2048);
            i = i + 2048 * sizeof(char);
            buf_size = send(fd, str, 2048, 0);
            free(str);
            printf("\n Sent %d bytes total : %d", buf_size, total =
                   total + buf_size);

            temp = start + sizeof(char) * 2048;
            start = temp;

        } else {

            i = numbytes - i * sizeof(char);
            if (i > 0) {
                printf("\n If 2");
                printf("\n Value of i %d", i);
                str = (char *)malloc(sizeof(char) * i);
                memcpy(str, start, sizeof(char) * i);
                printf("Total bytes finally sent:%d", total =
                       total + send(fd, str, i, 0));
                if (total == numbytes) {
                    printf("\nTransfer Complete!");
                }
                free(str);

            }
            start = NULL;
        }
    }
    printf("out of loop!");
    return 0;
}

【问题讨论】:

  • 您是否真的收到了符合您期望的“最终发送的总字节数:..”消息?如果是这样,则可能是缓冲/刷新问题。
  • 是的,事实上我正在从服务器端发送()所有字节,但 wget 只收到 5775!

标签: c sockets unix


【解决方案1】:

我建议用Advanced Programming in the Unix Environment, 2nd edition 中的以下writen() 函数替换您的代码:

ssize_t             /* Write "n" bytes to a descriptor  */
writen(int fd, const void *ptr, size_t n)
{
        size_t          nleft;
        ssize_t         nwritten;

        nleft = n;
        while (nleft > 0) {
                if ((nwritten = write(fd, ptr, nleft)) < 0) {
                        if (nleft == n)
                                return(-1); /* error, return -1 */
                        else
                                break;      /* error, return amount written so far */
                } else if (nwritten == 0) {
                        break;
                }
                nleft -= nwritten;
                ptr   += nwritten;
        }
        return(n - nleft);      /* return >= 0 */
}

此代码已经过调试并且已知可以正常工作,并且进一步允许write(2) 在运行良好时一次写入PIPE_BUF 字节以获得更快的速度。

send(2)应该阻止,如果它不能发送您请求的所有数据,但是。我认为更有趣的是使用纯 send(2) 调试版本,而不需要任何周围的努力将事情分成块。

write(2)send(2) 更好的是sendfile(2) -- 打开文件,将描述符和套接字传递给sendfile(2),然后让内核为你处理这一切,如果出现以下情况,使用零拷贝机制可能。

最后一点:HTTP uses CRLF,不是简单的回车。每个\n 应替换为\r\n

【讨论】:

  • 非常感谢您的意见!我添加了您的代码,但 wget 仍然卡在 5775 字节上!我 ssh 进入不同的机器,结果发现我到处都遇到同样的问题!有什么想法吗?
【解决方案2】:

试试这样的(printf() 语句为了清楚起见省略):

int send_buf(in fd, void *buf, int numbytes)
{
    char *start = (char*) buf;
    while (numbytes > 0)
    {
        int sent = send(fd, start, numbytes, 0);
        if (sent <= 0)
        {
            if ((sent == -1) && (errno == EAGAIN))
            {
                fd_set wfds;
                FD_ZERO(&wfds);
                FD_SET(fd, &wfds);
                if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
                    continue;
            }
            return -1;
        }

        start += sent;
        numbytes -= sent;
    }

    return 0;
}

int return_result(int fd, char *content_type, void *buf, int numbytes) 
{ 
    char out_buf[BUF_SIZE], 

    int len = sprintf(out_buf,
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: %s\r\n"
        "Content-Length: %d\r\n"
        "Connection: Close\r\n"
        "\r\n",
        content_type,
        numb); 

    if (send_buf(fd, out_buf, len) != 0)
        return -1;

    if (send_buf(fd, buf, numbytes) != 0)
        return -1;

    return 0; 
}

【讨论】:

  • 完美!非常感谢!我的解决方案有什么问题?
  • 当您发送特定的数据块时,无论是格式化的 HTTP 标头还是输入数据缓冲区的一块,您每个块只调用一次 send()。这不能保证一次性发送整个块。事实上,在很多情况下它很可能不会。 send() 返回实际接受的字节数。你必须一直打电话给send(),直到整个块被发送。在我的示例中,我就是这样做的,额外调用 select() 以获取在非阻塞模式下运行的套接字(您没有说您使用的是什么)。
  • 代码中的另一个错误是这一行:temp = start + sizeof(char) * 2048;。应该是这样的:temp = start + (buf_size * sizeof(char));,可以简化为:temp = start + buf_size; 使用指针算法,然后可以进一步简化为:start += buf_size;,就像我在代码中使用的那样。
  • 感谢您的详细回复!我现在完全明白了!
猜你喜欢
  • 1970-01-01
  • 2013-05-12
  • 1970-01-01
  • 2014-09-05
  • 2015-04-16
  • 1970-01-01
  • 2015-06-03
  • 2020-04-28
  • 1970-01-01
相关资源
最近更新 更多