如何使一个HTTP使用C get请求没有libcurl的?如何使一个HTTP使用C get请求没有l

2019-06-14 05:47发布

我想写一个C程序来生成一个GET请求,而无需使用任何外部库。 仅使用C库,使用套接字这可能吗? 我想制作一个HTTP数据包(使用正确的格式),并将其发送到服务器。 这是唯一可能的方式还是有更好的办法?

Answer 1:

使用BSD套接字或者,如果你是比较有限的,说你有一些RTOS,一些简单的TCP协议栈,LWIP的一样,可以形成GET / POST请求。

有许多的开源实现。 参见“happyhttp”作为样品( http://scumways.com/happyhttp/happyhttp.html )。 我知道,这是C ++,不C,但是这是唯一的“C ++ - 依赖”有一个字符串/阵列管理,所以很容易移植到纯C.

当心,有没有“三包”,因为HTTP通常转移通过TCP连接,所以在技术上,只有在RFC格式符号流。 由于HTTP请求的连接,发送断开的方式通常做,一个实际上可能称之为“包”。

基本上,一旦你有一个开放的插座(的sockfd)“的全部”你必须做的是一样的东西

char sendline[MAXLINE + 1], recvline[MAXLINE + 1];
char* ptr;

size_t n;

/// Form request
snprintf(sendline, MAXSUB, 
     "GET %s HTTP/1.0\r\n"  // POST or GET, both tested and works. Both HTTP 1.0 HTTP 1.1 works, but sometimes 
     "Host: %s\r\n"     // but sometimes HTTP 1.0 works better in localhost type
     "Content-type: application/x-www-form-urlencoded\r\n"
     "Content-length: %d\r\n\r\n"
     "%s\r\n", page, host, (unsigned int)strlen(poststr), poststr);

/// Write the request
if (write(sockfd, sendline, strlen(sendline))>= 0) 
{
    /// Read the response
    while ((n = read(sockfd, recvline, MAXLINE)) > 0) 
    {
        recvline[n] = '\0';

        if(fputs(recvline,stdout) == EOF) { cout << ("fputs erros"); }
        /// Remove the trailing chars
        ptr = strstr(recvline, "\r\n\r\n");

        // check len for OutResponse here ?
        snprintf(OutResponse, MAXRESPONSE,"%s", ptr);
    }          
}


Answer 2:

POSIX 7最小可运行示例

让我们获取http://example.com 。

wget.c

#define _XOPEN_SOURCE 700
#include <arpa/inet.h>
#include <assert.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char** argv) {
    char buffer[BUFSIZ];
    enum CONSTEXPR { MAX_REQUEST_LEN = 1024};
    char request[MAX_REQUEST_LEN];
    char request_template[] = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n";
    struct protoent *protoent;
    char *hostname = "example.com";
    in_addr_t in_addr;
    int request_len;
    int socket_file_descriptor;
    ssize_t nbytes_total, nbytes_last;
    struct hostent *hostent;
    struct sockaddr_in sockaddr_in;
    unsigned short server_port = 80;

    if (argc > 1)
        hostname = argv[1];
    if (argc > 2)
        server_port = strtoul(argv[2], NULL, 10);

    request_len = snprintf(request, MAX_REQUEST_LEN, request_template, hostname);
    if (request_len >= MAX_REQUEST_LEN) {
        fprintf(stderr, "request length large: %d\n", request_len);
        exit(EXIT_FAILURE);
    }

    /* Build the socket. */
    protoent = getprotobyname("tcp");
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }
    socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
    if (socket_file_descriptor == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Build the address. */
    hostent = gethostbyname(hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname);
        exit(EXIT_FAILURE);
    }
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
    if (in_addr == (in_addr_t)-1) {
        fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
        exit(EXIT_FAILURE);
    }
    sockaddr_in.sin_addr.s_addr = in_addr;
    sockaddr_in.sin_family = AF_INET;
    sockaddr_in.sin_port = htons(server_port);

    /* Actually connect. */
    if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
        perror("connect");
        exit(EXIT_FAILURE);
    }

    /* Send HTTP request. */
    nbytes_total = 0;
    while (nbytes_total < request_len) {
        nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total);
        if (nbytes_last == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }
        nbytes_total += nbytes_last;
    }

    /* Read the response. */
    fprintf(stderr, "debug: before first read\n");
    while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) {
        fprintf(stderr, "debug: after a read\n");
        write(STDOUT_FILENO, buffer, nbytes_total);
    }
    fprintf(stderr, "debug: after last read\n");
    if (nbytes_total == -1) {
        perror("read");
        exit(EXIT_FAILURE);
    }

    close(socket_file_descriptor);
    exit(EXIT_SUCCESS);
}

GitHub的上游 。

编译:

gcc -ggdb3 -std=c99 -Wall -Wextra -o wget wget.c

获取http://example.com并输出到stdout:

./wget example.com

此命令挂起大多数服务器,直到超时,而且预计:

  • 无论是服务器还是客户端必须关闭连接
  • 我们(客户端)都没有这样做
  • 大部分HTTP服务器保持连接打开,直到超时期待着更进一步的要求,例如JavaScript,CSS和图像下面的HTML页面
  • 我们可以解析响应,并关闭在读的内容长度的字节,但我们没有为简单起见。 什么HTTP响应头是必需的说,如果Content-Length不发送,服务器可以关闭,以确定长度。

连接部分还与IP:

host example.com

得到:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

所以我们做的:

./wget 93.184.216.34

然而,得到的答复是错误的,因为我们没有设置Host:在我们的程序正确,那就是在HTTP 1.1要求 。

经测试在Ubuntu 18.04。

服务器实例

  • 最小POSIX C例程: 发送和使用C接收在套接字编程一个文件在Linux中/ C ++(GCC / G ++)
  • 最小Android的Java示例: 如何在Android的Socket连接?


Answer 3:

“没有任何外部库”严格来说将排除的libc一样,所以你必须写所有的系统调用自己。 我怀疑你的意思是说严格的,虽然。 如果你不希望链接到另一个库,并且不希望从其他图书馆复制源代码到你的应用程序,然后直接处理使用套接字API是您最好的办法的TCP流。

创建HTTP请求发送过来一个TCP套接字连接是容易的,因为正在读答案。 它的解析这将是真正的麻烦,特别是如果你的目标是支持该标准的一个相当大的部分答案。 例如错误页面,重定向,内容协商等事情可以让我们的生活很辛苦,如果你在跟谁说话任意web服务器。 如果在另一方面,已知的服务器中运行良好,和一个简单的错误消息是所有适合任何意外的服务器的响应,则认为是相当简单的为好。



文章来源: How to make an HTTP get request in C without libcurl?