• Linux C/C++ 学习笔记(八):实现http客户端请求


    本文参考Linux C/C++ 开发(学习笔记十 ):实现http请求器(TCP客户端)_菊头蝙蝠的博客-CSDN博客_linux c++ http客户端

    一、需要实现的内容

     

    客户端请求消息
    客户端发送一个 HTTP 请求到服务器的请求消息包括以下格式:请求行( request line )、请求
    头部( header )、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
    服务器响应消息
    HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
    下面实例是一点典型的使用 GET 来传递数据的实例:
    客户端请求:
    1. GET /hello.txt HTTP/1.1
    2. User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
    3. Host: www.example.com
    4. Accept-Language: en, mi

     服务端响应:

    1. HTTP/1.1 200 OK
    2. Date: Mon, 27 Jul 2009 12:28:53 GMT
    3. Server: Apache
    4. Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
    5. ETag: "34aa387-d-1568eb00"
    6. Accept-Ranges: bytes
    7. Content-Length: 51
    8. Vary: Accept-Encoding
    9. Content-Type: text/plain

    二、全部代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #define HTTP_VERSION "HTTP/1.1"
    11. #define CONNECTION_TYPE "Connection:close\r\n"
    12. #define BUFFER_SIZE 4096
    13. char* host_to_ip(const char* hostname){
    14. struct hostent *host_entry=gethostbyname(hostname);
    15. //14.215.177.39 -->usigned int(因为ipv4是32位的,每个点分隔,值为0~255.正好可以用unsigned int表示,每8位表示一个0~255,共4个)
    16. //inet_nota 是把 0x12121212 --->"18.18.18.18" 也就是将网络地址转换成“.”点隔的字符串格式
    17. if(host_entry){
    18. return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);//由于h_addr_list可能有几个ip地址,这边取第一个,因此加*,然后强转成in_addr*,由于inet_ntoa为值传递,因此还要加*
    19. }
    20. return NULL;
    21. }
    22. int http_create_socket(char* ip){
    23. int sockfd=socket(AF_INET,SOCK_STREAM,0);//sockfd的结果就是int类型,是一个文件句柄。TCP要用SOCK_STREAM,UDP用SOCK_DGRAM
    24. struct sockaddr_in sin={0};
    25. sin.sin_family=AF_INET;
    26. sin.sin_port=htons(80);//http协议端口为80
    27. sin.sin_addr.s_addr=inet_addr(ip);//"字符串的ip"地址转为uint32_t和上面的inet_ntoa正好相反
    28. // int ret=connect(sockfd,(sockaddr*)&sin,sizeof(sockaddr_in));
    29. // if(ret!=0) return -1;
    30. if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
    31. return -1;
    32. }
    33. fcntl(sockfd,F_SETFL,O_NONBLOCK);//设置为非阻塞(如果socket是阻塞,read(),会阻塞挂起。如果socket是非阻塞,read()也是立刻返回)(设置文件状态标志用F_GETFL,读取文件状态标志用F_SETFL)
    34. return sockfd;
    35. }
    36. //hostname:github.com
    37. //resource:/wangbojing
    38. char* http_send_request(const char* hostname,const char* resource){
    39. char* ip=host_to_ip(hostname);
    40. int sockfd=http_create_socket(ip);
    41. char buffer[BUFFER_SIZE]={0};
    42. sprintf(buffer,
    43. "GET %s %s\r\n\
    44. Host:%s\r\n\
    45. %s\r\n\
    46. \r\n",
    47. resource,HTTP_VERSION,
    48. hostname,
    49. CONNECTION_TYPE
    50. );
    51. send(sockfd,buffer,strlen(buffer),0);
    52. // recv()//因为是非阻塞状态,如果用recv会快速过去
    53. //select
    54. fd_set fdread;
    55. FD_ZERO(&fdread);//用来清空fd_set集合,即让fd_set集合不再包含任何文件句柄
    56. FD_SET(sockfd,&fdread);//将sockfd置为检测的状态(用来将一个给定的文件描述符加入集合之中)
    57. struct timeval tv;
    58. tv.tv_sec=5;//5秒间隔 select采集一次
    59. tv.tv_usec=0;
    60. char* result=(char*)malloc(sizeof(int));
    61. memset(result,0,sizeof(int));
    62. while(1){
    63. int selection=select(sockfd+1,&fdread,NULL,NULL,&tv);//第一个参数为所有文件描述符的最大值+1 用select去监测,这个io里面有无数据
    64. if(!selection||!FD_ISSET(sockfd,&fdread)){//FD_ISSET判断描述符fd是否在给定的描述符集fdset中
    65. break;
    66. }else{
    67. memset(buffer,0,BUFFER_SIZE);
    68. int len=recv(sockfd,buffer,BUFFER_SIZE,0);//接受数据,放入buffer中
    69. if(len==0){//disconnect
    70. break;
    71. }
    72. result=(char*)realloc(result,(strlen(result)+len+1)*sizeof(char));//重新分配内存重建(+1是为了末尾'\0',不是最后一个的话会被覆盖)
    73. strncat(result,buffer,len);//把buffer数据copy到resulut中
    74. }
    75. }
    76. return result;
    77. }
    78. int main(int argc,char* argv[]){
    79. if(argc<3) return -1;
    80. char* response = http_send_request(argv[1],argv[2]);
    81. printf("response:%s\n",response);
    82. free(response);
    83. }

    结果

    请求的是百度的首页,所以只用了个斜杠

     

  • 相关阅读:
    软考笔记(3)——浮点数
    计算机网络重点概念整理-第五章 传输层【期末复习|考研复习】
    本地部署_语音识别工具_Whisper
    express在nodejs中的基本使用
    python实现长链矩阵的高效乘法
    SpringBoot@Profile详解
    Ansible自动化运维工具介绍与部署
    Leetcode1128. 等价多米诺骨牌对的数量
    项目管理主管岗位的主要职责概述
    2023.10.08
  • 原文地址:https://blog.csdn.net/weixin_45767431/article/details/128040530