c - 伯克利套接字的网络连接速度慢

标签 c multithreading sockets buffer download

我和我的 friend 用 C 语言编写了一个小型下载管理器,它将目标文件分成几个部分,并使用单个 posix 线程下载每个部分。一切似乎都工作正常,除了与其他下载管理器(如 wget)相比它非常慢(据我所知,它不会将文件分成几个 block )。在每个线程中,我们使用一个简单的循环从套接字下载每个部分:

while ((nrecv = recv(sockfd, downbuf, sizeof(downbuf), 0)) > 0)
{
    if ((nwrite = write(fd, downbuf, nrecv)) != nrecv)
        die("write");

    totalrw += nwrite;
}
    /* ... */

我尝试过使用几种不同大小的“downbuf”,例如 2014、2048、4096 和 8192,但差别不大。下载 270 MB 的文件大约需要 45 秒,而 wget 只需 5 秒即可下载相同的文件。服务器和客户端都在同一主机上。为何差异如此之大?你能告诉我 wget 使用了什么技巧吗?

这是我向服务器发出请求的方式:

sockfd = make_conn(website);

hdr.rq_buf = headerbuf; /* buffer to save response header */
hdr.rq_bufsize = sizeof(headerbuf);
hdr.rq_host = website;
hdr.rq_fpath = filepath; /* target file */
hdr.rq_flags = S_HEADFLAG; /* use head method at this moment
                 to get the total file size */

error = headerinit(hdr);

if (error)
{
    die("headerinit()");
}

send(sockfd, headerbuf, strlen(headerbuf), 0); /* send the initial request */

recv(sockfd, respbuf, sizeof(respbuf), 0);

if (-1 == response_proc(respbuf, strlen(respbuf), &resp))
{
    myperror("response_proc()");
    exit(EXIT_FAILURE);
} /* process the header */

size_t sz = (size_t)strtol(resp.rs_content_length, NULL, 10);

divide(sz, chunks, numcons); /* divide the file into several parts */

for (int i = 0; i < numcons; i++)
{
            /* populate data needed for threads */
    args[i].t_hdr.rq_offset.c_start = chunks[i].c_start; /* where to start */
    args[i].t_hdr.rq_offset.c_end = chunks[i].c_end; /* download up to this point */
    args[i].t_hdr.rq_host = strdup(website);
    args[i].t_hdr.rq_fpath = strdup(filepath);

    snprintf(args[i].t_fname, BUFSIZ, "%sp%i", outfile, i);

    args[i].t_order = i;

}

for (i = 0; i < numcons; i++)
{


    if (0 != pthread_create(&threads[i], NULL, thread_main,
                &args[i]))
    {
        die("pthread_create()");
    }

}

for (i = 0; i < numcons; i++)
{

    if (0 != pthread_join(threads[i], &thread_status))
    {
        die("pthread_join()");
    }

}

http_request_header_t 定义为:

typedef struct {
    void        *rq_buf;
    size_t       rq_bufsize;
    char        *rq_host;
    char        *rq_fpath;
    chunk_t      rq_offset;
    int      rq_flags;
} http_request_header_t;

http_response_header_t 定义为:

    typedef struct {
#ifdef WITH_EXTRA_HEADERS
    char    *rs_version;
#endif
    char    *rs_status;
    char    *rs_date;
    char    *rs_server;
    char    *rs_last_modified;
    char    *rs_accept_ranges;
    char    *rs_content_length;
    char    *rs_connection;
    char    *rs_content_type;
} http_response_header_t;

这是每个线程使用的主例程:

    void *
thread_main(void *arg_orig)
{
    thr_arg_t *arg = (thr_arg_t*)arg_orig;

    int fd, sockfd;

    http_response_header_t resp;

    size_t totalrw = 0;
    ssize_t nrecv;

    char *line = malloc(BUFSIZ * sizeof(char));

    char hdrbuf[BUFSIZ];
    char respbuf[BUFSIZ];

    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP;

    ssize_t nwrite = 0;

    void *downbuf = malloc(DOWNBUF * sizeof(char));

    sockfd = make_conn(arg->t_hdr.rq_host);

    fd = open(arg->t_fname, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);

    if (-1 == fd)
    {
        die("thread_open(): fd");
    }

    arg->t_hdr.rq_flags = S_OFFSET;
    arg->t_hdr.rq_buf = hdrbuf;
    arg->t_hdr.rq_bufsize = sizeof(hdrbuf);

    headerinit(arg->t_hdr);
    //printf("%s\n", arg->t_hdr.rq_buf);

    sendn(sockfd, hdrbuf, strlen(hdrbuf), 0);
         /* first, read the header */
    while ((nrecv = readheader(sockfd, &line, BUFSIZ)) > 0)
    {
        strncpy(respbuf + nwrite, line, sizeof(respbuf) - nwrite);
        nwrite += nrecv;
    }

    nwrite = 0;

    //printf("\n\n%s\n\n", respbuf);


    if (-1 == response_proc(respbuf, strlen(respbuf), &resp))
    {
        myperror("thread_response_proc()");
        exit(EXIT_FAILURE);
    }

    if (strncmp(resp.rs_status, "416", 3) == 0)
    {
        fprintf(stderr, "Partial content is not supported by the server\n");
        exit(EXIT_FAILURE);
    }
            /* now read the actual data */
    while ((nrecv = recv(sockfd, downbuf, sizeof(downbuf), 0)) > 0)
    {

        if ((nwrite = write(fd, downbuf, nrecv)) != nrecv)
            die("write");

        totalrw += nwrite;
    }
    if(-1 == nrecv)
    {
        die("recv()");
    }

    close(sockfd);
    close(fd);

    idxwr(arg->t_fname, arg->t_order, totalrw);

    return ((void*)0);
}

最佳答案

您在这里发布的内容还不够多,但通常 TCP 意外减慢的原因是 Nagle's algorithm 。当您将小块数据写入套接字时会触发此操作。这些单独放在线路上效率很低,因此 TCP 堆栈会等待用户程序添加更多数据,然后再发送数据包。只有在“一段时间内”没有添加任何内容时,它才会真正发送不完整的数据包。

可以禁用此功能,但由于您的目标是高效的批量传输,因此您可能不应该这样做。

关于c - 伯克利套接字的网络连接速度慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29436800/

相关文章:

c - 用C语言访问一个URL

c++ - Boost.asio 端点是否可以用于识别 UDP 连接的客户端?

python - 错误号 111 : Connection refused only in python IDLE

c - 如何在C中读取无限字符

c - 打开并写入文件函数/将变量传递给c中的函数?

multithreading - 线程同步与进程同步

c++ - OpenMP:是使用更少的长线程更好,还是使用最大可用的短线程更好?

java - 从 main() 终止 Runnable() 对象

C - 带线程的套接字聊天服务器 - 昵称数组

c - 使用 MPI_Pack 和 Unpack 以及 MPI_send 和 Recieve 时收到的垃圾值