与伯克利插座的网络连接速度较慢

时间:2015-04-03 17:19:10

标签: c multithreading sockets buffer downloading

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

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);
}

1 个答案:

答案 0 :(得分:0)

你在这里发布的不够,但通常TCP的意外放缓的原因是Nagle's algorithm。将大块数据写入套接字时会触发此操作。这些在自己上线时是低效的,因此TCP堆栈等待用户程序在发送数据包之前添加更多数据。只有在“暂时”没有添加任何内容的情况下,它才会实际发送不完整的数据包。

这可以被禁用,但由于您的目标是有效的批量转移,您可能不应该这样做。