用C语言实现简单的HTTP数据请求

2023-05-16

  • 我的博客:https://blog.csdn.net/qq_37388044
  • 我的知乎:https://www.zhihu.com/people/bbtganmin
  • 联系方式:知乎私信

转载或者引用本文内容请注明来源及原作者!


目录

  • 一、前言
  • 二、什么是http?
  • 三、浏览器是如何访问地址的?
  • 四、HTTP请求报文格式
  • 五、HTTP响应报文格式
  • 六、代码
    • 1、通过域名获取ip
    • 2、创建socket并连接服务器
    • 3、合成并发送请求报文,接收响应
  • 最后说下GET和POST的区别


一、前言

  之前在做嵌入式开发时遇到一个项目,需要用http与服务器通信,移植了一个http库,编译后发现固体太大,flash都差点不够放了,要实现的功能也就简单的信息获取和上报而已。于是我决定用socket简单的实现这些功能。


二、什么是http?

超文本传输协议(HyperTextTransferProtocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。基于TCP/IP通信协议来传递数据。


三、浏览器是如何访问地址的?

我们平常在浏览器地址栏输入http网址后,浏览器会做以下动作:

  1. 从网址(url)中取出域名,然后通过DNS服务器获取到对应的IP地址。
  2. 通过IP地址连接到服务器(默认端口为80)。
  3. 浏览器合以HTTP协议合成请求报文,并发送给服务器。
  4. 服务器返回响应报文。
  5. 服务器断开TCP。
  6. 浏览器以HTTP协议解析响应报文,得到内容并做出相应的处理(一般会得到html文本,浏览器通过html生成页面并显示出来)。

四、HTTP请求报文格式

在这里插入图片描述
在这里插入图片描述
例如, 下面的网址:

http://www.zhihu.com/people/cheng-tian-zhe

合成请求报文应该是:

GET /people/cheng-tian-zhe HTTP/1.1
Host: www.zhihu.com


五、HTTP响应报文格式

在这里插入图片描述
在这里插入图片描述
在这里,我们只关注状态码、Content-Length字段、主体:

  1. 状态码200表示成功。
  2. Content-Length可以获取主体的大小,以便申请内存接收主体内容。(不一定有)
  3. 主体就是我们要得到的数据。

六、代码

下面是一个获取服务器时间的例子,esp8266通过http请求服务器时间。

请求报文:

GET /bbt/nowtime HTTP/1.1
Host: xxx.xxx.com:80
                                //这里需要有个"\r\n"

响应报文:

Content-Type:application/json;charset=UTF-8
Date:Tue, 17 Nov 2020 08:42:27 GMT
...                             //这里省略部分字段
Connection:close

{"datas":"2020-11-17 16:41:05","resp_code":0,"resp_msg":"SUCCESS","successed":true}

1、通过域名获取ip

int bbtGetIpByHostname(char *host_name, char *first_ip)
{
    char **pptr;
    const char *first;
    struct hostent *hptr;
    char str[32] = {'\0'}; 
 
    /* 调用gethostbyname()。结果存在hptr结构中 */
    if((hptr = gethostbyname(host_name)) == NULL)
    {
        ESP_LOGE(TAG, " gethostbyname error for host:%s", host_name);
        return -1;
    }

    /* 根据地址类型,将地址打出来 */
    switch(hptr->h_addrtype)
    {

        case AF_INET:
        case AF_INET6:

            pptr=hptr->h_addr_list;
            /* 将刚才得到的所有地址都打出来。其中调用了inet_ntop()函数 */
            for(; *pptr!=NULL; pptr++)
                ESP_LOGI(TAG, " address:%s", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
            first = inet_ntop(hptr->h_addrtype, hptr->h_addr, str, sizeof(str));
            // ESP_LOGI(TAG, first address: %s\n", first);
            if(first)
                memcpy(first_ip, str, 32);
            break;

        default:
            ESP_LOGI(TAG, "unknown address type");
            break;

    }

    return 0;
}

2、创建socket并连接服务器

int socket_open_http(char *host_name, char *ip_out)
{
    int socketId;
    struct sockaddr_in serv_addr;
    int status;

    if (bbtGetIpByHostname(host_name, ip_out))
        return -1;
 
    socketId = socket(AF_INET, SOCK_STREAM, 0);

    if((int)socketId < 0)
    {
       ESP_LOGE(TAG, "Create a socket failed!");
       return -1;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    
    unsigned long net_ip = inet_addr(ip_out);
    if(net_ip == INADDR_NONE) {
        ESP_LOGE(TAG, "net_ip failed!");
        return -1;
    }
    serv_addr.sin_addr.s_addr = net_ip;
    serv_addr.sin_port = htons(80);   // 端口默认80
    serv_addr.sin_family = AF_INET;

    status = connect(socketId, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if(status!=0) {
        ESP_LOGE(TAG,  "Connecting failed!");
        close(socketId);
        return -1;
    }
    ESP_LOGI(TAG, "connect sucess");
    
    return socketId;

}

3、合成并发送请求报文,接收响应

#define BBT_HTTP_HOST_NAME	"xxx.xxx.com"    //域名
#define BBT_HTTP_TIME_PATH	"/bbt/nowtime"   //路径

#define	BBT_HTTP_TIMEOUT_S	2  //超时时间
#define TCP_BUFFSIZE	        1460

int bbtGetServerTime(void)
{
    char http_ip[32] = {0};
    int http_skt = socket_open_http(BBT_HTTP_HOST_NAME, http_ip);
    if (http_skt <= 0)
        return -1;

    //这里控制模块的请求时间,设置tcp收发超时,如果没有要求可以不设置 
    struct timeval timeout = {BBT_HTTP_TIMEOUT_S, 0}; 
    //设置发送超时
    setsockopt(http_skt, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
    //设置接收超时
    setsockopt(http_skt, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
    
    char *http_request;
    const char *GET_TIME =
        "GET %s HTTP/1.1\r\n"
        "Host: %s:%d\r\n"
        "\r\n";

    // 合成请求报文
    int get_len = asprintf(&http_request, GET_TIME, BBT_HTTP_TIME_PATH, http_ip, 80);
    if (get_len < 0) {
        ESP_LOGE(TAG, "GET request buffer error");
        goto GET_TIME_ERR;
    }
    // 发送报文
    int send_len = send(http_skt, http_request, get_len, 0);
    free(http_request);
    if (send_len < 0) 
        goto GET_TIME_ERR;
 
    // 接收响应(这里假设一包能收完)
    char tcp_buf[TCP_BUFFSIZE] = {0};
    memset(tcp_buf, 0x00, TCP_BUFFSIZE);
    int buff_len = recv(http_skt, tcp_buf, TCP_BUFFSIZE, 0);
    if (buff_len < 0) { 
        ESP_LOGE(TAG, "receive data error!");
        goto GET_TIME_ERR;          
    }

    // 释放socket
    close(http_skt);

    printf("tcp_buf = \n%s\n", tcp_buf);

    /* 处理tcp_buf中的响应报文 */
    /* 可以以"\n"取行,一直取到一个只有2个字节的即"\r\n" ,下一行就是主体*/

    return 0;

GET_TIME_ERR:

    close(http_skt);
    return -1;
}


最后说下GET和POST的区别

GET和POST都是HTTP协议中的常用请求的方法,那么它们究竟有什么不同?

事实上,对于简单的http数据请求来说,两者之间并没有多大的区别。但当时项目负责服务器的同学提供的接口一般都用POST,而不用GET,这是为什么呢?

具体原因有很多,他跟我说了主要的几点:

  • GET请求的参数是有长度限制的,而POST没有。
  • GET请求的参数只能是ASCII字符,而POST没有限制。
  • GET请求的参数是直接显示在URL上,相比之下POST更安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用C语言实现简单的HTTP数据请求 的相关文章

  • 有人成功用 Robolectric 模拟 HttpRequests 吗?

    我刚刚开始使用 Robolectric 模拟大多数 Android 类似乎工作正常 但是当我的测试类尝试创建 DefaultHttpClient 时 它会收到可怕的 Stub 错误 被测试的类在这一行失败 HttpClient httpcl
  • 是否可以在ajax get请求中获取页面的一部分?

    我知道我们可以在向服务器发出 GET 请求时获取整个页面 但是如果我只对该页面上的一个特定 div 感兴趣 或者更准确地说对其内容感兴趣 该怎么办 这里唯一的选择是获取整个页面 例如使用 jquery find 从中获取 div 内容吗 或
  • C# - 如何进行 HTTP 调用

    我想对网站进行 HTTP 调用 我只需要点击 URL 不想上传或下载任何数据 最简单 最快的方法是什么 我尝试了下面的代码 但它很慢 并且在第二次重复请求后 它只是超时 59 秒 然后恢复 WebRequest webRequest Web
  • PHP 是否有与 Java 的 RequestDispatcher.forward 等效的功能?

    在 Java 中我可以编写一个非常基本的 JSPindex jsp像这样 这样做的效果是用户请求index jsp 或者只是包含目录 假设index jsp是目录的默认文档 将会看到home action没有浏览器重定向 即 forward
  • 为什么使用 HTTP 动词?

    因为动词的目标是像 server domain getallrecords 或 server domain delete1record 或类似的 URL 而getallrecords delete1record都是专门为特定目的而设计的 为
  • 减少1000张图片的HTTP请求?

    我知道这个问题可能听起来有点疯狂 但我想也许有人会想出一个聪明的主意 假设您在一个 HTML 页面上有 1000 个缩略图 图像大小约为5 10 kb 有没有办法在单个请求中加载所有图像 以某种方式将所有图像压缩到一个文件中 或者您对该主题
  • Spring webflow 应用程序:HTTP 302 暂时移动

    我的 java 应用程序中的每个请求都会生成另外 2 个带有 HTTP 302 错误的请求 例如 如果请求查看名为板 html 这个请求是从首页 html 我收到按以下顺序生成的 3 个请求 POST home html 302 Moved
  • 为什么使用HTTP协议时需要指定端口号?

    即使我们使用HTTP协议 为什么还需要用IP地址指定端口号 例如 http xyz 8080 这到底是什么意思 我们已经知道 在使用 HTTP 时 请求将在端口 80 上提供服务 那么为什么我们要显式指定端口呢 HTTP 的默认端口为 80
  • 使用特定 HTTP 方法链接到页面 (DELETE)

    如何像 Rails 那样链接到页面并让浏览器使用 DELETE 方法调用它 我试过 a href DELETE ME a 但不起作用 我使用 Node js 所以我可以用它来处理 DELETE 方法 你不能 链接只会触发 GET 请求 您可
  • 在 iOS 中,http 204 响应返回空白页面,有办法阻止这种情况吗?

    以前可能有人问过这个问题 但我似乎找不到解决方案 所以如果是这种情况 我深表歉意 我正在开发一个使用express的简单节点应用程序 其中一个帖子路由返回 http 204 并发送它 下面是我的代码 router post id funct
  • Node.js:server.setTimeout、server.timeout 和 server.keepAliveTimeout 之间有什么区别

    官方文档是here https nodejs org api http html http server settimeout msecs callback server setTimeout 中没有提及有关服务下一个请求的信息 所以我认为
  • JS 库请求的常见 HTTP 标头是什么?

    使用JavaScript 框架原型 http www prototypejs org 我注意到 Ajax 请求通过一个名为X Requested With 其他 JavaScript 库 如 jQuery dojo 和 YUI 是否会向其
  • UNIX/MacOS 上静态文件的“临时 Web 服务器”?

    是否有一个像小型网络服务器这样的东西 我可以从命令行调用它 只从本地文件系统获取文件并通过特定端口上的 HTTP 为它们提供服务 我希望能够做这样的事情 cd Sites mysite serve 10 0 1 1 8080 这应该会启动一
  • 服务器响应中的“连接:保持活动状态”

    我正在尝试建立从 Silverlight 应用程序到 Apache 服务器托管的 PHP 页面的 HTTP 持久连接 即无需为每个 HTTP 请求创建新的 TCP 连接 为此 我需要网络服务器发送其 HTTP 响应 并将 Connectio
  • 如何使用 python 的 http.client 准确读取一个响应块?

    Using http client在 Python 3 3 或任何其他内置 python HTTP 客户端库 中 如何一次读取一个分块 HTTP 响应一个 HTTP 块 我正在扩展现有的测试装置 使用 python 编写 http clie
  • 从手机访问本地主机[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在使用
  • 最适合“正在进行的作业”的 HTTP 状态代码

    向客户端提供的最合适的 HTTP 状态代码是什么 表示 您的请求很好 但仍在进行中 请稍后在完全相同的位置回来查看 例如 假设客户端提交初始请求以启动繁重的查询 服务器立即返回一个 URL 客户端可以定期轮询该 URL 以获取结果 如果客户
  • 自定义 HTTP 标头:命名约定

    我们的一些用户要求我们将与其帐户相关的数据包含在HTTP 标头我们向他们发送的请求 甚至是他们从我们的 API 获得的响应 添加自定义 HTTP 标头的一般约定是什么 naming format etc 另外 请随意发布您在网络上偶然发现的
  • 如何从 Retrofit2 获取字符串响应?

    我正在做 android 正在寻找一种方法来执行超级基本的 http GET POST 请求 我不断收到错误 java lang IllegalArgumentException Unable to create converter for
  • 我可以从 HTTP 请求中找到无线接入点的 BSSID(MAC 地址)吗?

    假设有人在咖啡店里无线连接到互联网 并向 johnsveryownserver com 发送 HTTP 请求 服务器端 有什么方法可以确定我的MAC地址吗 无线接入点他们连接到什么 请注意 我对他们机器的 MAC 地址不感兴趣 如果我无法使

随机推荐

  • 数字电路基础知识系列(六)之LC滤波器的基础知识

    针对设计过程的问题 xff0c 如有疑问 xff0c 欢迎留言讨论 xff01 点我返回目录 LC滤波器 xff0c 是指将电感 L 与电容器 进行组合设计构成的滤波电路 xff0c 可去除或通过特定频率的无源器件 电容器具有隔直流通交流
  • I-002 智能家居系列--ESP8266环境搭建

    智能家居系列 1 NodeMCU简介2 硬件介绍3 开发工具的选择3 1 软件介绍3 2 环境搭建 4 测试代码5 后续会更新这边文章 1 NodeMCU简介 NodeMCU简介 NodeMCU 是一个开源的 物联网 平台 它使用 Lua
  • I-000 智能家居系列--需求梳理

    智能家居系列 1 智能家居2 系统框架3 组成部分4 开发思路5 当前的进展 1 智能家居 智能家居的目的旨在提高人们的生活水平 xff0c 确保人们的生活更加舒适 2 系统框架 下图只是初版 xff0c 在具体的开发过程中 xff0c 会
  • Cartographer(二)使用思岚rplidar雷达进行cartographer建图

    一 修改launch文件和lua配置文件 xff08 1 xff09 修改revo lds lua 到目录下 xff0c 打开文件 home meng xx catographer ws src cartographer ros carto
  • 数字电路硬件设计系列(三)之缓启电路设计

    针对设计过程中的问题 xff0c 如有疑问 xff0c 欢迎留言评论 xff01 点我返回目录 1 简介 在一些大电压 大电流的产品中 xff0c 上电的瞬间通常会有较大的电流冲击 xff0c 下图是一款产品上电过程中波形 最大的电流达到1
  • 数字电路硬件设计系列(十)之RS485电路设计

    针对设计过程中的问题 xff0c 如有疑问 xff0c 欢迎留言评论 xff01 点我返回目录 1 简介 RS485通信属于串口通信中的半双工通信 xff0c RS485具有支持多节点 xff08 32个节点 xff09 传输距离远 xff
  • 数字电路硬件设计系列(五)之AT89C51/C52最小系统设计

    针对设计过程中的问题 xff0c 如有疑问 xff0c 欢迎留言评论 xff01 点我返回目录 1 简介 AT89C51 C52是指两个系列的产品 xff0c 具体包含AT89C51 AT89C52 xff0c 但是最小系统的组成基本上相差
  • 1.18 接地设计问题(非常实用)

    目录 1 简介 2 接地的分类 3 接地的目的 3 1 对地阻抗低 3 2 地平面稳定 3 3 对地均衡 4 共地耦合干扰 5 常用的基地方式 6 安全地 防雷击浪涌接地的接法 1 简介 在电路设计的过程中 xff0c 接地是一个老生常谈的
  • 1.0 硬件设计基础(面试题)

    1 滤波磁珠和滤波电感的区别 磁珠由导线穿过铁氧体组成 xff0c 直流电阻很小 xff0c 在低频时阻抗也很小 xff0c 对直流信号几乎没有影响 在高频 xff08 几十兆赫兹以上 xff09 时磁珠阻抗比较大 xff0c 高频电磁场在
  • 1.19 旁路电容与去耦电容

    1 简介 旁路电容和旁路电容是电路设计过程中十分常见 xff0c 但是很多应将工程师都没有真正区分这两者的功能 xff0c 我们先来看看这两个单词在英文中的意思 xff1a 电容英文中文解释旁路电容Bypass抄近道去耦电容couple是一
  • vins运行1

    vins fusion 运行笔记 安装code utils 1 fatal error elfutils libdw h 没有那个文件或目录 没有安装 sudo apt get install libdw dev 2 fatal error
  • 四旋翼无人机汇报

    我主要负责的是四旋翼无人机的组装和飞控参数的调试 遇到的问题 当初焊接时由于时间紧张用的是比较细的焊条 xff0c 虽然焊接成功 xff0c 但是导致了后续插拔导线时容易接触不良 xff0c 后来在焊点处重新焊接 由于未在地面站软件中修改无
  • 载波相位差分定位

    x1f30e 我接触差分定位是通过暑期在南京北斗一段时间的实习 x1f30e 我把 Lambda算法的zip和rar上传到了这里 x1f30e https wwi lanzoui com b01bp70yj x1f30e 再推荐一个gith
  • nRF SPI 与 TWI 操作相关 (BMI088 与 MLX90614 举例)

    SPI0 与 TWI0 的 ID相同 xff0c SPI0 与 TWI0 的 ID相同 编译时有报错 若要避免 xff0c 有两个方法 1 使用不同ID外设 2 使用模拟SPI或模拟IIC nRF SPI 初始化 sdk config h
  • ros(13):ros找不到包报错及解决办法--Config.cmake

    目录 一 基础包 1 1 rospy包 1 2 tf包 1 3 grid map包 1 4 serial 二 专有包 2 1 dynamic reconfigure包 2 2 rosparam handler包 2 3 qt build包
  • stm32 esp8266 ota升级-自建mqtt和文件服务器动态AB面升级

    stm32 esp8266 ota系列文章 xff1a stm32 esp8266 ota 快速搭建web服务器之docker安装openresty stm32 esp8266 ota升级 tcp模拟http stm32 esp8266 o
  • win系统C++的udp通信(接收并发送)详细教程、win下inet_pton和inet_ntop无法使用解决方法

    对UDP编程0基础的可以参考这篇记录博文 我做的是同一个程序中接收指定IP地址和端口号的信息作为输入 xff0c 通过程序的算法进行处理 xff0c 处理后的信息再通过另一个指定IP地址和端口号进行发送 也就是需要做两个udp一个接收数据
  • postman下载与汉化(附直接下载链接)

    想用英文版本的可以直接点击如下链接下载最新版本 官网最新版本 无法汉化 xff1a Download Postman Get Started for Free 如果想要汉化的就不能使用最新版本 xff0c 因为最新版本没有汉化包可以用 汉化
  • Qt 自定义流程图 diagram

    Qt 自定义流程图 diagram 前言程序执行效果程序源码下载图形视图框架成员介绍重写QGraphicsItem程序源码介绍重点代码 前言 本文将对QGraphicsScene QGraphicsView QGraphicsItem这三个
  • 用C语言实现简单的HTTP数据请求

    我的博客 xff1a https blog csdn net qq 37388044我的知乎 xff1a https www zhihu com people bbtganmin联系方式 xff1a 知乎私信 转载或者引用本文内容请注明来源