Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)

简介: Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)

一、需要实现的内容和方式

比如进入百度,可以看见右侧web界面的必要信息,这就需要通过http客户端去请求获取。

在网页上打开,比如在chrome浏览器中,按F12即可进入开发者模式,可以看见发送的请求

实现方式

二、完整代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<netdb.h>
#include <fcntl.h>
#define HTTP_VERSION "HTTP/1.1"
#define CONNECTION_TYPE "Connection:close\r\n"
#define BUFFER_SIZE 4096
char* host_to_ip(const char* hostname){
    hostent *host_entry=gethostbyname(hostname);
    //14.215.177.39 -->usigned int(因为ipv4是32位的,每个点分隔,值为0~255.正好可以用unsigned int表示,每8位表示一个0~255,共4个)
    //inet_nota 是把 0x12121212 --->"18.18.18.18" 也就是将网络地址转换成“.”点隔的字符串格式
    if(host_entry){
        return inet_ntoa(*(in_addr*)*host_entry->h_addr_list);//由于h_addr_list可能有几个ip地址,这边取第一个,因此加*,然后强转成in_addr*,由于inet_ntoa为值传递,因此还要加*
    }
    return NULL;
}
int http_create_socket(char* ip){
    int sockfd=socket(AF_INET,SOCK_STREAM,0);//sockfd的结果就是int类型,是一个文件句柄。TCP要用SOCK_STREAM,UDP用SOCK_DGRAM
    sockaddr_in sin={0};
    sin.sin_family=AF_INET;
    sin.sin_port=htons(80);//http协议端口为80
    sin.sin_addr.s_addr=inet_addr(ip);//"字符串的ip"地址转为uint32_t和上面的inet_ntoa正好相反
    // int ret=connect(sockfd,(sockaddr*)&sin,sizeof(sockaddr_in));
    // if(ret!=0) return -1;
    if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
    return -1;
  }
    fcntl(sockfd,F_SETFL,O_NONBLOCK);//设置为非阻塞(如果socket是阻塞,read(),会阻塞挂起。如果socket是非阻塞,read()也是立刻返回)(设置文件状态标志用F_GETFL,读取文件状态标志用F_SETFL)
    return sockfd;
}
//hostname:github.com
//resource:/wangbojing
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};
    sprintf(buffer,
"GET %s %s\r\n\
Host:%s\r\n\
%s\r\n\
\r\n",
    resource,HTTP_VERSION,
    hostname,
    CONNECTION_TYPE
    );
    send(sockfd,buffer,strlen(buffer),0);
    // recv()//因为是非阻塞状态,如果用recv会快速过去
    //select
    fd_set fdread;
    FD_ZERO(&fdread);//用来清空fd_set集合,即让fd_set集合不再包含任何文件句柄
    FD_SET(sockfd,&fdread);//将sockfd置为检测的状态(用来将一个给定的文件描述符加入集合之中)
    timeval tv;
    tv.tv_sec=5;//5秒间隔 select采集一次
    tv.tv_usec=0;
    char* result=(char*)malloc(sizeof(int));
    memset(result,0,sizeof(int));
    while(1){
        int selection=select(sockfd+1,&fdread,NULL,NULL,&tv);//第一个参数为所有文件描述符的最大值+1   用select去监测,这个io里面有无数据
        if(!selection||!FD_ISSET(sockfd,&fdread)){//FD_ISSET判断描述符fd是否在给定的描述符集fdset中
            break;
        }else{
            memset(buffer,0,BUFFER_SIZE);
            int len=recv(sockfd,buffer,BUFFER_SIZE,0);//接受数据
            if(len==0){//disconnect
                break;
            }
            result=(char*)realloc(result,(strlen(result)+len+1)*sizeof(char));//重新分配内存重建(+1是为了末尾'\0',不是最后一个的话会被覆盖)
            strncat(result,buffer,len);//把buffer数据copy到resulut中
        }
    }
    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.hostname和resource的区别

hostname 相当于前面的github.com

resource(资源) 是后面的/wangbojing,

2.fd_set的作用

实际上可能不止一个io,有一个fd_set,如果某个值为1,那么就代表该fd有数据过来了。比如sockfd=5,

并且当前sockfd有数据了,那么fd_set的为5的索引的值为1。如果没有数据则为0。

因此通过select来选择io。


相关文章
|
11天前
|
C++
c++学习笔记07 结构体
C++结构体的详细学习笔记07,涵盖了结构体的定义、使用、数组、指针、嵌套、与函数的交互以及在结构体中使用const的示例和解释。
26 0
|
8天前
|
移动开发 监控 网络协议
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
|
10天前
|
C++
【学习笔记】【C/C++】 c++字面值常量
【学习笔记】【C/C++】 c++字面值常量
19 1
|
6天前
|
网络协议 编译器 Go
揭秘!TCP、RPC、gRPC、HTTP大PK,谁才是网络通信界的超级巨星?一篇文章带你秒懂!
【8月更文挑战第25天】本文以教程形式深入对比了TCP、RPC、gRPC与HTTP这四种关键通信协议,并通过Go语言中的示例代码展示了各自的实现方法。TCP作为一种可靠的传输层协议,确保了数据的完整性和顺序性;RPC与gRPC作为远程过程调用框架,特别适合于分布式系统的函数调用与数据交换,其中gRPC在性能和跨语言支持方面表现出色;HTTP则是广泛应用于Web浏览器与服务器通信的应用层协议。选择合适的协议需根据具体需求综合考量。
49 0
|
8天前
|
网络协议 Linux
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
在Linux中,如何查看 http 的并发请求数与其 TCP 连接状态?
|
10天前
|
编译器 C++
【C/C++学习笔记】C++声明与定义以及头文件与源文件的用途
【C/C++学习笔记】C++声明与定义以及头文件与源文件的用途
22 0
|
10天前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
22 0
|
11天前
|
C++
c++学习笔记09 引用
C++引用的详细学习笔记,解释了引用的概念、语法、使用注意事项以及引用与变量的关系。
20 0
|
11天前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
22 0
|
11天前
|
存储 C++
c++学习笔记06 指针
C++指针的详细学习笔记06,涵盖了指针的定义、使用、内存占用、空指针和野指针的概念,以及指针与数组、函数的关系和使用技巧。
24 0
下一篇
云函数