Linux下使用Socket实现http文件下载

2023-05-16

//test.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>

#define TARGET_URL "http://seopic.699pic.com/photo/50010/8515.jpg_wh1200.jpg"
#define TARGET_HOST "seopic.699pic.com"
#define TARGET_PORT 80 //the default port 80

static void GetIPfromDNS(char* ip_addr);
static void get_resp_header(const char *response, int *status_code, char*content_type, long* content_length);

int main(){
    int client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (client_socket < 0) {
        printf("invalid socket : %d\n", client_socket);
        return 0;
    }
    struct sockaddr_in addr;
    char ip_addr[64];
    memset(&addr, 0, sizeof(addr));
    GetIPfromDNS(ip_addr);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(ip_addr);
    addr.sin_port = htons(TARGET_PORT);
    int res = 0;
    res = connect(client_socket, (struct sockaddr *) &addr, sizeof(addr));
    if (res == -1){
        printf("connect failed : %d\n", res);
        return 0;
    }
    char sendbuf[1024] = {0};  
    char recvbuf[1024] = {0}; 
    int index = 0;
    char response;
    bool isGetContent = false;
    sprintf(sendbuf, \
        "GET %s HTTP/1.1\r\n"\
        "User-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\r\n"\
        "Accept: */*\r\n"\
        "Host:%s\r\n"\
        "\r\n"\
        , TARGET_URL, TARGET_HOST);
    send(client_socket, sendbuf, strlen(sendbuf),0);
    while(recv(client_socket, &response, sizeof(response),0)!=0){
        recvbuf[index++] = response;
        if(response == '\r'){
            if(recv(client_socket, &response, sizeof(response),0)!=0){
                recvbuf[index++] = response;
                if(response == '\n'){
                       if(recv(client_socket, &response, sizeof(response),0)!=0){
                            recvbuf[index++] = response;
                            if(response == '\r'){
                                if(recv(client_socket, &response, sizeof(response),0)!=0){
                                    recvbuf[index++] = response;
                                    if(response == '\n'){
                                        isGetContent = true;
                                        printf("\n\nSUCCESS GET HEAD\n\n");
                                        break;
                                    }
                                }
                            }
                        }
                }
            }
        }
    }
    if(isGetContent == true){
        printf("---------------\n");
        printf("#### %ld ####\n\n", strlen(recvbuf));
        printf("%s", recvbuf);
        printf("---------------\n\n\n");
        fflush(stdout);
        int status_code;
        char content_type[1024]; 
        long content_length;
        get_resp_header(recvbuf, &status_code, content_type, &content_length);
        printf("%d, %s, %ld\n",  status_code, content_type, content_length);
        printf("Start write file to local disk ... .... \n");
        fflush(stdout);
        int fd = open("mydownload.jpg", O_CREAT | O_WRONLY, S_IRWXG | S_IRWXO | S_IRWXU);
        unsigned char buf[1024];
        int len = 0;
        int writeLength = 0;
        while((len = recv(client_socket, buf, 1024, 0))!=0){
            write(fd, buf, len);
            writeLength += len;
            if(writeLength == content_length){
                break;
            }
        }
        printf("\n\nTHE END\n\n");
        close(fd);
    }
    close(client_socket);
    return 0;
}

static void GetIPfromDNS(char* ip_addr){
    struct hostent *host = gethostbyname(TARGET_HOST);
    if (!host) {
        ip_addr = NULL;
        return;
    }
    for (int i = 0; host->h_addr_list[i]; i++){
        strcpy(ip_addr, inet_ntoa( * (struct in_addr*) host->h_addr_list[i]));
        break;
    }
}

/*
# status_code : 200, 503, .... 状态码
# content_type : image/jpeg 内容类型
# content_length : 560437 内容长度(字节)
*/
void get_resp_header(const char *response, int *status_code, char*content_type, long* content_length){
    char *pos = (char*)strstr(response, "HTTP/");
    if (pos)
        sscanf(pos, "%*s %d", status_code);

    pos = (char*)strstr(response, "Content-Type:");
    if (pos)
        sscanf(pos, "%*s %s", content_type);

    pos = (char*)strstr(response, "Content-Length:");
    if (pos)
        sscanf(pos, "%*s %ld",content_length);
}

运行结果

$ g++ test.cpp
$ ./a.out 
SUCCESS GET HEAD

---------------
#### 563 ####

HTTP/1.1 200 OK
Server: marco/1.6
Date: Fri, 18 Aug 2017 03:06:52 GMT
Content-Type: image/jpeg
Content-Length: 560437
Connection: keep-alive
X-Request-Id: 96aaeffc8c0ece839ba5495988d22dc5; 54f5daf9ca0effd3deacdcc88a5e54da
X-Source: U/304
ETag: "82e871eb8d245fef907c9e5ef8cd8809"
X-Slice-Complete-Length: 560437
Last-Modified: Thu, 06 Apr 2017 12:59:05 GMT
X-Slice-Size: 65536
Expires: Wed, 23 Aug 2017 17:02:27 GMT
Cache-Control: max-age=691200
Accept-Ranges: bytes
Age: 458936
Via: T.2424.H.1, V.mix-gd-can-008, T.141134.R.1, M.cun-gd-zhs-131

---------------


200, image/jpeg, 560437
Start write file to local disk ... .... 


THE END

主要注意两个点。
1 组织HTTP协议的应用层数据包发起请求。

这里写图片描述

2 利用服务器返回的数据格式中连续两次\r\n解析出头部信息(包含文件大小)和文件原始数据(字节流);
(例程中的51行到70行联系用了4个判断语句直接找出连续的\r\n)

这里写图片描述

补充一点
这里使用了gethostbyname系统函数向DNS服务器发起查询IP,但是帮助文档中已经说明这个函数不再推荐使用了

The gethostbyname*() and gethostbyaddr*() functions are obsolete.  
Applications should use getaddrinfo(3) and getnameinfo(3) instead.

HTTP使用了TCP作为传输层协议,所以会有3次握手的过程,标准的3次握手过程:

这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux下使用Socket实现http文件下载 的相关文章

  • Raspberry 交叉编译 - 执行程序以“分段错误”结束

    我有一个自己编写的程序 我想从我的 x86 机器上为 Raspberry Pi 构建它 我正在使用 eclipse 生成的 makefile 并且无法更改此内容 我已经阅读了 CC for raspi 的教程 Hackaday 链接 htt
  • Web 应用程序的带宽和流量模拟器?

    您能否建议如何创建一个测试环境来模拟 Web 应用程序中的各种类型的带宽和流量 或者也许是一个针对本地主机执行此操作的开源程序 我认为在编写网络应用程序时这是一个非常重要的主题 但这不是一个常见的主题 我能想象创建这种环境的唯一方法是在本地
  • X11 模式对话框

    如何使用 Xlib 在 X11 中创建模式对话框 模态对话框是一个位于应用程序其他窗口之上的窗口 就像瞬态窗口一样 并且拒绝将焦点给予应用程序的其他窗口 在 Windows 中 当试图从模态窗口夺取焦点时 模态也会通过闪 烁模态窗口的标题栏
  • HTTP请求的内容长度>正文大小

    我正在管理一个网站 该网站过去几个月在使用 MVC 3 0 ASP net 构建的 IIS 7 5 上运行良好 当我们的 AJAX POST 请求 通过 jQuery 触发 因发布的 JSON 被截断而失败时 我们时不时地会遇到一个问题 到
  • pprof 和 ps 之间的内存使用差异

    我一直在尝试分析用 cobra 构建的 cli 工具的堆使用情况 这pprof工具显示如下 Flat Flat Sum Cum Cum Name Inlined 1 58GB 49 98 49 98 1 58GB 49 98 os Read
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • 未找到 Gem 命令

    我已经在 Ubuntu 10 10 32 位上安装了 gem apt get install gem y 但当我尝试跑步时 gem install something gem 我收到未找到命令的错误 bash gem command not
  • eBay API 调用不适用于 UPC/EAN

    eBay 的 API findItemsByProduct 操作适用于 UPC 和 EAN 但不幸的是它不起作用 例如 下面的 HTTP GET 请求会抛出 无效的产品 ID 值 错误41 Note 请将 SECURITY APPNAME
  • 如何在 Linux 中重新添加 unicode 字节顺序标记?

    我有一个相当大的 SQL 文件 它以 FFFE 的字节顺序标记开头 我使用 unicode 感知的 linux 分割工具将此文件分割成 100 000 行块 但是当将这些传递回窗口时 它确实not与第一个部分以外的任何部分一样 只是它具有
  • 为什么http使用CRLF作为行分隔符?

    据我所知 使用LF因为行分隔符非常流行 但我想知道为什么许多文本协议 如 HTTP FTP 使用CRLF作为它的行分隔符 我不认为这些协议是为旧打字机发明的 那么这有什么历史原因吗 我尝试通过谷歌 stackoverflow 和维基百科搜索
  • 无需 root 访问权限即可安装 zsh? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 有可能 以及如何 我确实需要在几台具有 ssh 访问权限 但没有 root 访问权限 的远程计算机上使用此功能 下载 zsh wget O zsh t
  • 将node.js +expressjs应用程序的NODE_ENV设置为ubuntu下的守护进程

    我按照这些说明让守护进程正常工作 http kevin vanzonneveld net techblog article run nodejs as a service on ubuntu karmic http kevin vanzon
  • 如何防止 Firefox 缓存

    我尝试了很多可能的解决方案 但无法解决问题 这些不起作用 有人可以帮忙吗 我正在使用jsp servlet application 是websphere Portal 6 1 的一个portlet 切勿
  • Python将文件从Linux复制到WIndows

    我正在构建一个网站 该网站有一个表单 可以捕获用户数据并在用户数据上运行一些cgi cgi 的第一步是需要将文件从 Linux Web 服务器复制到 Windows 计算机 服务器将使用 Active Directory 角色帐户作为复制凭
  • 为什么docker容器提示“权限被拒绝”?

    我使用以下命令来运行 docker 容器 并从主机映射目录 root database 到容器 tmp install database docker run it name oracle install v root database t
  • 如何列出 nginx 中的所有虚拟主机

    有没有一个命令可以列出 CentOS 上 nginx 下运行的所有虚拟主机或服务器 我想将结果通过管道传输到文本文件以用于报告目的 我正在寻找与我用于 Apache 的命令类似的命令 apachectl S 2 gt 1 grep 端口 8
  • 如何在perl中使用O_ASYNC和fcntl?

    我想使用 O ASYNC 选项 当管道可以读取时 SIGIO 的处理程序将运行 但以下代码不起作用 任何人都可以帮助我吗 bin env perl use Fcntl SIG IO sub print catch SIGIO n my fl
  • 在非实时操作系统/内核上执行接近实时任务的最佳方法是什么?

    在一台 GNU Linux 机器上 如果想要执行 实时 亚毫秒级时间关键 任务 您几乎总是必须经历漫长 复杂且容易出现问题的内核补丁过程 以提供足够的支持 1 http en wikipedia org wiki RTLinux Backg
  • 为什么默认情况下不启用 arp 忽略/通告 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有一个需要经验才能回答的具体问题 为什么 arp ignore arp announce 在 Linux 安装 例如 debian 上默认不启用 有

随机推荐