Шпаргалка по HTTP-библиотекам для С++

  • Tutorial
К сожалению, в стандартной библиотеке языка С++ нет никаких средств для работы с протоколом HTTP. Возможно, в будущем появятся, но на данный момент каждый раз при необходимости дёрнуть какой-нибудь REST-сервис, пропарсить веб-страничку, написать простенького бота или краулера приходится задаваться вопросами «А какую же библиотеку взять, так чтобы побыстрее и попроще?». Иногда проект уже использует какой-то фреймворк (а иногда даже несколько) и тогда приходится вспоминать «А как же сделать HTTP-запрос имеющимися средствами?». Чтобы не путаться я решил написать для себя шпаргалку с примерами HTTP-запросов на С++ с применением разных библиотек. А самое удобное место для хранения подобных шпаргалок — Хабр: и сам не потеряешь, и другим может пригодиться.

Будут рассмотрены:
  • WinInet
  • WinHttp
  • Casablanca
  • Qt
  • POCO
  • wxWidgets
  • Boost.Asio
  • libcurl
  • neon
  • .NET (С++/CLI)
  • IXMLHTTPRequest
  • HappyHttp
  • cpp-netlib




WinInet


Сайт: http://msdn.microsoft.com/en-us/library/windows/desktop/aa385483(v=vs.85).aspx
Платформа: Windows 95 и выше
Пример использования
 #include <tchar.h>
    #include <wininet.h>  

    /// ....

    HINTERNET hIntSession = 
      ::InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);

    HINTERNET hHttpSession = 
      InternetConnect(hIntSession, _T("api.twitter.com"), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);

    HINTERNET hHttpRequest = HttpOpenRequest(
      hHttpSession, 
      _T("GET"), 
      _T("1/statuses/user_timeline.xml?screen_name=twitterapi"),
      0, 0, 0, INTERNET_FLAG_RELOAD, 0);

    TCHAR* szHeaders = _T("Content-Type: text/html\nMySpecialHeder: whatever");
    CHAR szReq[1024] = "";
    if( !HttpSendRequest(hHttpRequest, szHeaders, _tcslen(szHeaders), szReq, strlen(szReq))) {
      DWORD dwErr = GetLastError();
      /// handle error
    }

    CHAR szBuffer[1025];
    DWORD dwRead=0;
    while(::InternetReadFile(hHttpRequest, szBuffer, sizeof(szBuffer)-1, &dwRead) && dwRead) {
      szBuffer[dwRead] = 0;
      OutputDebugStringA(szBuffer);
      dwRead=0;
    }

    ::InternetCloseHandle(hHttpRequest);
    ::InternetCloseHandle(hHttpSession);
    ::InternetCloseHandle(hIntSession);



WinHttp


Сайт: http://msdn.microsoft.com/en-us/library/windows/desktop/aa382925(v=vs.85).aspx
Платформа: Windows 2000 и выше
Пример использования
DWORD dwSize = 0;
  DWORD dwDownloaded = 0;
  LPSTR pszOutBuffer;
  BOOL  bResults = FALSE;
  HINTERNET  hSession = NULL, 
             hConnect = NULL,
             hRequest = NULL;

  // Use WinHttpOpen to obtain a session handle.
  hSession = WinHttpOpen( L"WinHTTP Example/1.0",  
                          WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                          WINHTTP_NO_PROXY_NAME, 
                          WINHTTP_NO_PROXY_BYPASS, 0 );

  // Specify an HTTP server.
  if( hSession )
    hConnect = WinHttpConnect( hSession, L"www.microsoft.com",
                               INTERNET_DEFAULT_HTTPS_PORT, 0 );

  // Create an HTTP request handle.
  if( hConnect )
    hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
                                   NULL, WINHTTP_NO_REFERER, 
                                   WINHTTP_DEFAULT_ACCEPT_TYPES, 
                                   WINHTTP_FLAG_SECURE );

  // Send a request.
  if( hRequest )
    bResults = WinHttpSendRequest( hRequest,
                                   WINHTTP_NO_ADDITIONAL_HEADERS, 0,
                                   WINHTTP_NO_REQUEST_DATA, 0, 
                                   0, 0 );


  // End the request.
  if( bResults )
    bResults = WinHttpReceiveResponse( hRequest, NULL );

  // Keep checking for data until there is nothing left.
  if( bResults )
  {
    do 
    {
      // Check for available data.
      dwSize = 0;
      if( !WinHttpQueryDataAvailable( hRequest, &dwSize ) )
        printf( "Error %u in WinHttpQueryDataAvailable.\n",
                GetLastError( ) );

      // Allocate space for the buffer.
      pszOutBuffer = new char[dwSize+1];
      if( !pszOutBuffer )
      {
        printf( "Out of memory\n" );
        dwSize=0;
      }
      else
      {
        // Read the data.
        ZeroMemory( pszOutBuffer, dwSize+1 );

        if( !WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, 
                              dwSize, &dwDownloaded ) )
          printf( "Error %u in WinHttpReadData.\n", GetLastError( ) );
        else
          printf( "%s", pszOutBuffer );

        // Free the memory allocated to the buffer.
        delete [] pszOutBuffer;
      }
    } while( dwSize > 0 );
  }


  // Report any errors.
  if( !bResults )
    printf( "Error %d has occurred.\n", GetLastError( ) );

  // Close any open handles.
  if( hRequest ) WinHttpCloseHandle( hRequest );
  if( hConnect ) WinHttpCloseHandle( hConnect );
  if( hSession ) WinHttpCloseHandle( hSession );



Casablanca

Сайт: https://casablanca.codeplex.com
Платформа: все
Пример использования
http_client client(L"http://www.myhttpserver.com");
http_request request(methods::GET);
client.request(request).then([](http_response response)
    {
        // Perform actions here to inspect the HTTP response...
        if(response.status_code() == status_codes::OK)
        {
        }
    });



Qt

Сайт: http://qt-project.org
Платформа: все
Пример использования для Qt 4.x (уже устарел)
#include "handler.h"
 
Handler::Handler(QObject *parent) :QObject(parent)  {
    http = new QHttp(this);
    connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
    connect(http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(responseHeaderReceived(QHttpResponseHeader)));
    connect(http, SIGNAL(requestFinished(int,bool)), this, SLOT(requestFinished(int,bool)));
}
 
void Handler::doHttp()  {
    http->setHost("google.com");
    http->get("/");
}
 
void Handler::stateChanged(int state)   {
    switch(state)   {
    case 0:
        qDebug() << "Unconnected";
        break;
    case 1:
        qDebug() << "Host Lookup";
        break;
    case 2:
        qDebug() << "Connecting";
        break;
    case 3:
        qDebug() << "Sending";
        break;
    case 4:
        qDebug() << "Reading";
        break;
    case 5:
        qDebug() << "Connect";
        break;
    case 6:
        qDebug() << "Closing";
        break;
    }
}
 
void Handler::responseHeaderReceived(const QHttpResponseHeader &resp)   {
    qDebug() << "Size : " << resp.contentLength();
    qDebug() << "Type : " << resp.contentType();
    qDebug() << "Status Code : " << resp.statusCode();
}
 
void Handler::requestFinished(int id, bool error)   {
    qDebug() << "Request Id : " << id;
    if(error)   {
        qDebug() << "Error";
    }   else    {
        qDebug() << http->readAll();
    }
}

Пример использования для Qt 5.x

void Downloader::doDownload()
{
    manager = new QNetworkAccessManager(this);

    connect(manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(replyFinished(QNetworkReply*)));

    manager->get(QNetworkRequest(QUrl("http://google.com")));
}

void Downloader::replyFinished (QNetworkReply *reply)
{
   if(reply->error() != QNetworkReply::NoError)
    {
        qDebug() << "ERROR!";
        qDebug() << reply->errorString();
    }
    else
    {
        qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
        qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();
        qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
        qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();

        QFile file("C:/Qt/Dummy/downloaded.txt");
        if(file.open(QFile::Append))
        {
            file.write(reply->readAll());
        }
    }

    reply->deleteLater();
}



POCO

Сайт: http://pocoproject.org
Платформа: все
Пример использования
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>
#include <Poco/Path.h>
#include <Poco/URI.h>
#include <Poco/Exception.h>
#include <iostream>
#include <string>

using namespace Poco::Net;
using namespace Poco;
using namespace std;

int main(int argc, char **argv)
{
  if (argc != 2)
  {
    cout << "Usage: " << argv[0] << " <uri>" << endl;
    cout << "       fetches the resource identified by <uri> and print it" << endl;
    return -1;
  }

  try
  {
    // prepare session
    URI uri(argv[1]);
    HTTPClientSession session(uri.getHost(), uri.getPort());

    // prepare path
    string path(uri.getPathAndQuery());
    if (path.empty()) path = "/";

    // send request
    HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1);
    session.sendRequest(req);

    // get response
    HTTPResponse res;
    cout << res.getStatus() << " " << res.getReason() << endl;

    // print response
    istream &is = session.receiveResponse(res);
    StreamCopier::copyStream(is, cout);
  }
  catch (Exception &ex)
  {
    cerr << ex.displayText() << endl;
    return -1;
  }

  return 0;
}



wxWidgets

Сайт: http://www.wxwidgets.org
Платформа: все
Пример использования
#include <wx/sstream.h>
#include <wx/protocol/http.h>
 
wxHTTP get;
get.SetHeader(_T("Content-type"), _T("text/html; charset=utf-8"));
get.SetTimeout(10); // 10 seconds of timeout instead of 10 minutes ...
 
// this will wait until the user connects to the internet. It is important in case of dialup (or ADSL) connections
while (!get.Connect(_T("www.google.com")))  // only the server, no pages here yet ...
    wxSleep(5);
 
wxApp::IsMainLoopRunning(); // should return true
 
// use _T("/") for index.html, index.php, default.asp, etc.
wxInputStream *httpStream = get.GetInputStream(_T("/intl/en/about.html"));
 
// wxLogVerbose( wxString(_T(" GetInputStream: ")) << get.GetResponse() << _T("-") << ((resStream)? _T("OK ") : _T("FAILURE ")) << get.GetError() );
 
if (get.GetError() == wxPROTO_NOERR)
{
    wxString res;
    wxStringOutputStream out_stream(&res);
    httpStream->Read(out_stream);
 
    wxMessageBox(res);
    // wxLogVerbose( wxString(_T(" returned document length: ")) << res.Length() );
}
else
{
    wxMessageBox(_T("Unable to connect!"));
}
 
wxDELETE(httpStream);
get.Close();



Boost.Asio

Сайт: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio.html
Платформа: все
Пример использования
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 3)
    {
      std::cout << "Usage: sync_client <server> <path>\n";
      std::cout << "Example:\n";
      std::cout << "  sync_client www.boost.org /LICENSE_1_0.txt\n";
      return 1;
    }

    boost::asio::io_service io_service;

    // Get a list of endpoints corresponding to the server name.
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(argv[1], "http");
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    // Try each endpoint until we successfully establish a connection.
    tcp::socket socket(io_service);
    boost::asio::connect(socket, endpoint_iterator);

    // Form the request. We specify the "Connection: close" header so that the
    // server will close the socket after transmitting the response. This will
    // allow us to treat all data up until the EOF as the content.
    boost::asio::streambuf request;
    std::ostream request_stream(&request);
    request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";
    request_stream << "Host: " << argv[1] << "\r\n";
    request_stream << "Accept: */*\r\n";
    request_stream << "Connection: close\r\n\r\n";

    // Send the request.
    boost::asio::write(socket, request);

    // Read the response status line. The response streambuf will automatically
    // grow to accommodate the entire line. The growth may be limited by passing
    // a maximum size to the streambuf constructor.
    boost::asio::streambuf response;
    boost::asio::read_until(socket, response, "\r\n");

    // Check that response is OK.
    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;
    unsigned int status_code;
    response_stream >> status_code;
    std::string status_message;
    std::getline(response_stream, status_message);
    if (!response_stream || http_version.substr(0, 5) != "HTTP/")
    {
      std::cout << "Invalid response\n";
      return 1;
    }
    if (status_code != 200)
    {
      std::cout << "Response returned with status code " << status_code << "\n";
      return 1;
    }

    // Read the response headers, which are terminated by a blank line.
    boost::asio::read_until(socket, response, "\r\n\r\n");

    // Process the response headers.
    std::string header;
    while (std::getline(response_stream, header) && header != "\r")
      std::cout << header << "\n";
    std::cout << "\n";

    // Write whatever content we already have to output.
    if (response.size() > 0)
      std::cout << &response;

    // Read until EOF, writing data to output as we go.
    boost::system::error_code error;
    while (boost::asio::read(socket, response,
          boost::asio::transfer_at_least(1), error))
      std::cout << &response;
    if (error != boost::asio::error::eof)
      throw boost::system::system_error(error);
  }
  catch (std::exception& e)
  {
    std::cout << "Exception: " << e.what() << "\n";
  }

  return 0;
}



libcurl

Сайт: http://curl.haxx.se/libcurl
Платформа: все
Пример использования
#include <stdio.h>
#include <curl/curl.h>
 
int main(void)
{
  CURL *curl;
  CURLcode res;
 
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
    /* example.com is redirected, so we tell libcurl to follow redirection */ 
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
 
    /* Perform the request, res will get the return code */ 
    res = curl_easy_perform(curl);
    /* Check for errors */ 
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
 
    /* always cleanup */ 
    curl_easy_cleanup(curl);
  }
  return 0;
}



neon

Сайт: http://www.webdav.org/neon
Платформа: все
Пример использования
#include <ne_session.h>
#include <ne_request.h>
#include <ne_utils.h>
#include <ne_uri.h>

int httpResponseReader(void *userdata, const char *buf, size_t len)
{
    string *str = (string *)userdata;
    str->append(buf, len);
    return 0;
}
 
int do_get(string host)
{
    ne_session *sess;
    ne_request *req;
    string response;
 
    ne_sock_init();
 
    sess = ne_session_create("http", host.c_str(), 80);
    ne_set_useragent(sess, "MyAgent/1.0");
 
    req = ne_request_create(sess, "GET", "/SomeURL/method?with=parameter&value=data");
    // if accepting only 2xx codes, use "ne_accept_2xx"
    ne_add_response_body_reader(req, ne_accept_always, httpResponseReader, &response);
 
    int result = ne_request_dispatch(req);
    int status = ne_get_status(req)->code;
 
    ne_request_destroy(req);
 
    string errorMessage = ne_get_error(sess);
    ne_session_destroy(sess);
 
    printf("result %d, status %d\n", result, status);
    cout << response << "\n";
 
    switch (result) {
    case NE_OK:
        break;
    case NE_CONNECT:
        throw ConnectionError(errorMessage);
    case NE_TIMEOUT:
        throw TimeOutError(errorMessage);
    case NE_AUTH:
        throw AuthenticationError(errorMessage);
    default:
        throw AnotherWebError(errorMessage);
    }
 
    return 0;
}



.NET

Сайт: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
Платформа: Windows XP и выше
Пример использования
#using <System.dll>

using namespace System;
using namespace System::Net;
using namespace System::Text;
using namespace System::IO;

// Specify the URL to receive the request.
int main()
{
   array<String^>^args = Environment::GetCommandLineArgs();
   HttpWebRequest^ request = dynamic_cast<HttpWebRequest^>(WebRequest::Create( args[ 1 ] ));

   // Set some reasonable limits on resources used by this request
   request->MaximumAutomaticRedirections = 4;
   request->MaximumResponseHeadersLength = 4;

   // Set credentials to use for this request.
   request->Credentials = CredentialCache::DefaultCredentials;
   HttpWebResponse^ response = dynamic_cast<HttpWebResponse^>(request->GetResponse());
   Console::WriteLine( "Content length is {0}", response->ContentLength );
   Console::WriteLine( "Content type is {0}", response->ContentType );

   // Get the stream associated with the response.
   Stream^ receiveStream = response->GetResponseStream();

   // Pipes the stream to a higher level stream reader with the required encoding format. 
   StreamReader^ readStream = gcnew StreamReader( receiveStream,Encoding::UTF8 );
   Console::WriteLine( "Response stream received." );
   Console::WriteLine( readStream->ReadToEnd() );
   response->Close();
   readStream->Close();
}



IXMLHTTPRequest

Сайт: http://msdn.microsoft.com/en-us/library/ms759148(v=vs.85).aspx
Платформа: Windows XP и выше
Пример использования
#include <atlbase.h>
#include <msxml6.h>

HRESULT hr;
CComPtr<IXMLHTTPRequest> request;

hr = request.CoCreateInstance(CLSID_XMLHTTP60);
hr = request->open(
    _bstr_t("GET"),
    _bstr_t("https://www.google.com/images/srpr/logo11w.png"),
    _variant_t(VARIANT_FALSE),
    _variant_t(),
    _variant_t());
hr = request->send(_variant_t());

// get status - 200 if succuss
long status;
hr = request->get_status(&status);

// load image data (if url points to an image)
VARIANT responseVariant;
hr = request->get_responseStream(&responseVariant);
IStream* stream = (IStream*)responseVariant.punkVal;
CImage *image = new CImage();
image->Load(stream);
stream->Release();



HappyHttp

Сайт: http://scumways.com/happyhttp/happyhttp.html
Платформа: все
Пример использования
static int count=0;

// invoked when response headers have been received
void OnBegin( const happyhttp::Response* r, void* userdata )
{
	printf( "BEGIN (%d %s)\n", r->getstatus(), r->getreason() );
	count = 0;
}

// invoked to process response body data (may be called multiple times)
void OnData( const happyhttp::Response* r, void* userdata, const unsigned char* data, int n )
{
	fwrite( data,1,n, stdout );
	count += n;
}

// invoked when response is complete
void OnComplete( const happyhttp::Response* r, void* userdata )
{
	printf( "COMPLETE (%d bytes)\n", count );
}


void TestGET()
{
	happyhttp::Connection conn( "www.scumways.com", 80 );
	conn.setcallbacks( OnBegin, OnData, OnComplete, 0 );

	conn.request( "GET", "/happyhttp/test.php" );

	while( conn.outstanding() )
		conn.pump();
}



cpp-netlib

Сайт: http://cpp-netlib.org
Платформа: все
Пример использования
using namespace boost::network;
using namespace boost::network::http;

client::request request_("http://127.0.0.1:8000/");
request_ << header("Connection", "close");
client client_;
client::response response_ = client_.get(request_);
std::string body_ = body(response_);



Так, скажи уже в конце концов, что использовать!


Хотите проверенной годами классики — берите libcurl. Пишете приложение с визуальным интерфейсом — берите Qt. Хотите писать на С++11 — берите Casablanca. Пишете под .NET — используйте стандартные средства платформы. Пишете что-то без интерфейса и кроме HTTP-клиента хотите вообще иметь разные удобные инструменты — Boost или POCO.
Инфопульс Украина 223,83
Creating Value, Delivering Excellence
Поделиться публикацией
Комментарии 38
  • +5
    Полностью согласен с вами на счёт libcurl!
    • +2
      На Qt, если совсем по простому, то лучше использовать QNetworkAccessManager.
      • 0
        А не могли бы привести пример кода? Мне в реальном проекте показалось что с QNetworkAccessManager все получается сложнее и развесистее.
        • +1
          Странно, как по мне как раз наоборот. Пример из документации для GET:

          QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); manager->get(QNetworkRequest(QUrl("http://qt-project.org")));

          Или, тут когда-то давно писал про POST.
          • –2
            Сформировать запрос несложно и через QTcpSocket, а распарсить ответ?
            • +1
              А в чем проблема? QNetworkReply является QIODevice, т.е. нет никакой проблемы скормить его тому же QJson. Конечно, пример выше сильно упрощен и на реальном проекте скорей всего понадобятся подписки на сигналы QNetworkReply для того чтобы, например, читать данные как только они готовы, следить за процессом и т.п.
              • –1
                Не совсем понятно какие тотальные преимущества по сравнению с низкоуровневым QTcpSocket? Тот тоже QIODevice.
                • 0
                  Как я понимаю, встроенная асинхронность:
                  QNetworkAccessManager has an asynchronous API. When the replyFinished slot above is called, the parameter it takes is the QNetworkReply object containing the downloaded data as well as meta-data (headers, etc.).
                  • 0
                    QTcpSocket тоже асинхронен «из коробки».
                    Правда QNetworkReply хотя бы парсит ответ на заголовок и тело, что уже плюс.
                    • +1
                      QTcpSocket тоже асинхронен «из коробки».

                      Что-то в документации по этому поводу ни слова.
                      Сори нашел.
                  • +1
                    QNetworkAccessManager — высокоуровневая обертка над сокетами. Я думаю, что в большинстве случаев, будет достаточно этого класса, в котором многие вещи уже сделаны за нас, например, уведомление о прогрессе. Я понимаю, что можно сделать самому, но гораздо проще связать прогресс бар с этим сигналом и забыть. То же касается многих вещей по мелочи, вроде авторизации, куков и т.п.

                    Пример из статьи переписанный с QNetworkAccessManager, так как считаю, что пример из статьи стоит убрать из-за неактуальности. Было бы отлично, если бы Вы переписали этот пример с сокетами, для полноты картины :-).

                    Handler::Handler(QObject *parent) : QObject(parent)
                    {
                        manager = new QNetworkAccessManager(this);
                    }
                    void Handler::doHttp()
                    {
                        QNetworkRequest request;
                        request.setUrl(QUrl("google.com"));
                        QNetworkReply *reply = manager->get(request);
                        connect(reply, SIGNAL(finished())this, SLOT(requestFinished()));
                        connect(reply, SIGNAL(error(QNetworkReply::NetworkError))this, SLOT(requestError(QNetworkReply::NetworkError)));
                    }
                    void Handler::requestFinished()
                    {
                        QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
                        QByteArray bytes = reply->readAll();
                        QString stringData(bytes);
                        qDebug() << stringData;
                        reply->deleteLater();
                    }
                    void Handler::requestError(QNetworkReply::NetworkError error)
                    {
                    }
                    • +1
                      Было бы отлично, если бы Вы переписали этот пример с сокетами

                      Пожалуйста. Нашел небольшой примерчик в каком-то старом проекте.

                      Обертка над сокетом:
                      RemoteRequest::RemoteRequest(const QString &request, QObject *receiver, const char * slot):
                          QObject(0)
                          m_request(request)
                      {
                          m_socket = new QTcpSocket(this);
                          connect(m_socket, SIGNAL(connected()), this, SLOT(connected()));
                          connect(m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
                          if(receiver)
                              connect(this, SIGNAL(dataReceived(QByteArray)), receiver, slot);
                          m_socket->connectToHost(Settings::server(), Settings::port());
                      }
                      
                      void RemoteRequest::connected()
                      {
                          m_socket->write("GET " + m_request.toUtf8());
                      }
                      
                      void RemoteRequest::readyRead()
                      {
                          QByteArray result = m_socket->readAll();
                          emit dataReceived(result);
                      }
                      

                      и вызов
                        new RemoteRequest("/index.html", this, SLOT(dataReceived(QByteArray)));
                      
          • 0
            В QNetworkAccessManager есть ограничения на настройку количества подключений.
            Используются дефолтные (для HTTP) 4 одновременные подключения к серверу, и если нужна строгая последовательность ответов то ничего настроить невозможно.
            А так же невозможно забиндить конкретный сетевой адаптер для использования нескольких «интернетов» параллельно.
          • +3
            Неплохо было бы пометить, какие из них C, а какие именно C++, для наглядности.
            • 0
              Qt
              В довольно старой уже версии 4.х QHttp уже назывался deprecated и не рекомендовался к использованию. В современной 5.х его выпилили совсем.
              • +2
                Может быть стоит пример в статье пометить как Qt 4.x и добавить пример с QNetworkAccessManager с пометкой Qt 5.x?
                • 0
                  Однозначно. И Qt 4.x под спойлер и указание как устарвший, чтобы не смущать новичков
                  • +2
                    Ок, сделано.
                    • +1
                      QNetworkAccessManager появился в Qt 4.4, и подозреваю, ваш код «для Qt5» будет прекрасно работать и в Qt4 (хотя, возможно, что-то по мелочи понадобится изменить). А QHttp стал deprecated аж в Qt 4.6, т.е. 4 с лишним года назад, и сейчас упоминать эту древность нет смысла. Рекомендую этот пример удалить вообще.

                      Также есть несколько замечаний по примеру для Qt5:

                      > if(reply->error())

                      Правильно:
                      if(reply->error() != QNetworkReply::NoError)
                      


                      QNetworkReply::error возвращает значение enum, а не boolean, и полагаться на численные значения элементов enum — дурной тон.

                      > qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
                      >…

                      QNetworkReply::header возвращает QVariant, который можно напрямую передать в qDebug() без дополнительных преобразований. Если вы хотели показать, как преобразовать QVariant в другой тип, лучше сделать присвоение к переменной этого типа. В текущем виде это не имеет особого смысла.

                      > QFile *file = new QFile(«C:/Qt/Dummy/downloaded.txt»);

                      Удобнее и безопаснее создавать файл в виде
                      QFile file("C:/Qt/Dummy/downloaded.txt"); 
                      

                      При этом отпадает необходимость вызова delete. См. официальный пример.

                      > file->flush();
                      > file->close();

                      flush не нужен, так как он делается автоматически при вызове close. А close не нужен, так как он делается автоматически при удалении объекта.

                      Если эти недостатки устранить, пример станет короче и понятней.
                      • +1
                        reply->error() и и QFile — исправил. А toString() оставил, поскольку основная задача при разборе ответа — не передать заголовок в виде QVariant кому-то, кто умеет с ним что-то сделать, а всё-таки получить заголовок в виде строки и дальше заниматься её анализом.
              • +4
                Стоит сделать акцент в теме на то, что это библиотеки http-клиентов. Ну и да, реквестирую подобный пост про библиотеки, реализующие сервера.
                • 0
                  В принципе, многие библиотеки из статьи дают возможность написать и сервер (POCO, boost, Casablanca, возможно и другие). Я когда-то писал о веб-сервере на POCO, а с остальным в качестве сервера не работал.
                  • 0
                    Ага, когда самим потребовалось, было сделано на boost.asio. Т.е. что написать можно — это понятно, но, как минимум упираемся в парсер, обработку разных ответов, авторизацию и т.п., что достаточно рутинно.
                  • +2
                    Рекомендую mongoose. Кросплатформенная, на C, легковесная (пара файлов, 5к строк). Правда GPL.
                  • +2
                    Еще бы табличку со сравнением, например, асинхронности, потокобезопасности, поддержки SSL, зависимостей, расширяемости, лицензий.
                    • 0
                      Спасибо за обзор! Обязательно вернусь к нему, когда придёт время выбора библиотечки.
                      • +1
                        Можно в Вашу коллекцию еще и libevent добавить. В нем есть неплохая поддержка HTTP как для разработки серверной части, так и для клиента.
                        • 0
                          Спасибо, однозначно в избранное. Страшно подумать, сколько бы у меня заняло времени собственное исследование вроде этого. А здесь просмотрел примеры за две минуты и уже составил впечатление.
                          • 0
                            Для embedded (размер ipk для openwrt):
                            libcurl90 Kb
                            libmicrohttpd20 Kb
                            • 0
                              А в какой конфигурации libcurl? По умолчанию в ней много протоколов, лишние можно отключить через макроопределения.
                            • 0
                              ---WinInet

                              у майкрософта есть еще winhttp

                              msdn.microsoft.com/en-us/library/windows/desktop/aa382925%28v=vs.85%29.aspx
                              • 0
                                Более того, WinInet не рекоммендуется (написано, в частности, по ссылке выше) для использования в сервереных средах ( и есть реальный опыт проблем с ним)
                                • 0
                                  Ага, а для не-серверных как-раз рекомендуют WinInet. В общем, я добавил WinHttp в статью, пусть будет, не помешает.
                                  • 0
                                    А где рекомендуют WinInet?
                                    Мне казалось, что WinHttp сейчас для всех случаев его заменяет.
                                    • 0
                                      Ну вот: «With a few exceptions, WinINet is a superset of WinHTTP. When selecting between the two, you should use WinINet, unless you plan to run within a service or service-like process that requires impersonation and session isolation.»
                            • 0
                              Boost.asio сам по себе http не поддерживает, зато с его применением написан pion. Использовал эту библиотеку в проекте, где требовалось реализовать свой протокол поверх http(s), она оказалось очень удобной.

                              Хотя если требуется только пару страничек загрузить, libcurl будет гораздо проще.
                              • 0
                                отличная шпаргалка. Сам долгое время мучился с портированием libcurl под MarmaladeSDK, так и не, смог привести порт в стабильное состояние. Специфика SDK требовала строгой однотредности и асинхронности, терпимости к реализации socket api. В этом смысле из списка привлекателен HappyHttp, но его декларация асинхронности не соответствует действительности: gethostbyname не асинхронен.

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое