使用 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 源码

原文地址:https://www.cnblogs.com/chorm590/p/c_http_get_201905081356.html