使用berkeley套接字的网络连接速度很慢

使用berkeley套接字的网络连接速度很慢

问题描述:

我和我的朋友在C中编写了一个小型下载管理器,它将目标文件拆分为多个部分,并使用单个posix线程下载每个部分。一切似乎都很好,除了它比其他像wget这样的下载管理器(据我所知,不会将文件分成几个块)非常缓慢。在每一个线程,我们用一个简单的循环,从一个插座下载的每个部分:使用berkeley套接字的网络连接速度很慢

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

我想你还应该发布如何构建你的下载请求。 – holgac 2015-04-03 17:52:04

+0

@holgac,我发布了大量的代码。 – 2015-04-03 18:12:24

您尚未在此处发布足够的内容,但通常TCP的意外减速原因将为Nagle's algorithm。当您将小块数据写入套接字时会触发这种情况。这些将自己放在线路上效率低下,所以TCP堆栈等待用户程序在发送数据包之前添加更多数据。只有在“一段时间”内没有添加任何内容时,它才会发送一个不完整的数据包。

这可以被禁用,但由于您的目标是一个高效的批量传输,你可能不应该这样做。