linux网络编程之断点传输文件

以下载链接“http://www.boa.org/boa-0.94.13.tar.gz”为例:

断点续传实验大概步骤:
=====================
1,使用gethostbyname()获取站点“www.boa.org”的IP地址
2,以“boa-0.94.13.tar.gz”为例,构建HTTP请求报文首部:

   char *httpreq = "GET /boa-0.94.13.tar.gz HTTP/1.1\r\n"
                   "Range: bytes=%d-%d\r\n"
                   "Host: %s\r\n\r\n"
                    begin, end, host);

注意:
    A) 以上字符串httpreq就是发送给远程主机www.boa.org的HTTP请求报文,通过TCP发送
    B) Range字段是要求主机发送申请文件的部分内容,begin和end分别是文件的开始和结束
       B.1 如果写成 "Range: bytes=0-\r\n",代表要求主机发送全文
       B.2 如果写成 "Range: bytes=-1000\r\n",代表要求主机发送前1000个字节
       B.3 如果写成 "Range: bytes=0-200\r\n",代表要求主机发送前200个字节
    C) 正常情况下,HTTP服务器会对这个请求报文返回206,并给出实际返回的字节数和范围:
       C.1 "Content-Length: 1000" 这个字段代表本次HTTP发来的数据大小为1000个字节(不含HTTP首部)
       C.2 "Content-Range: bytes 2000-2999/9999" 代表本次传送的数据范围是第2000-2999个字节(共1000个字节),而所请求的文件总大小是9999个字节。

3,将收到的报文的HTTP首部去掉(HTTP首部是指从开头到\r\n\r\n结尾的部分),剩下的就是下载的文件内容
4,将文件内容以非缓冲方式保存下来。

5,如果发生下载时网络断线,或者人为终止了下载进程,那么在下一次下载时先获取当前已下载部分的大小,并作为Range参数告知远端HTTP服务器,要求发送部分文件,实现断点续传,节约网络流量节约时间。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>
#include <stdbool.h>

#include "common.h"

#define RWRWRW  (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

#define SERV_PORT   80

#define FILENAME    "boa-0.94.13.tar.gz"

typedef struct sockaddr SA;


void http_request(char *buf, int size, const char *filename
                            , const int begin, const char *host);


int main(void)
{
    int sockfd;
    struct sockaddr_in servaddr;
    struct hostent *hptr = NULL;
    struct in_addr **pptr = NULL;


    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        err_quit("socket error");
    }

    char *hostname = "www.boa.org";
    if ((hptr = gethostbyname(hostname)) == NULL) {
        err_quit("gethostbyname error for host: %s: %s",
                    hostname, hstrerror(h_errno));
    }

    pptr = (struct in_addr**)hptr->h_addr_list;

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(SERV_PORT);
    memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));

    if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0) {
        err_quit("connect error");
    }


    int fd;
    int file_size;
    struct stat statbuf;

    if (access(FILENAME, F_OK)) {

        if ((fd = open(FILENAME, O_WRONLY|O_CREAT, RWRWRW)) < 0) {
            err_sys("create %s failure", FILENAME);
        }

        file_size = 0;
    } else {

        if ((fd = open(FILENAME, O_APPEND|O_WRONLY, RWRWRW)) < 0) {
            err_sys("open %s failure", FILENAME);
        }

        if (stat(FILENAME, &statbuf) < 0) {
            err_sys("stat error");
        }

        file_size = statbuf.st_size;
    }


#ifdef  DEBUG
    printf("      host:\t%s\n", hostname);
    printf("  filename:\t%s\n", FILENAME);
    printf("start size:\t%d\n", file_size);
    printf("------------------------------------------------\n\n");
#endif

    char request[MAXLINE];
    http_request(request, sizeof(request), FILENAME, file_size, hostname);


    if (write(sockfd, request, strlen(request)) != strlen(request)) {
        err_quit("request failure");
    }


    char recvbuf[4096];
    int nread;
    bool flage = true;
    char tar[] = "\r\n\r\n";
    char *begin = NULL;

    while (1) {
        bzero(recvbuf, sizeof(recvbuf));

        if ((nread = read(sockfd, recvbuf, sizeof(recvbuf))) <= 0) {
            if (0 == nread) {
                break;
            } else {
                err_sys("read error");
            }
        }

        if (flage) {
            if ((begin = strstr(recvbuf, tar)) == NULL) {
                continue;
            } else {
                write(fd, begin+strlen(tar), nread-(begin-recvbuf)-strlen(tar));

                flage = false;
            }
        }

        if (!flage) {
            if (write(fd, recvbuf, nread) != nread) {
                err_quit("write error");
            }
        }
#ifdef  DEBUG
        printf("******");
        fflush(stdout);
#endif
    }
/*
    if (stat(FILENAME, &statbuf) < 0) {
        err_quit("stat failure");
    } else {
        if (statbuf.st_size == full_size) {
            printf("---success!---\n");
        }
    }
*/
    printf("---success!---\n");
    return EXIT_SUCCESS;
}

void http_request(char *buf, int size, const char *filename, const int begin, const char *host)
{
    assert(buf);
    assert(filename);
    assert(host);

    bzero(buf, size);
    snprintf(buf, size, "GET /%s "
                        "HTTP/1.1\r\n"
                        "Range: bytes=%d-\r\n"
                        "Host: %s\r\n\r\n", filename, begin, host);
}

posted @ 2017-08-16 20:04  gluo-dreamer  阅读(367)  评论(0编辑  收藏  举报