用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数据请求 的相关文章

  • Unicorn + Rails + 大型上传

    我试图在使用 Rails 在 Heroku 上运行 Unicorn 时允许进行大型上传 但我意识到任何大型上传可能需要比 Unicorn 工作线程的超时时间更长的时间 这意味着 我见过这种情况发生 Unicorn 主进程将杀死上传大文件的工
  • Request.Form 和 Request.QueryString 之间的区别?

    有人可以告诉我两者之间的确切区别吗Request Form and Request QueryString 我知道一个区别 比如 如果HTTP请求方式为POST 则用户提交的数据在申请表 收藏 如果HTTP请求方法是GET 则用户提交的数据
  • 如何在 ASP.NET MVC 中获取参数数组作为 GET / POST?

    如何最好地获取数组 item gt value 对作为 GET POST 参数 在 PHP 中 我可以这样做 网址 这得到的参数为 Array a gt Array one gt 100 two gt 200 有什么方法可以在 ASP NE
  • HTTP 响应可以省略 Reason-Prase 吗?

    正常的 HTTP 响应如下所示 HTTP 1 0 200 OK 可以省略什么吗RFC http www w3 org Protocols rfc2616 rfc2616 sec6 html称为原因短语 就像是 HTTP 1 0 200 RF
  • CXF 增加连接池大小而不更改 http.maxConnections

    最近我被要求将 CXF 配置为与我们旧的 XFire 服务相同的参数 这些参数之一是Keep Alive timeout 60 max 20 然而 我做了一些研究 看来 CXF 使用 JVMHttpURLConnection引擎盖下的对象
  • HTTP 接受“级别”?

    我一直在阅读HTTP 1 1 标头 http www w3 org Protocols rfc2616 rfc2616 sec14 html sec14 1在第 14 1 节 接受 的一些示例标头中 他们使用accept extension
  • 在 Go 中读取请求负载?

    我正在使用文件上传器 需要请求负载中的详细信息来裁剪它 func Upload w http ResponseWriter r http Request reader err r MultipartReader if err nil htt
  • Node.JS Web 服务器中的安全性

    所以 我正在学习 Node JS 到目前为止我很喜欢它 我已经有几个项目在工作了 我想我可以在其中使用nodejs 不过 我担心安全问题 如果我使用 Node JS http 模块编写自定义 Web 服务器 我是否可能非常容易受到攻击 Ap
  • 如何自定义解析错误的 HTTP 400 响应?

    我编写了一个 REST API 服务 要求所有响应均为 JSON 但是 当 Go HTTP 请求解析器遇到错误时 它会返回 400 作为纯文本响应 而不会调用我的处理程序 例子 gt curl i H Authorization Basic
  • Spring Security (3.2.5) HTTP POST 身份验证后不转发到原始请求

    我有一个示例 Spring MVC 应用程序 由 Spring security Spring 版本 4 0 1 RELEASE Spring security 3 2 5 RELEASE 保护 当我作为未经身份验证的用户发送 HTTP G
  • HTTP实时音频流服务器

    作为概念验证 我需要创建一个 HTTP 服务器 该服务器在 GET 请求时应启动连续的非编码 非压缩音频数据流 WAV PCM16 我们假设音频数据是 4096 个随机生成的单声道音频样本块 采样率为 44 1kHz 我应该在 HTTP 响
  • 使用传输编码分块的 HTTP 响应中的最大块大小是多少?

    The w3 org RFC2616 http www w3 org Protocols rfc2616 rfc2616 sec3 html sec3 6 1似乎没有定义块的最大大小 但是如果没有最大块大小 则没有空间用于块扩展 必须有一个
  • .htaccess 路由到服务器上的子目录?性能/加载时间

    我想知道是否可以使用 htaccess 文件将我的域直接路由到服务器上的子目录 我从常规虚拟主机提供商处购买了虚拟主机软件包 其中我的domain com 连接到我的服务器的根目录 我想知道是否可以以某种方式将 htaccess 文件上传到
  • GET 和 POST 方法的单独 Flask 路由

    在 Flask 中定义路由时 最好的做法是使用由多个 HTTP 方法定义的单个路由 并在该单个路由中使用显式逻辑处理不同的 HTTP 方法 例如 app route api users methods GET POST def users
  • 通过 http 的私有 git 存储库

    你能推荐任何简单的解决方案来设置可通过http s cleutus建议的 访问的git存储库吗 我有自己的 http 服务器 我想用它来托管一些小型私人项目 在家里我可以通过 ssh 连接 但在工作中防火墙阻止我这样做 有没有免费的方法来设
  • “双点”可以作为 URL 路径部分的一部分吗

    在 URL 中使用父目录双点是否有效且安全 如下例所示 http example com path to file jpg RFC3986 https www rfc editor org rfc rfc3986定义 URI 它描述了路径如
  • HTTP部分上传、断点续传的标准方法

    我正在开发 http 客户端 服务器框架 并寻找处理部分上传的正确方法 与使用带有 Range 标头的 GET 方法进行下载相同 但是 HTTP PUT 并不打算恢复 据我所知 PATCH 方法不接受 Range 标头 有没有办法通过 HT
  • 在处理程序之后访问 HTTP 请求上下文

    在我的日志记录中间件 链中的第一个 中 我需要访问一些在链下游的某些身份验证中间件中编写的上下文 并且仅在处理程序本身执行之后 旁注 需要首先调用日志记录中间件 因为我需要记录请求的持续时间 包括在中间件中花费的时间 此外 当权限不足时 身
  • 是否可以修改 $_SESSION 变量?

    恶意用户是否可以将 SESSION 在 php 中 变量设置为他想要的任何值 很大程度上取决于您的代码 有一点非常明显 SESSION username REQUEST username
  • 以 RESTful 方式增加资源计数器:PUT 与 POST

    我有一个带有计数器的资源 为了举例 我们将该资源称为profile 计数器是数量views对于该配置文件 Per the 休息维基 http rest blueoxen net cgi bin wiki pl HttpMethods PUT

随机推荐

  • 数字电路基础知识系列(六)之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 知乎私信 转载或者引用本文内容请注明来源