Отправка GET/POST-запроса средствами чистого C++

102
10 декабря 2019, 23:20

Как реализовать простой GET/POST запрос без использования сторонних библиотек?

Нашел много примеров, но там либо cpp-netlib, либо curl, а мне бы хотелось самому все это написать, но с сетью в С++ не работал, только через Qt. Было бы очень здорово увидеть маленький работающий пример :)

Answer 1

На "чистом C++" много чего нельзя сделать, потому что стандартная библиотека языка слишком миниатюрная, она не включает достаточно функционала, чтобы работать с HTTP.

В какой-то мере "полустандартной" библиотекой является boost (какие-то его библиотеки позже становятся частью стандартной библиотеки), поэтому можно воспользоваться boost::asio. Однако учтите, что это всё равно достаточно низкоуровневая библиотека. Простой HTTP-запрос вы сможете написать в пару строчек, но вы будете вручную разруливать все сотни современных возможностей HTTP: безопасное соединение, сжатие траффика, кэширование и так далее и тому подобное. Я уж молчу про новую версию стандарта. И если вы можете сказать серверу "я не поддерживаю сжатие, давай данные как есть", то проигнорировать HTTPS вы ну никак не сможете.

Вы изобретёте велосипед, который никому не будет нужен. Вы станете лучше понимать потроха HTTP, конечно, но кроме как для обучения смысла писать такой код нет.

Если вам нужны маленькие и работающие примеры, то посмотрите Debunking Stroustrup's debunking of the myth “C++ is for large, complicated, programs only” — они там есть на всех языках, в том числе несколько на C++, а один из примеров написан самим Страуструпом.

Answer 2

Я решил проблему так:

#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <vector>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <sstream>
char http[]={"http://"};
char host[]={"localhost"};
char parm[]={"/api/method.php"};

std::string getProtocol( std::string url )
{
    std::string protocol = "";
    int i = 0;
    for(i = 0; i < url.size(); i++)
    {
        if( url[i] != '/' || url[i+1] != '/'  )
        {
            protocol += url[i];
        }
        else
        {
            protocol += "//";
            break;
        }
    }
    return protocol;
}
std::string getHost( std::string url )
{
    std::string host = "";
    url.replace(0, getProtocol(url).size(), "");
    int i = 0;
    for(i = 0; i < url.size(); i++)
    {
        if( url[i] != '/' )
        {
            host += url[i];
        }
        else
        {
            break;
        }
    }
    return host;
}
std::string getAction( std::string url )
{
    std::string parm = "";
    url.replace(0, getProtocol(url).size()+getHost(url).size(), "");
    int i = 0;
    for(i = 0; i < url.size(); i++)
    {
        if( url[i] != '?' && url[i] != '#' )
        {
            parm += url[i];
        }
        else
        {
            break;
        }
    }
    return parm;
}
std::string getParams( std::vector< std::pair< std::string, std::string> > requestData )
{
    std::string parm = "";
    std::vector< std::pair< std::string, std::string> >::iterator itr = requestData.begin();
    for( ; itr != requestData.end(); ++itr )
    {
        if( parm.size() < 1 )
        {
            parm += "";
        }
        else
        {
            parm += "&";
        }
        parm += itr->first + "=" + itr->second;
    }
    return parm;
}

std::string GET( std::string url, std::vector< std::pair< std::string, std::string> > requestData )
{
    std::string http = getProtocol(url);
    std::string host = getHost(url);
    std::string script = getAction(url);
    std::string parm = getParams( requestData );
    char buf[1024];
    std::string header = "";
    header += "GET ";
    header += http + host + script + "?" + parm;
    header += (std::string)" HTTP/1.1" + "\r\n";
    header += (std::string)"Host: " + http + host + "/" + "\r\n";
    header += (std::string)"User-Agent: Mozilla/5.0" + "\r\n";
    //header += (std::string)"Accept: text/html" + "\r\n";
    header += (std::string)"Accept-Language: ru,en-us;q=0.7,en;q=0.3" + "\r\n";
    header += (std::string)"Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7" + "\r\n";
    header += (std::string)"Connection: keep-alive " + "\r\n";
    header += "\r\n";

    int sock;
    struct sockaddr_in addr;
    struct hostent* raw_host;
    raw_host = gethostbyname( host.c_str() );
    if (raw_host == NULL)
    {
        std::cout<<"ERROR, no such host";
        exit(0);
    }
    sock = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(80);
    bcopy( (char*)raw_host->h_addr, (char*)&addr.sin_addr, raw_host->h_length );
    if( connect( sock, (struct sockaddr *)&addr, sizeof(addr) ) < 0)
    {
        std::cerr<<"connect error"<<std::endl;
        exit(2);
    }

    char * message = new char[ header.size() ];
    for(int i = 0; i < header.size(); i++)
    {
        message[i] = header[i];
    }
    send(sock, message, header.size(), 0);
    recv(sock, buf, sizeof(buf), 0);
    std::string answer = "";
    for(int j = 0; j < 1024; j++)
    {
            answer += buf[j];
    }
    return answer;
}
Answer 3

На данный момент в стандарте С++ нет средств для работы с сетью.

При помощи библиотеки Boost.Asio (которую собираются включить в стандарт) это можно сделать буквально тремя строчками кода:

#define _WIN32_WINNT 0x0A00
#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB
#include <boost/asio.hpp>
#include <iostream>
int main() {
  boost::asio::ip::tcp::iostream stream("httpbin.org", "http");
  stream << "GET /ip HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n";
  std::cout << stream.rdbuf();
}

С использованием Boost.Beast:

#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <iostream>
int main() {
  boost::asio::io_service io_service;
  boost::asio::ip::tcp::resolver resolver(io_service);
  boost::asio::ip::tcp::resolver::query query("httpbin.org", "http");
  boost::asio::ip::tcp::socket socket(io_service);
  boost::asio::connect(socket, resolver.resolve(query));
  boost::beast::http::request<boost::beast::http::string_body> req(
      boost::beast::http::verb::get, "/headers", 11);
  req.set(boost::beast::http::field::host, "httpbin.org");
  req.set(boost::beast::http::field::user_agent, "MyAgent/1");
  req.set(boost::beast::http::field::connection, "Close");
  write(socket, req);
  boost::beast::flat_buffer buffer;
  boost::beast::http::response<boost::beast::http::string_body> reply;
  boost::beast::http::read(socket, buffer, reply);
  std::cout << reply.body();
}
Answer 4

Средствами чистого сиплюсплюса можно написать HTTP запрос, используя только iostream. А программу запускать, присоединив streams к netcat.

unix-way в чистом виде, если что.

Answer 5

Можно на чистом с++ это сделать стандартными библиотеками #include <sys/socket.h> или # include <winsock2.h>. Конечно не ручаюсь, насколько это работает сейчас, но раньше на сокетах как раз можно было написать вполне вменяемы TCP_IP запросы. По первой либе здесь можно посмотреть пример

READ ALSO
Как сделать текст курсивным в Qt

Как сделать текст курсивным в Qt

В продолжении вопроса

129
Какой лучше выставить glOrtho для 3d?

Какой лучше выставить glOrtho для 3d?

Для 2d я выставлял glOrtho так, чтобы начало координат было от левого нижнего угла и размером в размер экранаА в 3d я помню как то читал старую книгу...

97
C++ вопрос по CIN

C++ вопрос по CIN

Есть маленькая прогаммка, все ничего, но столкнулся с одной странной трудностьюПриложен код простой менюшки, которая обёрнута в цикл do while и через...

144
Подключение к БД Qt

Подключение к БД Qt

Всем приветМожно ли как-то получить список доступных для подключения БД, а не явно задавать имя БД в методе setDatabaseName() ? Грубо говоря, чтобы...

133