C ++ ile nasıl HTTP isteği yaparsınız?


258

C ++ ile kolayca bir HTTP isteği yapmanın bir yolu var mı? Özellikle, bir sayfanın (API) içeriğini indirmek ve 1 veya 0 içerip içermediğini görmek için içeriği kontrol etmek istiyorum. İçeriği bir dizeye indirmek de mümkün mü?


1
Hayır, şu anda dil veya ağ için standart kütüphane aracılığıyla yerleşik destek bulunmamaktadır. Ancak, bir Ağ TS N4370 var . Ayrıca kütüphane önerilerini çektiği için bu soruyu VTC yaptım.

BoostBeast ne dersiniz?
twocrush

Yanıtlar:


250

Ben de aynı problemi yaşadım. libcurl gerçekten tamamlandı. Bir C ++ kitaplığı istediğinde ilginizi çekebilecek bir C ++ sarıcı curlpp var. neon , WebDAV'ı da destekleyen bir başka ilginç C kütüphanesidir .

C ++ kullanıyorsanız curlpp doğal görünüyor. Kaynak dağıtımında birçok örnek verilmiştir. Bir URL'nin içeriğini almak için böyle bir şey yaparsınız (örneklerden çıkarılır):

// Edit : rewritten for cURLpp 0.7.3
// Note : namespace changed, was cURLpp in 0.7.2 ...

#include <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>

// RAII cleanup

curlpp::Cleanup myCleanup;

// Send request and get a result.
// Here I use a shortcut to get it in a string stream ...

std::ostringstream os;
os << curlpp::options::Url(std::string("http://www.wikipedia.org"));

string asAskedInQuestion = os.str();

Curlpp kaynak dağıtımındakiexamples dizine bakın, çok daha karmaşık vakaların yanı sıra curlpp kullanarak basit bir tam minimal bir tane var.

2 sentim ...


1
son sürümü mac altında kırık gibi görünüyor .. bir şey kütüphane olarak bağlandığında config.h ile berbat.
eugene

1
Yukarıdakileri derleyemedim. Ancak, değiştirilmesi os << myRequest.perform();ile myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();verdi sonuçların. Kullanmamaya dikkat edin http://example.com, bu boş bir sayfa döndürür. Daha iyi kullanım örn http://www.wikipedia.org.
Zane

4
MSVS'de curlpp'i nasıl oluşturuyorsunuz?
Çalışamıyorum

2
@ Ryan-sam'in son düzenlemesine katılmıyorum. Yazarın amacı, verilen kütüphane açıkça "HTTP ve WebDAV işlemleri" için yapıldığı için web geliştirme değil "webdav" yazmaktı.
Bostrot

2
@bostrot: Demek istediğim bu. Geri döndüm ve bir bağlantı ekledim, sanırım insanlar webdev yazdığımı düşündüler. Yazık :)
nöro

115

Windows kodu:

#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <locale>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")




int main( void ){

WSADATA wsaData;
SOCKET Socket;
SOCKADDR_IN SockAddr;
int lineCount=0;
int rowCount=0;
struct hostent *host;
locale local;
char buffer[10000];
int i = 0 ;
int nDataLength;
string website_HTML;

// website url
string url = "www.google.com";

//HTTP GET
string get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n";


    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
        cout << "WSAStartup failed.\n";
        system("pause");
        //return 1;
    }

    Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    host = gethostbyname(url.c_str());

    SockAddr.sin_port=htons(80);
    SockAddr.sin_family=AF_INET;
    SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);

    if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){
        cout << "Could not connect";
        system("pause");
        //return 1;
    }

    // send GET / HTTP
    send(Socket,get_http.c_str(), strlen(get_http.c_str()),0 );

    // recieve html
    while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){        
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){

            website_HTML+=buffer[i];
            i += 1;
        }               
    }

    closesocket(Socket);
    WSACleanup();

    // Display HTML source 
    cout<<website_HTML;

    // pause
    cout<<"\n\nPress ANY key to close.\n\n";
    cin.ignore(); cin.get(); 


 return 0;
}

İşte çok daha iyi bir uygulama:

#include <windows.h>
#include <string>
#include <stdio.h>

using std::string;

#pragma comment(lib,"ws2_32.lib")


HINSTANCE hInst;
WSADATA wsaData;
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);


int main()
{
    const int bufLen = 1024;
    char *szUrl = "http://stackoverflow.com";
    long fileSize;
    char *memBuffer, *headerBuffer;
    FILE *fp;

    memBuffer = headerBuffer = NULL;

    if ( WSAStartup(0x101, &wsaData) != 0)
        return -1;


    memBuffer = readUrl2(szUrl, fileSize, &headerBuffer);
    printf("returned from readUrl\n");
    printf("data returned:\n%s", memBuffer);
    if (fileSize != 0)
    {
        printf("Got some data\n");
        fp = fopen("downloaded.file", "wb");
        fwrite(memBuffer, 1, fileSize, fp);
        fclose(fp);
         delete(memBuffer);
        delete(headerBuffer);
    }

    WSACleanup();
    return 0;
}


void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename)
{
    string::size_type n;
    string url = mUrl;

    if (url.substr(0,7) == "http://")
        url.erase(0,7);

    if (url.substr(0,8) == "https://")
        url.erase(0,8);

    n = url.find('/');
    if (n != string::npos)
    {
        serverName = url.substr(0,n);
        filepath = url.substr(n);
        n = filepath.rfind('/');
        filename = filepath.substr(n+1);
    }

    else
    {
        serverName = url;
        filepath = "/";
        filename = "";
    }
}

SOCKET connectToServer(char *szServerName, WORD portNum)
{
    struct hostent *hp;
    unsigned int addr;
    struct sockaddr_in server;
    SOCKET conn;

    conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (conn == INVALID_SOCKET)
        return NULL;

    if(inet_addr(szServerName)==INADDR_NONE)
    {
        hp=gethostbyname(szServerName);
    }
    else
    {
        addr=inet_addr(szServerName);
        hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
    }

    if(hp==NULL)
    {
        closesocket(conn);
        return NULL;
    }

    server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
    server.sin_family=AF_INET;
    server.sin_port=htons(portNum);
    if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
    {
        closesocket(conn);
        return NULL;
    }
    return conn;
}

int getHeaderLength(char *content)
{
    const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r";
    char *findPos;
    int ofset = -1;

    findPos = strstr(content, srchStr1);
    if (findPos != NULL)
    {
        ofset = findPos - content;
        ofset += strlen(srchStr1);
    }

    else
    {
        findPos = strstr(content, srchStr2);
        if (findPos != NULL)
        {
            ofset = findPos - content;
            ofset += strlen(srchStr2);
        }
    }
    return ofset;
}

char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut)
{
    const int bufSize = 512;
    char readBuffer[bufSize], sendBuffer[bufSize], tmpBuffer[bufSize];
    char *tmpResult=NULL, *result;
    SOCKET conn;
    string server, filepath, filename;
    long totalBytesRead, thisReadSize, headerLen;

    mParseUrl(szUrl, server, filepath, filename);

    ///////////// step 1, connect //////////////////////
    conn = connectToServer((char*)server.c_str(), 80);

    ///////////// step 2, send GET request /////////////
    sprintf(tmpBuffer, "GET %s HTTP/1.0", filepath.c_str());
    strcpy(sendBuffer, tmpBuffer);
    strcat(sendBuffer, "\r\n");
    sprintf(tmpBuffer, "Host: %s", server.c_str());
    strcat(sendBuffer, tmpBuffer);
    strcat(sendBuffer, "\r\n");
    strcat(sendBuffer, "\r\n");
    send(conn, sendBuffer, strlen(sendBuffer), 0);

//    SetWindowText(edit3Hwnd, sendBuffer);
    printf("Buffer being sent:\n%s", sendBuffer);

    ///////////// step 3 - get received bytes ////////////////
    // Receive until the peer closes the connection
    totalBytesRead = 0;
    while(1)
    {
        memset(readBuffer, 0, bufSize);
        thisReadSize = recv (conn, readBuffer, bufSize, 0);

        if ( thisReadSize <= 0 )
            break;

        tmpResult = (char*)realloc(tmpResult, thisReadSize+totalBytesRead);

        memcpy(tmpResult+totalBytesRead, readBuffer, thisReadSize);
        totalBytesRead += thisReadSize;
    }

    headerLen = getHeaderLength(tmpResult);
    long contenLen = totalBytesRead-headerLen;
    result = new char[contenLen+1];
    memcpy(result, tmpResult+headerLen, contenLen);
    result[contenLen] = 0x0;
    char *myTmp;

    myTmp = new char[headerLen+1];
    strncpy(myTmp, tmpResult, headerLen);
    myTmp[headerLen] = NULL;
    delete(tmpResult);
    *headerOut = myTmp;

    bytesReturnedOut = contenLen;
    closesocket(conn);
    return(result);
}

Dev-C ++ Sürüm 4.9.9.2 ile derlenen bir Windows Vista'da bu kodu denedim. Bağlarken bana bir sürü hata verdim: [Bağlayıcı hatası] `` WSAStartup @ 8 '' için tanımsız referans
Expanding-Dev

4
@ Expanding-Dev Yalnızca MSVC (görsel stüdyo) "pragma yorumu" nu anlar. Başka bir şey kullanıyorsanız, "ws2_32.lib" dosyasını manuel olarak (diğer herhangi bir kütüphane gibi) bağlamanız gerekir.
Navin

25
@JuanLuisSoldi Sanırım bu kodun "güzelliğini" takdir etmek için gerçekten bir Windows geliştiricisi olmanız gerekiyor ...
static_rtti

Burada (recv kullanarak) ne alınması gerekiyor? Ben çıktı olarak bir sürü anlamsızlık alıyorum. Ayrıca, neden gönderme tamponuna (ör. GET / HTTP/1.1.1/... etc) Yaptığınızı koydunuz ? Gönderdiğimi nasıl biçimlendireceğimi nasıl öğrenebilirim?
LazerSharks

44

Güncelleme 2020: Şimdi 8 yaşında olanın yerine yeni bir cevabım var: https://stackoverflow.com/a/61177330/278976

Linux'ta cpp-netlib, libcurl, curlpp, urdl, boost :: asio'yu denedim ve Qt olarak düşündüm (ancak lisansa göre geri çevirdim). Bunların hepsi ya bu kullanım için eksikti, özensiz arayüzlere sahipti, zayıf dokümantasyona sahipti, bakımsızdı ya da https'yi desteklemedi.

Sonra, https://stackoverflow.com/a/1012577/278976 önerisinde POCO'yu denedim. Vay, keşke bu yıllar önce görseydim. İşte POCO ile HTTP GET isteği yapma örneği:

https://stackoverflow.com/a/26026828/2817595

POCO ücretsiz, açık kaynaklıdır (lisans yükseltme). Ve hayır, şirketle hiçbir ilişkim yok; Arayüzlerini gerçekten çok seviyorum. Harika iş adamları (ve kızlar).

https://pocoproject.org/download.html

Umarım bu birine yardımcı olur ... tüm bu kütüphaneleri denemek üç günümü aldı.



2
Poco'yu önerin üzerine indirdim. Çoğunu yeniden yazmaktan ziyade STL üzerine kurulu ve güçlendirici bir şey tercih ederim. Artı ben CppUnit hayranı değilim, özellikle yapı ile çalışan nefret testleri, ve ben inşa ederken kitaplıklarını test etmek için beklemeyin.
CashCow

Biraz büyük. Bununla birlikte, configure ile testlerin ve örneklerin (veya paylaşılan kütüphanelerin) oluşturulmasını devre dışı bırakabilirsiniz (örn. --No-testing veya --no-samples veya --no-sharedlibs). Bkz. Github.com/pocoproject/poco/blob/develop/configure
Homer6

Bunun için teşekkür ederim. Yapmam gereken işleri yapmayı önemsediğim için yine de istiyorum. Ve ben de orada JSON ayrıştırma not edin ki ben kütüphane için ne var HTTP isteği gönderdikten sonra bunu yapmak gerekir.
CashCow

bir süre önceydi, ancak bu sadece bağlantılarınızın hiçbirinin şu anda çalışmadığını, github
deposu

33

C ++ İstekleri olarak adlandırılan daha yeni, daha az olgun bir kıvırma sarıcısı geliştirilmektedir . İşte basit bir GET isteği:

#include <iostream>
#include <cpr.h>

int main(int argc, char** argv) {
    auto response = cpr::Get(cpr::Url{"http://httpbin.org/get"});
    std::cout << response.text << std::endl;
}

Çok çeşitli HTTP fiilleri ve kıvırma seçeneklerini destekler. Burada daha fazla kullanım belgesi var .

Feragatname: Bu kütüphanenin koruyucusuyum .


10
Dün CppCon 2015 yıldırım konuşmanızdaydım. Aferin - konuşma ve kütüphane. Özellikle "İnsanlar için kıvrılma" tasarım felsefesini seviyorum.
U007D

Merhaba, ben sadece bu yazıyla karşılaştım, basit yoldan daha kolay C ++ HTTP istekleri arıyorum. Ancak, kütüphaneler konusunda gerçekten tecrübeli değilim ve bunu görsel stüdyo C ++ projeme nasıl ekleyeceğimi bilmiyorum. Bir yerde bir açıklama var mı? Kütüphaneye özgü değil gibi hissediyorum, aksine genel olarak önümde ne yaptığımı gerçekten bilmiyorum.
Sossenbinder

2
@Sossenbinder, CMake ile tanışabilirseniz, bunu kullanarak bu proje için Visual Studio oluşturma dosyaları oluşturabilirsiniz. Appveyor yapılandırma dosyası bunu gerçekleştirmek için nasıl kaba bir örnek içerir.
huu

2
Güzel görünüyor, ancak bina cehennem, bu yüzden lib'niz işe yaramaz, paket yöneticisine güvenemiyorum (harici olarak nasıl bölüm ekleyeceğinize güvenilir bir şekilde ihtiyacım var) ve mümkün olan en kısa sürede fonksiyonel lib'e ihtiyacınız var ...
dev1223

Bu iş böyle olur. Bunu en çok ikinci ikinci cevabın 200 satırı ile karşılaştırdığınızda .......
v.oddou

17

İşte bir web sayfasını dize olarak alabilmek için cURL etrafındaki minimal paketleyicim. Bu, örneğin birim testi için kullanışlıdır. Temelde C kodu etrafında bir RAII sarıcıdır.

Makinenize yum install libcurl libcurl-develveya eşdeğerinize "libcurl" yükleyin .

Kullanım örneği:

CURLplusplus client;
string x = client.Get("http://google.com");
string y = client.Get("http://yahoo.com");

Sınıf uygulaması:

#include <curl/curl.h>


class CURLplusplus
{
private:
    CURL* curl;
    stringstream ss;
    long http_code;
public:
    CURLplusplus()
            : curl(curl_easy_init())
    , http_code(0)
    {

    }
    ~CURLplusplus()
    {
        if (curl) curl_easy_cleanup(curl);
    }
    std::string Get(const std::string& url)
    {
        CURLcode res;
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);

        ss.str("");
        http_code = 0;
        res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            throw std::runtime_error(curl_easy_strerror(res));
        }
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        return ss.str();
    }
    long GetHttpCode()
    {
        return http_code;
    }
private:
    static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
    {
        return static_cast<CURLplusplus*>(userp)->Write(buffer,size,nmemb);
    }
    size_t Write(void *buffer, size_t size, size_t nmemb)
    {
        ss.write((const char*)buffer,size*nmemb);
        return size*nmemb;
    }
};

16

libCURL sizin için oldukça iyi bir seçenek. Ne yapmanız gerektiğine bağlı olarak, öğretici özellikle kolay kullanım için ne istediğinizi söylemelidir. Ancak, temel olarak, bunu yalnızca bir sayfanın kaynağını görmek için yapabilirsiniz:

CURL* c;
c = curl_easy_init();
curl_easy_setopt( c, CURL_URL, "www.google.com" );
curl_easy_perform( c );
curl_easy_cleanup( c );

Bunun sonucun yazdırılmasına neden olacağına inanıyorum. Onun yerine ele almak istiyorsanız - ki, sanırım, sen - CURL_WRITEFUNCTION ayarlamanız gerekir. Bunların tümü, yukarıda bağlantılı olan kıvırma eğitiminde ele alınmıştır.


16

Bir C ++ çözümü istediğiniz gibi, Qt kullanabilirsiniz . Kullanabileceğiniz bir QHttp sınıfı vardır.

Dokümanları kontrol edebilirsiniz :

http->setHost("qt.nokia.com");
http->get(QUrl::toPercentEncoding("/index.html"));

Qt ayrıca ortak bir C ++ uygulamasında kullanabileceğiniz çok daha fazlasına sahiptir.


4
Ben QHttp QNet 4.6 ve sonraki sürümlerinde QNetworkAccessManager ve ilgili sınıflarla değiştirildi düşünüyorum.
juzzlin

3
QNetworkAccessManagerQt 4.4'ten beri belgelenmiştir; Ve Qt 4.8'de şöyle diyor: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Yani, kullanımdan kaldırılmış uyarıları görmezden gelirseniz, hala kullanılabilir.
Jesse Chisholm

13

C ++ REST SDK'sını (kod adı "Casablanca") kontrol etmek isteyebilirsiniz . http://msdn.microsoft.com/en-us/library/jj950081.aspx

C ++ REST SDK ile C ++ uygulamanızdan HTTP sunucularına daha kolay bağlanabilirsiniz.

Kullanım örneği:

#include <iostream>
#include <cpprest/http_client.h>

using namespace web::http;                  // Common HTTP functionality
using namespace web::http::client;          // HTTP client features

int main(int argc, char** argv) {
    http_client client("http://httpbin.org/");

    http_response response;
    // ordinary `get` request
    response = client.request(methods::GET, "/get").get();
    std::cout << response.extract_string().get() << "\n";

    // working with json
    response = client.request(methods::GET, "/get").get();
    std::cout << "url: " << response.extract_json().get()[U("url")] << "\n";
}

C ++ REST SDK, modern bir eşzamansız C ++ API tasarımı kullanarak yerel kodda bulut tabanlı istemci-sunucu iletişimi için bir Microsoft projesidir.


10

Bu cevapla Software_Developer'ın cevabına atıfta bulunuyorum . Kodu yeniden oluşturarak bazı parçaların kullanımdan kaldırıldığını ( gethostbyname()) veya hata işlemeyi sağlamadığını gördüm (soket oluşturma, bir şey gönderme) .

Aşağıdaki Windows kodu , Visual Studio 2013 ve Windows 8.1 64-bit ve Windows 7 64-bit ile test edilmiştir. Www.google.com Web Sunucusu ile bir IPv4 TCP Bağlantısını hedefleyecektir.

#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
    int main (){
    // Initialize Dependencies to the Windows Socket.
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        cout << "WSAStartup failed.\n";
        system("pause");
        return -1;
    }

    // We first prepare some "hints" for the "getaddrinfo" function
    // to tell it, that we are looking for a IPv4 TCP Connection.
    struct addrinfo hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;          // We are targeting IPv4
    hints.ai_protocol = IPPROTO_TCP;    // We are targeting TCP
    hints.ai_socktype = SOCK_STREAM;    // We are targeting TCP so its SOCK_STREAM

    // Aquiring of the IPv4 address of a host using the newer
    // "getaddrinfo" function which outdated "gethostbyname".
    // It will search for IPv4 addresses using the TCP-Protocol.
    struct addrinfo* targetAdressInfo = NULL;
    DWORD getAddrRes = getaddrinfo("www.google.com", NULL, &hints, &targetAdressInfo);
    if (getAddrRes != 0 || targetAdressInfo == NULL)
    {
        cout << "Could not resolve the Host Name" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Create the Socket Address Informations, using IPv4
    // We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR
    SOCKADDR_IN sockAddr;
    sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr;    // The IPv4 Address from the Address Resolution Result
    sockAddr.sin_family = AF_INET;  // IPv4
    sockAddr.sin_port = htons(80);  // HTTP Port: 80

    // We have to free the Address-Information from getaddrinfo again
    freeaddrinfo(targetAdressInfo);

    // Creation of a socket for the communication with the Web Server,
    // using IPv4 and the TCP-Protocol
    SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (webSocket == INVALID_SOCKET)
    {
        cout << "Creation of the Socket Failed" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Establishing a connection to the web Socket
    cout << "Connecting...\n";
    if(connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
    {
        cout << "Could not connect";
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }
    cout << "Connected.\n";

    // Sending a HTTP-GET-Request to the Web Server
    const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
    int sentBytes = send(webSocket, httpRequest, strlen(httpRequest),0);
    if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR)
    {
        cout << "Could not send the request to the Server" << endl;
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }

    // Receiving and Displaying an answer from the Web Server
    char buffer[10000];
    ZeroMemory(buffer, sizeof(buffer));
    int dataLen;
    while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0))
    {
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            cout << buffer[i];
            i += 1;
        }
    }

    // Cleaning up Windows Socket Dependencies
    closesocket(webSocket);
    WSACleanup();

    system("pause");
    return 0;
}

Referanslar:

Gethostbyname'in kullanımdan kaldırılması

Soket Dönüş Değeri ()

Send () dönüş değeri


7

C ++ doğrudan yapmak için herhangi bir yol sağlamaz. Bu tamamen sahip olduğunuz platformlara ve kütüphanelere bağlıdır.

En kötü durumda, bir TCP bağlantısı kurmak, HTTP üstbilgilerini (RFC 2616) göndermek ve yanıtları doğrudan ayrıştırmak için boost :: asio kütüphanesini kullanabilirsiniz. Uygulama ihtiyaçlarınıza bakarak, bunu yapmak için yeterince basit.


1
En azından şimdi. :) stackoverflow.com/a/51959694/1599699
Andrew

@Andrew: Eğer "It does" adresiniz sybreon's "C ++ bunu yapmak için herhangi bir yol sağlamaz." , bağlantılı yanıt sistem özelliklerini kullanarak bunu yapmanın bir yolunu gösterdiğinden geçerli değildir.
Sebastian Mach

@SebastianMach Yani, olsa da. Sadece sistem tarafından sağlanan bir kütüphaneyi içe aktarın ve bir fonksiyon çağırın, bu işi sizin için yapar. Bunu diğer tüm c ++ seçenekleriyle karşılaştırın ve ya gerçekten zor ya da 3. taraf kodu kullanıyor. Bunu oldukça doğrudan düşünüyorum.
Andrew

1
@Andrew: Yalnızca Windows'ta "sistem tarafından sağlanır". Diğer sistemlerde durum farklıdır. Ve "C ++ doğrudan yapmak için herhangi bir yol sağlamaz" ile ilgisi yoktur, bu da gerçekten "C ++ standardı yoktur" anlamına gelir.
Sebastian Mach

@SebastianMach Evet ama bu da öznel çünkü c ++ aynı zamanda telefon tabletleri mikrodenetleyiciler vb üzerinde çalışır. Her bir cihaz veya işletim sistemi c ++ 'da bazı işlevleri kolayca desteklemiyorsa, doğrudan c ++ tarafından sağlanmadı mı diyoruz? OP "c ++ standardı" demedi sadece c ++ dedi. Bu cevaplar Linux ve Windows çözümleri sağlıyor, çünkü genellikle böyle bir şey için kullanacağınız şey bu. Bu yüzden, bir Linux çözümü değil, ama evet, büyük bir işletim sistemi tarafından doğrudan sağlanıyor.
Andrew

6

İşte herhangi bir 3. taraf kitaplığı kullanmaya gerek kalmadan çalışacak bazı kodlar: İlk olarak ağ geçidinizi, kullanıcınızı, şifrenizi ve bu sunucuya göndermeniz gereken diğer parametreleri tanımlayın.

#define USERNAME "user"
#define PASSWORD "your password"
#define GATEWAY "your gateway"

İşte kodun kendisi:

HINTERNET hOpenHandle, hResourceHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");


hOpenHandle = InternetOpen(_T("HTTPS"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
    return false;
}


hConnectHandle = InternetConnect(hOpenHandle,
    GATEWAY,
    INTERNET_DEFAULT_HTTPS_PORT,
    NULL, NULL, INTERNET_SERVICE_HTTP,
    0, 1);

if (hConnectHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    return false;
}


hResourceHandle = HttpOpenRequest(hConnectHandle,
    _T("POST"),
    GATEWAY,
    NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_KEEP_CONNECTION,
    1);

if (hResourceHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    InternetCloseHandle(hConnectHandle);
    return false;
}

InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME, (LPVOID)USERNAME, _tcslen(USERNAME));
InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD, (LPVOID)PASSWORD, _tcslen(PASSWORD));

std::string buf;
if (HttpSendRequest(hResourceHandle, szHeaders, 0, NULL, 0))
{
    while (true)
    {
        std::string part;
        DWORD size;
        if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))break;
        if (size == 0)break;
        part.resize(size);
        if (!InternetReadFile(hResourceHandle, &part[0], part.size(), &size))break;
        if (size == 0)break;
        part.resize(size);
        buf.append(part);
    }
}

if (!buf.empty())
{
    // Get data back
}

InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);

Bu bir Win32 API ortamında çalışmalıdır.

İşte bir örnek .


Ağ geçidi için ne koymalıyım? Lanet olası bir ağ geçidi yok ... Win API çok kötü.
Tomáš Zato - Monica'yı

1
"Ağ Geçidi", servis sağlayıcı tarafından sağlanan URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) için genel bir kelimedir . Bunun Windows ile bir ilgisi yok.
Michael Haephrati

Ah teşekkürler. Bu ifadenin URL için kullanıldığını hiç duymadım, bu yüzden beni şaşırttı. Açıklama için teşekkürler.
Tomáš Zato - Monica'yı

Tamam, kodu test ettim ve örneğiniz eklenmiyor. InternetConnecttam URL verildiğinde null değerini döndürür, ancak yalnızca alan adı belirtildiğinde null olmayan değeri döndürür. Peki, indirmek istediğim sayfayı almak için tam URL'yi ne zaman / nerede kullanırım?
Tomáš Zato - Monica'yı

Url kullanmak istiyorsanız InternetConnect () yerine InternetOpenUrl () kullanın
Al Po

4

Nisan 2020 için cevap güncellendi:

Son zamanlarda cpp-httplib (hem istemci hem de sunucu olarak) ile çok başarılı oldum . Olgun ve yaklaşık tek dişli RPS yaklaşık 6k.

Kanama kenar daha günü, gerçekten umut verici çerçeve var gbm-çerçeve iki çekirdek etrafında 180k RPS alabilirsiniz (ve temel aldığı için çekirdek sayısı ile ölçeklendirilebileceği, seastar çerçevesinde, yetki hızlı DBs üzerinde gezegen, scylladb ).

Bununla birlikte, cpv-framework hala nispeten olgunlaşmamış durumda; yani, çoğu kullanım için, cpp-httplib tavsiye ederim.

Bu öneri önceki cevabımın yerini alıyor (8 yıl önce).


Teşekkürler, belki de yakın gelecekte denemek;)
Hack06

Gerçekten cpp-httplib 1-dosya (5K-hatları tamam) yaklaşımını seviyorum. Performansı hakkında bir fikriniz var mı?
Hack06

1
@ Hack06 Kaba ölçüt saniyede yaklaşık 6000 istek (RPS) idi.
Homer6

3

C ve C ++ 'nın HTTP veya soket bağlantıları için standart bir kütüphanesi yoktur. Yıllar içinde bazı taşınabilir kütüphaneler geliştirilmiştir. Diğerlerinin söylediği gibi en yaygın kullanılan libcurl .

İşte bir liste libcurl (libcurl web sitesinden gelen) alternatiflerinin .

Ayrıca, Linux için bu basit bir HTTP istemcisidir. Kendi basit HTTP GET istemcinizi uygulayabilirsiniz, ancak ilgili kimlik doğrulama veya yönlendirmeler varsa veya bir proxy'nin arkasında çalışmanız gerekiyorsa bu çalışmaz. Bu durumlar için libcurl gibi tam bir kütüphaneye ihtiyacınız var.

Libcurl içeren kaynak kodu için bu , istediğiniz şeye en yakın olanıdır (Libcurl'un birçok örneği vardır ). Ana işleve bakın. Html içeriği başarılı bir bağlantıdan sonra ara belleğe kopyalanır. Sadece parseHtml dosyasını kendi işlevinizle değiştirin.


3

EmbeddedRest kitaplığını kullanabilirsiniz . Hafif başlık sadece bir kütüphanedir. Bu yüzden projenize dahil etmek kolaydır ve derleme gerektirmez, çünkü hayır.cpp içinde dosya .

Repo'dan örnek isteyin readme.md:

#include "UrlRequest.hpp"

//...

UrlRequest request;
request.host("api.vk.com");
const auto countryId=1;
const auto count=1000;
request.uri("/method/database.getCities",{
    {"lang","ru"},
    {"country_id",countryId},
    {"count",count},
    {"need_all","1"},
});
request.addHeader("Content-Type: application/json");
auto response=std::move(request.perform());
if(response.statusCode()==200){
  cout<<"status code = "<<response.statusCode()<<", body = *"<<response.body()<<"*"<<endl;
}else{
  cout<<"status code = "<<response.statusCode()<<", description = "<<response.statusDescription()<<endl;
}

Win32'de derlenmez: /
uhfocuz

@uhfocuz, lib iOS ve Android için yazılmıştır. Ama Win32 için derlemenize yardımcı olabilirim. Çok zor değil
fnc12 6

Benim kullanım için, benim Mac üzerinde gayet güzel derlenmiş ama Windows üzerinde kütüphanelerini sahip daha dont gibi farklı ne kadar hafif olduğunu seviyorum netdb.hben biraz yardım evet istiyorum böylece vb
uhfocuz

@uhfocuz tüm yapmanız gereken gibi koşullar #ifdef _WIN32eklemek ve orada pencerelere özel kod eklemek. Buraya bakın - unix soketleri ile pencere soketleri arasında çok fazla fark yoktur. İki ana farklılık görüyorum: 1) WSAStartupönce arayın ve 2) closesocketyerine kullanınclose
fnc12

@ uhfocuz lütfen repoda bir sorun yaratın - Yeterli zamanım olduğunda win32 desteği ekleyeceğim
fnc12

3

HTTP protokolü çok basit, bu nedenle bir HTTP istemcisi yazmak çok basit. İşte burada

https://github.com/pedro-vicente/lib_netsockets

Bir web sunucusundan bir dosya almak için HTTP GET kullanır, hem sunucu hem de dosya komut satırı parametreleridir. Uzak dosya yerel bir kopyaya kaydedilir.

Feragatname: Ben yazarım

EDIT: düzenlenmiş URL


Verilen URL geçersiz.
Shravan40

3

Bunun libcurl, Windows.h veya WinSock gerektirmediğine dikkat edin! Hiçbir derleme kütüphaneleri, hiçbir proje yapılandırması, vb. Windows 10 üzerinde Visual Studio 2017 c ++ ile çalışan bu kodu var:

#pragma comment(lib, "urlmon.lib")

#include <urlmon.h>
#include <sstream>

using namespace std;

...

IStream* stream;
//Also works with https URL's - unsure about the extent of SSL support though.
HRESULT result = URLOpenBlockingStream(0, "http://google.com", &stream, 0, 0);
if (result != 0)
{
    return 1;
}
char buffer[100];
unsigned long bytesRead;
stringstream ss;
stream->Read(buffer, 100, &bytesRead);
while (bytesRead > 0U)
{
    ss.write(buffer, (long long)bytesRead);
    stream->Read(buffer, 100, &bytesRead);
}
stream.Release();
string resultString = ss.str();

Basit bir API erişim komut dosyası istediğim için bunu nasıl yapacağımı anladım, libcurl gibi kütüphaneler bana her türlü soruna neden oluyordu (talimatları takip ettiğimde bile ...) ve WinSock çok düşük seviyeli ve karmaşık .

Tüm IStream okuma kodu (özellikle while koşulu - düzeltmek / geliştirmek için çekinmeyin) hakkında emin değilim , ama hey, o sorunsuz çalışır ! (Bir engelleme (eşzamanlı) çağrısı kullandığım için , bu iyi, bytesReadakışın ( ISequentialStream ?) Okunması bitene kadar her zaman> 0U olacağı mantıklı .

Ayrıca bkz: URL Takma Adları ve Eşzamansız Takılabilir Protokol Başvurusu


Düzenlendi, ancak c ++ 'da adil bir deneyim tecrübesinden sonra, bunun muhtemelen c ++' da bu tür bir şeyi yapabileceğiniz en kolay yol olduğunu iddia edeceğim ... (At Şimdilik en az ...)
Andrew

Sadece URLOpenBlockingStream'i birkaç URL ile badssl.com'dan (oldukça kullanışlı) test ettim ve SSL sertifikası kötü ise bu işlem başarısız olacaktır. Test ettiğim her durumda (sadece birkaçı), yukarıdaki kodun çıktısı boş bir dize olacaktır (akış verisi yok). Bu oldukça hoş.
Andrew

Ben bunu easiesst, ama http yanıt kodunu nasıl elde eder?
Robin

@Robin Ha! Mesajı ve cevap kodunu istedin mi ?! Benim yaptığım kadar iyi biliyorsunuz, ancak belgelere bakarsanız ve daha manuel URL Monikers sayfalarına bakarsanız, bir cevap bulabilirsiniz. Birisi daha fazla yapılandırma için izin verecek elle URLOpenBlockingStream uygulayarak çevrimiçi kod gönderme hatırlamak gibi görünüyor. Bir şey bulursanız bana bildirin!
Andrew

Az önce bulduğum bu da var : docs.microsoft.com/en-us/windows/desktop/WinInet/… İyi olup olmadığını bilmiyorum.
Andrew

2

Bir URL'nin içeriğini bir URL'ye indirmek için libCURL kullanan bazı (nispeten) basit C ++ 11 kodu std::vector<char> :

http_download.hh

# pragma once

#include <string>
#include <vector>

std::vector<char> download(std::string url, long* responseCode = nullptr);

http_download.cc

#include "http_download.hh"

#include <curl/curl.h>
#include <sstream>
#include <stdexcept>

using namespace std;

size_t callback(void* contents, size_t size, size_t nmemb, void* user)
{
  auto chunk = reinterpret_cast<char*>(contents);
  auto buffer = reinterpret_cast<vector<char>*>(user);

  size_t priorSize = buffer->size();
  size_t sizeIncrease = size * nmemb;

  buffer->resize(priorSize + sizeIncrease);
  std::copy(chunk, chunk + sizeIncrease, buffer->data() + priorSize);

  return sizeIncrease;
}

vector<char> download(string url, long* responseCode)
{
  vector<char> data;

  curl_global_init(CURL_GLOBAL_ALL);
  CURL* handle = curl_easy_init();
  curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
  curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback);
  curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
  curl_easy_setopt(handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  CURLcode result = curl_easy_perform(handle);
  if (responseCode != nullptr)
    curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, responseCode);
  curl_easy_cleanup(handle);
  curl_global_cleanup();

  if (result != CURLE_OK)
  {
    stringstream err;
    err << "Error downloading from URL \"" << url << "\": " << curl_easy_strerror(result);
    throw runtime_error(err.str());
  }

  return move(data);
}

2

Genellikle cURL, POCO veya Qt gibi platformlar arası bir şey öneriyorum. Ancak, işte bir Windows örneği !:

#include <atlbase.h>
#include <msxml6.h>
#include <comutil.h> // _bstr_t

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();

2

Huzurlu web hizmetlerini tüketmek için birden fazla platformda (Linux, Windows ve Mac) desteklenen bir C ++ HTTP sunucusu arşivi arıyorsanız. Aşağıdaki seçeneklere sahip olabilirsiniz.

  1. QT Ağ Kütüphanesi - Uygulamanın ağ istekleri göndermesine ve yanıt almasına izin verir
  2. C ++ REST SDK - PPL destekli yeni bir üçüncü taraf HTTP kitaplığı
  3. Libcurl - Muhtemelen yerli dünyada en çok kullanılan http lib'den biridir.

1

Biraz geç olmasına rağmen. Https://github.com/Taymindis/backcurl tercih edebilirsiniz .

Mobil c ++ geliştirmede http araması yapmanızı sağlar. Mobil oyun geliştirme için uygun

bcl::init(); // init when using

bcl::execute<std::string>([&](bcl::Request *req) {
    bcl::setOpts(req, CURLOPT_URL , "http://www.google.com",
             CURLOPT_FOLLOWLOCATION, 1L,
             CURLOPT_WRITEFUNCTION, &bcl::writeContentCallback,
             CURLOPT_WRITEDATA, req->dataPtr,
             CURLOPT_USERAGENT, "libcurl-agent/1.0",
             CURLOPT_RANGE, "0-200000"
            );
}, [&](bcl::Response * resp) {
    std::string ret =  std::string(resp->getBody<std::string>()->c_str());
    printf("Sync === %s\n", ret.c_str());
});


bcl::cleanUp(); // clean up when no more using

0

ACE'yi kullanarak şunları yapabilirsiniz:

#include "ace/SOCK_Connector.h"

int main(int argc, ACE_TCHAR* argv[])
{
    //HTTP Request Header
    char* szRequest = "GET /video/nice.mp4 HTTP/1.1\r\nHost: example.com\r\n\r\n"; 
    int ilen = strlen(szRequest);

    //our buffer
    char output[16*1024];

    ACE_INET_Addr server (80, "example.com");
    ACE_SOCK_Stream peer;
    ACE_SOCK_Connector connector;
    int ires = connector.connect(peer, server);
    int sum = 0;
    peer.send(szRequest, ilen);
    while (true)
    {
        ACE_Time_Value timeout = ACE_Time_Value(15);
        int rc = peer.recv_n(output, 16*1024, &timeout);
        if (rc == -1)
        {
            break;
        }
        sum += rc;
    }
    peer.close();
    printf("Bytes transffered: %d",sum);

    return 0;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.