10. Linux下实现简单的http客户端请求

简介: 10. Linux下实现简单的http客户端请求

本文Linux下实现简单的http客户端请求

HTTP(超文本传输协议)是一种用于在网络上进行数据通信的协议。HTTP 协议定义了客户端和服务器之间如何交换信息,包括请求和响应格式、使用的方法、状态码等。

在 HTTP 协议中,资源(Resource)指的是由 URL (统一资源定位符)所标识的任意可用于访问的信息。这些信息可以是各种不同类型的数据,例如:HTML 页面、图片、音频、视频等多媒体文件、JavaScript 文件、样式表文件(CSS)、XML 文件、JSON 数据。


要完成一个完整的 HTTP 网络通信过程,需要使用以下一系列函数

  1. socket():创建套接字。
  2. connect():连接到指定的服务器。
  3. send():向服务器发送请求报文。
  4. listen():监听(多个)io里面有没有可读(或可写)的数据
  5. recv():从服务器接收响应报文。
  6. close():关闭套接字,释放资源。


简单的http客户端请求主要函数如下,详细可看代码里面的注释


  • char *host_to_ip(const char*hostname):该函数用于将主机名(hostname)转换为 IP 地址。
  • int http_create_socket(char *ip):该函数创建一个 TCP 套接字,并连接到指定的 IP 地址和端口号上。
  • char *http_send_request(const char *hostname,const char *resource):该函数发送 HTTP 请求并获取响应结果。


编译指令

zxmgcc -o http http.c
./http www.baidu.com /
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
//Linux下的头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#define BUFFER_SIZE     4096
#define HTTP_VERSION    "HTTP/1.1"
#define CONNETION_TYPE "Connection: close\r\n"
// /*该函数用于将主机名(hostname)转换为 IP 地址。它首先调用 gethostbyname 函数
// 获取主机名对应的地址信息结构体,然后从中提取出 IP 地址信息,并返回一个指向字符串类型的 IP 地址。*/
char *host_to_ip(const char*hostname){
    /*gethostbyname用于通过主机名获取主机的IP地址信息,
    并返回一个hostent结构体类型的指针,该结构体包含了主机名、别名和IP地址等信息。*/
    struct hostent *host_entry = gethostbyname(hostname);
    /*inet_ntoa()函数是一个在网络编程中常用的函数,它可以将一个32位整数IP地址转换成标准的点分十进制形式
    struct in_addr是一个C语言中的数据结构,用于表示32位IPv4地址。*/
    if (host_entry){
        //h_addr_list 是指向主机IP地址列表的指针,其类型为 char **
        return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);
    }
    return NULL;
}
/*该函数创建一个 TCP 套接字,并连接到指定的 IP 地址和端口号上。它首先调用 socket 函数创建套接字,
然后调用 connect 函数连接到目标地址。如果成功则返回套接字文件描述符,否则返回 -1。*/
int http_create_socket(char *ip){
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    //sockaddr_in 用于表示 IPv4 地址和端口号
    struct sockaddr_in sin={0}; //
    sin.sin_family = AF_INET;   //指定 IP 地址的协议族
    sin.sin_port=htons(80);  //表示端口号
    sin.sin_addr.s_addr=inet_addr(ip);//表示 IP 地址,inet_addr作用与inte_ntoa相反
    /*在网络编程中,由于不同协议族(如IPv4、IPv6等)使用不同的地址结构体,为了保证代码的通用性和可移植性,
    在函数调用时需要将特定协议族所对应的地址结构体指针转换为通用的 sockaddr 结构体指针。
    因此,当我们想要将一个特定协议族(如IPv4)的地址结构体传递给一个接受 sockaddr 结构体参数的函数时,
    就需要使用 (struct sockaddr*)&sin 进行类型强制转换,以避免编译器报错。*/
    if ( 0!= connect(sockfd,(struct sockaddr*)&sin,sizeof(struct sockaddr_in))){
        return -1;
    }
    //将 sockfd 文件描述符设置为非阻塞模式
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
    return sockfd;
}
/*该函数发送 HTTP 请求并获取响应结果。它首先通过 host_to_ip 函数将主机名转换为 IP 地址,
然后使用 http_create_socket 函数创建套接字并连接到目标服务器。接着构建 HTTP 请求报文,
并使用 send 函数将其发送给服务器。最后等待服务器响应并读取响应结果。*/
char *http_send_request(const char *hostname,const char *resource){
    char *ip=host_to_ip(hostname);
    int sockfd=http_create_socket(ip);
    char buffer[BUFFER_SIZE]={0};
    //HTTP协议中使用的行结束符是 "\r\n" ,每个字段之间需要用两个连续的回车符和换行符 "\r\n\r\n" 分隔开来
  sprintf(buffer, "GET %s %s\r\nHost: %s\r\n%s\r\n\r\n",
        resource, HTTP_VERSION, hostname, CONNETION_TYPE);
    /*如果在使用 UDP 协议进行通信或者需要对多个目标地址进行发送数据操作,则应该选择使用 sendto 函数;
    如果在使用 TCP 协议进行通信且只涉及一个目标地址,则可以选择使用 send 函数。*/
    send(sockfd,buffer,strlen(buffer),0);
    /*fd_set 是一个数据类型,可以同时监视多个文件描述符的状态,比如可读、可写或者异常等。
    在使用前必须先通过FD_ZERO宏清空,并使用FD_SET来添加或删除其中的文件描述符*/
    fd_set fdread;
    FD_ZERO(&fdread);
    FD_SET(sockfd,&fdread);
    /*timeval 结构体是一个用于表示时间的结构体,tv_sec 表示秒数,tv_usec 表示微秒数*/
    struct timeval tv;
    tv.tv_sec=5;
    tv.tv_usec=0;
    char *result=malloc(sizeof(int));
    memset(result,0,sizeof(int));
    while (1){
        /*select函数可以监听多个文件描述符(包括Socket),并等待其中任意一个或多个文件描述符上有I/O事件发生时通知调用者
        通过使用select函数,程序可以同时监听多个I/O事件,并且只要有任何一个事件发生了变化就会被立即通知。
        这样可以大大提高程序的性能和响应速度,避免阻塞或死循环等问题*,具体参数分别如下:
        nfds:需要监听的文件描述符最大值+1。即所有待监听文件描述符中最大的那个数加一。
        readfds:指向fd_set类型的指针,用于设置需要检查可读性的文件描述符集合。
        writefds:指向fd_set类型的指针,用于设置需要检查可写性的文件描述符集合。
        exceptfds:指向fd_set类型的指针,用于设置需要检查异常条件(错误)的文件描述符集合。
        timeout:等待超时时间。如果为NULL,则表示无限等待;如果为0,则表示立即返回。*/
        int selection=select(sockfd+1,&fdread,NULL,NULL,&tv);
        if( !selection || !FD_ISSET(sockfd,&fdread)){
            break;
        }
        else{
            //buffer之前已经被send之后,需要清空
            memset(buffer,0,BUFFER_SIZE);
            int len=recv(sockfd,buffer,BUFFER_SIZE,0);
            if (len==0){
                break;
            }
            result=realloc(result,(strlen(result)+len+1)*sizeof(char));
            strncat(result,buffer,len); //strncat()将指定长度的字符串追加到目标字符串末尾
        }
    }
    return result;
}
int main(int argc, char *argv[]) {
  if (argc < 3) return -1;
  char *response = http_send_request(argv[1], argv[2]);
  printf("response : %s\n", response);
  free(response);
}


目录
相关文章
|
1天前
|
缓存 应用服务中间件 Apache
HTTP 范围Range请求
HTTP范围请求是一种强大的技术,允许客户端请求资源的部分内容,提高了传输效率和用户体验。通过正确配置服务器和实现范围请求,可以在视频流、断点续传下载等场景中发挥重要作用。希望本文提供的详细介绍和示例代码能帮助您更好地理解和应用这一技术。
37 19
|
28天前
|
JSON Java 数据格式
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
84 25
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
|
9天前
|
JSON JavaScript 前端开发
什么是HTTP POST请求?初学者指南与示范
HTTP POST请求是一种常用的HTTP方法,主要用于向服务器发送数据。通过合理设置请求头和请求主体,可以实现数据的可靠传输。无论是在客户端使用JavaScript,还是在服务器端使用Node.js,理解和掌握POST请求的工作原理和应用场景,对于Web开发至关重要。
122 18
|
9天前
|
JSON 数据格式
.net HTTP请求类封装
`HttpRequestHelper` 是一个用于简化 HTTP 请求的辅助类,支持发送 GET 和 POST 请求。它使用 `HttpClient` 发起请求,并通过 `Newtonsoft.Json` 处理 JSON 数据。示例展示了如何使用该类发送请求并处理响应。注意事项包括:简单的错误处理、需安装 `Newtonsoft.Json` 依赖,以及建议重用 `HttpClient` 实例以优化性能。
51 2
|
26天前
|
Web App开发 大数据 应用服务中间件
什么是 HTTP Range请求(范围请求)
HTTP Range 请求是一种非常有用的 HTTP 功能,允许客户端请求资源的特定部分,从而提高传输效率和用户体验。通过合理使用 Range 请求,可以实现断点续传、视频流播放和按需加载等功能。了解并掌握 HTTP Range 请求的工作原理和应用场景,对开发高效的网络应用至关重要。
63 15
|
30天前
|
数据采集 JSON 测试技术
Grequests,非常 Nice 的 Python 异步 HTTP 请求神器
在Python开发中,处理HTTP请求至关重要。`grequests`库基于`requests`,支持异步请求,通过`gevent`实现并发,提高性能。本文介绍了`grequests`的安装、基本与高级功能,如GET/POST请求、并发控制等,并探讨其在实际项目中的应用。
42 3
|
2月前
|
前端开发 UED 开发者
CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度
本文探讨了CSS Sprites和图标字体在网页图标加载优化中的应用。CSS Sprites通过合并多图标减少HTTP请求,提升加载速度;图标字体则以字体形式呈现图标,便于调整样式。文章分析了两者的优缺点及应用场景,并提供了应用技巧和注意事项,旨在帮助开发者提升页面性能,改善用户体验。
33 5
|
2月前
|
Web App开发 Linux 应用服务中间件
【DrissionPage】Linux上如何将https改为http
通过上述步骤,可以在Linux上将DrissionPage从HTTPS改为HTTP。关键在于修改DrissionPage配置、代码中的HTTPS设置、URL以及Web服务器配置,确保所有部分都正确使用HTTP协议。通过合理配置和测试,能够确保系统在HTTP环境下稳定运行。
42 1
|
2月前
|
JSON API 数据格式
Python中获取HTTP请求响应体的详解
本文介绍了如何使用Python的`requests`和`urllib`库发送HTTP请求并处理响应体。`requests`库简化了HTTP请求过程,适合快速开发;`urllib`库则更为底层,适用于性能要求较高的场景。文章详细演示了发送GET请求、处理JSON响应等常见操作。
51 3
|
27天前
|
Web App开发 网络安全 数据安全/隐私保护
Lua中实现HTTP请求的User-Agent自定义
Lua中实现HTTP请求的User-Agent自定义