zoukankan      html  css  js  c++  java
  • 使用 C 语言实现一个 HTTP GET 连接

    2019-05-08

    关键字:HTTP 连接、C语言访问网络


    如果您比较有耐心,建议从头至尾读完这篇文章。如果您只想快速应用 C 语言的 HTTP GET 连接功能,可以直接跳到文末拷贝源代码去使用

    1、HTTP 连接的流程

    HTTP 连接都是建立在 TCP 连接之上的。这里我们不讨论 TCP 的三次握手四次挥手过程。我们只单纯地来分析下一个 HTTP 连接的过程应该是怎样的。

    首先,我们需要创建一个 TCP 的 Socket 。后面我们的网络连接操作都是基于这个 Socket 来构建的。

    其次,我们需要来组装一下 HTTP 请求。就是封装一个 GET 请求,表明一下我们想要连接哪个服务器的哪些资源。当然,其实第 1 步和第 2 步的顺序并不重要。

    第三步,我们需要发送 HTTP GET 请求了,将前面封装好的请求信息通过前面创建好的 TCP 通道发送出去。

    第四步,读取服务端的返回结果。

    2、代码实操

    1、创建 Socket

        int sockfd;
         
         //创建套接字
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
            printf("socket failed!!!
    ");
            exit(0);
        }

    没什么好说的,照着做就好了。

    2、封装 HTTP 请求

        char request[512] = {0};
        memset(request, 0, 512);
        strcat(request, "GET ");
        strcat(request, "/index.html");
        strcat(request, " HTTP/1.1
    ");
        strcat(request, "Host: ");
        strcat(request, "192.168.221.30");
        strcat(request, "
    Content-Type: text/html
    ");
        strcat(request, "Content-Length: 0
    ");
        strcat(request, "
    ");

    上面加粗标灰底的部分分别是要访问的资源路径以及服务器地址。其中服务器地址并不是很重要,但是上面的资源路径一定不能错!

    3、发起 HTTP 请求

        struct sockaddr_in servaddr;
        int writeRet;
        
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(80);
        
        if (inet_pton(AF_INET, "192.168.221.30", &servaddr.sin_addr) <= 0 ){
            printf("inet_pton error!
    ");
            exit(0);
        }
    
        if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
            printf("connect error!
    ");
            exit(0);
        }
    
        writeRet = write(sockfd, request, strlen(request));
        if (writeRet < 0) {
            printf("make http error:%d,%s
    ", errno, strerror(errno));
            exit(0);
        }

    上面加粗标灰底的地址就很重要了,一定不能填错,而且只能填 IP 地址。那个部分的 inet_pton 函数的作用是将字符串形式的 IPV4 地址转换成二进制形式的。如果是 IPV6 地址,则要用 inet_ntop 函数。

    4、读取返回结果

        struct timeval tv;
        int selectRet = 0;
        fd_set t_set1;
        
        sleep(2);
        
        tv.tv_sec= 0;
        tv.tv_usec= 0;
    
        FD_ZERO(&t_set1);
        FD_SET(sockfd, &t_set1);
        
        selectRet = select(sockfd + 1, &t_set1, NULL, NULL, &tv);
    
        if (selectRet < 0) {
            close(sockfd);
            printf("select failed!
    ");
            return;
        }
    
        if (selectRet > 0){
            char buf[4096] = {0};
            int readLen = 0;
    
            memset(buf, 0, 4096);
            readLen = read(sockfd, buf, 4095); // read once only!
            printf("readLen:%d
    ", readLen);
            printf("
    
    %s
    
    ", buf);
        }

    上示代码最后会将读取的结果保存到 buf 数组中并打印出来。

    5、关闭 Socket

        close(sockfd);
        
        printf("Bye!
    ");

    最后,用完 HTTP 通信以后,一定不要忘记关闭刚才打开的资源。

    笔者这边通过 nginx 搭建了一个模拟服务器,与这份代码调试,一切正常。可以在控制台上得到如下回复

    hello world
    -------------
    GET /index.html HTTP/1.1
    Host: 192.168.221.30
    Content-Type: text/html
    Content-Length: 0
    
    
    >>>>>>> success with 90 byte(s) <<<<<<<
    readLen:850
    
    
    HTTP/1.1 200 OK
    Server: nginx/1.16.0
    Date: Wed, 08 May 2019 06:13:27 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 23 Apr 2019 13:09:07 GMT
    Connection: keep-alive
    ETag: "5cbf0e73-264"
    Accept-Ranges: bytes
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
             35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;
        }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
    
    Bye!
    测试结果

    再接下来,就是根据自己的实际需要,去做业务层的处理了。

    3、完整代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <string.h>
    #include <netinet/in.h>
    #include <errno.h>
    #include "http.h"
    
    void main()
    {
        
        printf("hello world
    ");
        
        // step 1 , create socket
        int sockfd;
         
         //创建套接字
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
            printf("socket failed!!!
    ");
            exit(0);
        }
        
        // step 2, package the http request
        char request[512] = {0};
        memset(request, 0, 512);
        strcat(request, "GET ");
        strcat(request, "/index.htm");
        strcat(request, " HTTP/1.1
    ");
        strcat(request, "Host: ");
        strcat(request, "www.baidu.com");
        strcat(request, "
    Content-Type: text/html
    ");
        strcat(request, "Content-Length: 0
    ");
        strcat(request, "
    ");
        printf("-------------
    %s
    ",request);
        
        
        // step 3, connect and send http request.
        struct sockaddr_in servaddr;
        int writeRet;
        
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(80);
        
        if (inet_pton(AF_INET, "192.168.221.30", &servaddr.sin_addr) <= 0 ){
            printf("inet_pton error!
    ");
            exit(0);
        }
    
        if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
            printf("connect error!
    ");
            exit(0);
        }
    
        writeRet = write(sockfd, request, strlen(request));
        if (writeRet < 0) {
            printf("make http error:%d,%s
    ", errno, strerror(errno));
            exit(0);
        }
    
        printf(">>>>>>> success with %d byte(s) <<<<<<<
    ", writeRet);
        
        
        // step 4, read response
        
        struct timeval tv;
        int selectRet = 0;
        fd_set t_set1;
        
        sleep(2);
        
        tv.tv_sec= 0;
        tv.tv_usec= 0;
    
        FD_ZERO(&t_set1);
        FD_SET(sockfd, &t_set1);
        
        selectRet = select(sockfd + 1, &t_set1, NULL, NULL, &tv);
    
        if (selectRet < 0) {
            close(sockfd);
            printf("select failed!
    ");
            return;
        }
    
        if (selectRet > 0){
            char buf[4096] = {0};
            int readLen = 0;
    
            memset(buf, 0, 4096);
            readLen = read(sockfd, buf, 4095); // read once only!
            printf("readLen:%d
    ", readLen);
            printf("
    
    %s
    
    ", buf);
        }
    
        close(sockfd);
        
        printf("Bye!
    ");
    }
    C语言的HTTP GET 源码

  • 相关阅读:
    普通锁和分布式锁
    java 正则表达式
    java 字符串转date 格式转换
    消息中间件 kafka
    数据的存储方式:对象存储、文件存储、块存储
    Exceptional Control Flow(6)
    Exceptional Control Flow(5)
    Exceptional Control Flow(4)
    Exceptional Control Flow(3)
    Exceptional Control Flow(2)
  • 原文地址:https://www.cnblogs.com/chorm590/p/c_http_get_201905081356.html
Copyright © 2011-2022 走看看