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。


相关文章
|
25天前
|
开发框架 Linux C语言
C、C++、boost、Qt在嵌入式系统开发中的使用
C、C++、boost、Qt在嵌入式系统开发中的使用
31 1
|
1月前
|
存储 Shell Linux
【Shell 命令集合 文件传输 FTP客户端工具】Linux ncftp 命令使用指南
【Shell 命令集合 文件传输 FTP客户端工具】Linux ncftp 命令使用指南
40 0
|
1月前
|
存储 Linux API
Linux系统编程 C/C++ 以及Qt 中的零拷贝技术: 从底层原理到高级应用(三)
Linux系统编程 C/C++ 以及Qt 中的零拷贝技术: 从底层原理到高级应用
31 1
|
16天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
40 6
|
1月前
|
Linux 编译器 程序员
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
68 0
|
1月前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
67 0
|
12天前
|
网络协议 Ubuntu Unix
Linux 下使用 socket 实现 TCP 客户端
Linux 下使用 socket 实现 TCP 客户端
|
25天前
|
XML JSON JavaScript
推荐一个比较好用的c++版本http协议库-cpp-httplib
推荐一个比较好用的c++版本http协议库-cpp-httplib
38 1
|
1月前
|
监控 Linux 编译器
Linux C++ 定时器任务接口深度解析: 从理论到实践
Linux C++ 定时器任务接口深度解析: 从理论到实践
70 2
|
1月前
|
存储 Linux 程序员
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
76 0