C ++로 HTTP 요청을 어떻게합니까?


258

C ++로 HTTP 요청을 쉽게 만들 수있는 방법이 있습니까? 특히, 페이지의 내용 (API)을 다운로드하고 내용에 1 또는 0이 포함되어 있는지 확인하고 싶습니다. 내용을 문자열로 다운로드 할 수도 있습니까?


1
아니요. 현재 언어 또는 네트워킹을위한 표준 라이브러리를 통해 기본적으로 지원되지 않습니다. 그러나 네트워킹 TS N4370이 있습니다. 나는 또한 도서관 추천을 끌어 들이기 때문에이 질문을 VTC했습니다.

BoostBeast는 어떻습니까?
— twocrush

답변:


249

나는 같은 문제가 있었다. libcurl 은 정말 완전합니다. C ++ 라이브러리를 요청할 때 관심을 가질 수 있는 C ++ 래퍼 curlpp 이 있습니다. neon 은 WebDAV 를 지원하는 또 다른 흥미로운 C 라이브러리입니다 .

C ++를 사용하면 curlpp가 자연스럽게 보입니다. 소스 배포에 많은 예제가 제공됩니다. URL의 내용을 얻으려면 다음과 같이하십시오 (예에서 추출) :

// 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 소스 배포판 의 examples디렉토리를 참조하십시오 . curlpp를 사용 하는 간단한 완전한 최소 디렉토리 뿐만 아니라 더 복잡한 경우가 많이 있습니다.

내 2 센트 ...


1
최신 버전은 mac ..에서 깨진 것 같습니다. 라이브러리로 링크하면 config.h에 문제가 발생합니다.
— eugene

1
글쎄, 나는 위를 컴파일 할 수 없었다. 그러나로 교체 os << myRequest.perform();하면 myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();결과가 나왔습니다. 를 사용하지 마십시오 http://example.com. 빈 페이지가 반환됩니다. 더 나은 사용 예 : http://www.wikipedia.org.
— Zane

4
MSVS에서 curlpp를 어떻게 구축합니까? 나는 그것을 작동시킬 수 없다 :(
— mr5

2
@ ryan-sam의 최신 편집 내용에 동의하지 않습니다. 주어진 라이브러리가 "HTTP 및 WebDAV 작업"을 위해 명시 적으로 만들어 졌기 때문에 웹 개발이 아니라 "webdav"를 작성하는 것이 저자의 의도였습니다.
— Bostrot

2
@ bostrot : 그렇습니다. 나는 되돌아 가서 링크를 추가했는데 사람들은 내가 webdev를 썼다고 생각했다. 이 얼마나 유감 :)
— 신경

115

Windows 코드 :

#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;
}

훨씬 나은 구현은 다음과 같습니다.

#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 ++ 버전 4.9.9.2로 컴파일하는 Windows Vista에서이 코드를 사용해 보았습니다. [Linker error]`WSAStartup @ 8 '에 대한 정의되지 않은 참조
— Expanding-Dev

4
@ Expanding-Dev Only MSVC (시각 스튜디오)는 "pragma comment"를 이해합니다. 다른 것을 사용하는 경우 다른 라이브러리처럼 "ws2_32.lib"를 수동으로 연결해야합니다.
— Navin

24
@JuanLuisSoldi 난 당신이 정말이 코드의 "아름다움"을 감상하기 위해 Windows 개발자가 필요하다고 생각합니다 ...
— static_rtti

여기에 무엇을 받아야합니까 (recv를 사용하여)? 출력으로 많은 횡설수설을 받고 있습니다. 또한 왜 송신 버퍼에 수행 한 작업을 넣었 GET / HTTP/1.1.1/... etc습니까 (예 :) ? 보내는 내용을 형식화하는 방법을 어떻게 알 수 있습니까?
— LazerSharks

43

업데이트 2020 : 나는 이것을 대체하는 새로운 대답을 얻었습니다. 현재 8 세입니다 .https : //stackoverflow.com/a/61177330/278976

Linux에서는 cpp-netlib, libcurl, curlpp, urdl, boost :: asio를 시도하고 Qt를 고려했지만 라이센스에 따라 거절했습니다. 이 모두는이 용도로는 불완전했거나, 인터페이스가 느슨하거나, 문서가 열악하거나, 유지 관리되지 않았거나 https를 지원하지 않았습니다.

그런 다음 https://stackoverflow.com/a/1012577/278976 의 제안에 따라 POCO를 시도했습니다. 와우, 나는 몇 년 전에 본 적이 있기를 바랍니다. 다음은 POCO로 HTTP GET 요청을 작성하는 예입니다.

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

POCO는 무료이며 오픈 소스입니다 (부스트 라이센스). 그리고 아뇨, 저는 회사와 아무런 관련이 없습니다. 나는 그들의 인터페이스를 정말로 좋아한다. 훌륭한 직업들 (그리고 여자들).

https://pocoproject.org/download.html

이것이 누군가를 돕기를 바랍니다 ...이 라이브러리를 모두 시험해 보는 데 3 일이 걸렸습니다.



2
방금 제안에 Poco를 다운로드했습니다. 나는 STL을 기반으로하고 그것을 많이 다시 쓰는 것이 아니라 부스트하는 가벼운 것을 선호합니다. 또한 나는 CppUnit의 팬이 아니며 특히 빌드로 실행되는 테스트를 싫어하며 빌드 할 때 라이브러리를 테스트 할 필요가 없습니다.
— CashCow

조금 큽니다. 그러나 configure를 사용하여 테스트 및 샘플 (또는 공유 라이브러리) 빌드를 비활성화 할 수 있습니다 (예 : --no-tests 또는 --no-samples 또는 --no-sharedlibs). 참조 github.com/pocoproject/poco/blob/develop/configure
— Homer6

고마워 어쨌든 내가해야 할 일을 완수하는 데 관심이 있기 때문에 원합니다. 그리고 JSON 구문 분석이있어서 라이브러리를 얻은 HTTP 요청을 보낸 후에해야합니다.
— CashCow

얼마 전 이었지만, 지금은 링크가 작동하지 않는 것을 알 수 있도록하기 위해서입니다. github 저장소도 제거 된 것처럼 보입니다 ...
— Hack06

33

C ++ Requests 라고하는 덜 성숙 된 컬 래퍼가 개발되었습니다 . 간단한 GET 요청은 다음과 같습니다.

#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;
}

다양한 HTTP 동사 및 컬 옵션을 지원합니다. 더 많은 사용 설명서가 있습니다 .

면책 조항 : 나는이 도서관의 관리자입니다 .


10
어제 CppCon 2015 번개 대화에 참석했습니다. 대화와 도서관 모두 잘했습니다. 저는 특히 "사람을위한 컬"디자인 철학을 좋아합니다.
— U007D

안녕하세요, 나는이 게시물을 보았습니다. 일반적인 방법보다 쉬운 C ++ HTTP 요청을 찾고 있습니다. 그러나 실제로 라이브러리에 익숙하지는 않으며 Visual Studio C ++ 프로젝트에 라이브러리를 포함시키는 방법을 모르겠습니다. 어딘가에 대한 설명이 있습니까? 나는 그것이 도서관에만 국한된 것이 아니라 오히려 일반적으로 내 앞에서 무엇을해야하는지 실제로 모른다는 느낌이 든다.
— Sossenbinder

2
@Sossenbinder, CMake에 익숙해지면이 프로젝트를 사용하여 Visual Studio 빌드 파일을 생성 할 수 있습니다. appveyor 구성 파일이 작업을 수행하는 방법에 대한 대략적인 예제를 포함하고 있습니다.
— huu

2
멋져 보이지만 건물은 지옥이므로, lib는 쓸모가 없습니다. 패키지 관리자에 의존 할 수 없으며 (외부 적으로
— dep

그것이 당신이하는 방법입니다. 이 질문을 두 번째로 가장 많이
— 찬성 된

17

다음은 웹 페이지를 문자열로 가져올 수 있도록 cURL을 둘러싼 최소 래퍼입니다. 예를 들어 단위 테스트에 유용합니다. 기본적으로 C 코드 주위의 RAII 래퍼입니다.

머신에 "libcurl"을 설치하십시오 yum install libcurl libcurl-devel.

사용 예 :

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

클래스 구현 :

#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 은 당신에게 아주 좋은 옵션입니다. 무엇을해야하는지에 따라, 튜토리얼 은 특히 ​​손쉬운 조작을 위해 원하는 것을 알려줍니다. 그러나 기본적으로 페이지 소스를 확인하기 위해이 작업을 수행 할 수 있습니다.

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

나는 이것이 결과가 stdout으로 인쇄되게 할 것이라고 믿는다. 대신 처리하려는 경우 CURL_WRITEFUNCTION을 설정해야합니다. 이 모든 것은 위에 링크 된 curl 튜토리얼에서 다룹니다.


16

C ++ 솔루션을 원한다면 Qt를 사용할 수 있습니다 . 사용할 수있는 QHttp 클래스가 있습니다.

당신은 문서 를 확인할 수 있습니다 :

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

Qt는 또한 일반적인 C ++ 앱에서 사용할 수있는 많은 기능을 제공합니다.


4
QHttp가 Qt 4.6 이상 버전에서 QNetworkAccessManager 및 관련 클래스로 대체되었다고 생각합니다.
— juzzlin

3
QNetworkAccessManagerQt 4.4부터 문서화되었습니다. Qt 4.8에서는 다음과 같이 말합니다. QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.더 이상 사용되지 않는 경고를 무시하면 여전히 사용할 수 있다고 생각합니다.
— Jesse Chisholm

13

C ++ REST SDK (코드 명 "Casablanca") 를 확인할 수 있습니다 . http://msdn.microsoft.com/en-us/library/jj950081.aspx

C ++ REST SDK를 사용하면 C ++ 앱에서 HTTP 서버에 더 쉽게 연결할 수 있습니다.

사용 예 :

#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는 최신 비동기 C ++ API 설계를 사용하여 원시 코드로 클라우드 기반 클라이언트-서버 통신을위한 Microsoft 프로젝트입니다.


10

이 답변에서는 Software_Developer 의 답변을 참조합니다 . 코드를 다시 작성하면 일부 부분이 더 이상 사용되지gethostbyname() 않거나 ( ) 오류 처리를 제공하지 않는 것으로 나타났습니다 (소켓 생성, 전송)를 .

다음 Windows 코드 는 Visual Studio 2013 및 Windows 8.1 64 비트와 Windows 7 64 비트에서 테스트되었습니다. www.google.com 웹 서버와의 IPv4 TCP 연결을 대상으로합니다.

#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;
}

참고 문헌 :

gethostbyname 사용 중단

socket ()의 반환 값

send ()의 반환 값


7

C ++은 직접 수행 할 수있는 방법을 제공하지 않습니다. 그것은 당신이 가진 플랫폼과 라이브러리에 전적으로 달려 있습니다.

최악의 경우 boost :: asio 라이브러리를 사용하여 TCP 연결을 설정하고 HTTP 헤더 (RFC 2616)를 보내고 응답을 직접 구문 분석 할 수 있습니다. 응용 프로그램 요구 사항을 살펴보면이 작업은 충분히 간단합니다.


1
적어도 지금은 그렇습니다. :) stackoverflow.com/a/51959694/1599699
— Andrew

@Andrew : " 그렇다면 " sybreon의 "C ++가 직접 수행 할 방법을 제공하지 않습니다." 링크 된 답변은 시스템 사양을 사용하여 수행하는 방법을 보여 주므로 유효하지 않습니다.
— Sebastian Mach

@SebastianMach 나는 의미한다. 시스템 제공 라이브러리를 가져 와서 함수를 호출하기 만하면됩니다. 다른 모든 C ++ 옵션과 비교하면 실제로 어렵거나 타사 코드를 사용하는 것입니다. 나는 그것이 매우 직접적이라고 생각합니다.
— 앤드류

1
@Andrew : Windows에서만 "시스템 제공"입니다. 다른 시스템에서는 다릅니다. 그리고 "C ++은 직접 할 수있는 방법을 제공하지 않습니다"와는 아무런 관련이 없습니다. 이는 "C ++ 표준은 그렇지 않습니다"를 의미합니다.
— Sebastian Mach

@SebastianMach 네, c ++도 휴대폰 태블릿 마이크로 컨트롤러 등에서 실행되기 때문에 주관적이기도합니다. 모든 단일 장치 나 OS가 c ++의 일부 기능을 쉽게 지원하지 않는 경우 c ++에서 직접 제공하지는 않습니까? OP는 "c ++ 표준"이라고 말하지 않았고 방금 c ++라고 말했습니다. 이 답변은 Linux 및 Windows 솔루션을 제공합니다. 일반적으로 이와 같은 용도로 사용되기 때문입니다. 확실히 이것은 Linux 솔루션이 아니지만 주요 OS에서 직접 제공합니다.
— 앤드류

6

타사 라이브러리를 사용할 필요없이 작동하는 코드는 다음과 같습니다. 먼저 게이트웨이, 사용자, 비밀번호 및이 특정 서버로 전송해야하는 기타 매개 변수를 정의하십시오.

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

코드 자체는 다음과 같습니다.

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

Win32 API 환경에서 작동합니다.

여기 예가 있습니다.


게이트웨이에 무엇을 넣어야합니까? 망할 게이트웨이가 없습니다 ... Win API가 너무 나쁩니다.
— Tomáš Zato – 복원 모니카

1
"게이트웨이"는 서비스 제공 업체가 제공 한 URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) 의 일반적인 단어입니다 . 이것은 Windows와 관련이 없습니다.
— Michael Haephrati

아 고마워 나는 그 표현이 URL에 사용될 것이라는 말을 들어 본 적이 없기 때문에 다소 혼란 스럽습니다. 설명해 주셔서 감사합니다.
— Tomáš Zato-복원 모니카

좋아, 나는 코드를 테스트했지만 예제가 합산되지 않는다. InternetConnect전체 URL이 제공되면 null을 반환하지만 도메인 이름 만 제공하면 null이 아닌 값을 반환합니다. 그렇다면 언제 어디에서 전체 URL을 사용하여 다운로드하려는 페이지를 얻을 수 있습니까?
— Tomáš Zato-복원 모니카

url을 사용하려면 InternetConnect () 대신 InternetOpenUrl ()을 사용하십시오
— Al Po

4

2020 년 4 월 업데이트 된 답변 :

최근에 cpp-httplib (클라이언트 및 서버 모두)를 통해 많은 성공을 거두었습니다 . 성숙하고 대략적인 단일 스레드 RPS는 약 6k입니다.

최첨단 기술에는 두 개의 코어에서 약 180k RPS를 얻을 수있는 가장 유망한 프레임 워크 인 cpv-framework 가 있으며 , 가장 빠른 DB를 구동 하는 seastar 프레임 워크를 기반으로하므로 코어 수에 따라 확장 됩니다. 행성, scylladb ).

그러나 cpv-framework는 여전히 상대적으로 미숙합니다. 따라서 대부분의 경우 cpp-httplib를 적극 권장합니다.

이 권장 사항은 이전 답변 (8 년 전)을 대체합니다 .


감사합니다, 가까운 장래에 시도해 볼 것입니다;)
— Hack06

나는 cpp-httplib의 1 파일 (5K 라인은 괜찮습니다) 접근법을 정말로 좋아합니다. 성능에 대한 아이디어가 있습니까?
— Hack06

1
@ Hack06 대략적인 벤치 마크는 초당 약 6000 개의 요청 (RPS)입니다.
— 호머 6

3

C 및 C ++에는 HTTP 또는 소켓 연결을위한 표준 라이브러리가 없습니다. 수년 동안 일부 휴대용 라이브러리가 개발되었습니다. 다른 사람들이 말했듯이 가장 널리 사용되는 것은 libcurl 입니다.

다음은 libcurl의 대안 목록 입니다 (libcurl 웹 사이트에서 제공).

또한, 리눅스, 이 간단한 HTTP 클라이언트입니다. 고유 한 간단한 HTTP GET 클라이언트를 구현할 수 있지만 인증이나 리디렉션이 있거나 프록시 뒤에서 작업해야하는 경우에는 작동하지 않습니다. 이러한 경우 libcurl과 같은 본격적인 라이브러리가 필요합니다.

libcurl에와 소스 코드의 경우, 이것은 당신이 원하는에 가장 가까운 (libcurl에 많은있다 예 ). 주요 기능을보십시오. 성공적으로 연결되면 html 컨텐츠가 버퍼에 복사됩니다. parseHtml을 자신의 함수로 바꾸십시오.


3

embeddedRest 라이브러리 를 사용할 수 있습니다 . 가벼운 헤더 전용 라이브러리입니다. 따라서 프로젝트에 쉽게 포함시킬 수 있으며 .cpp파일 이없는 컴파일 원인이 필요하지 않습니다 .

readme.mdrepo 에서 요청 예 :

#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에서 컴파일되지 않습니다 : /
— uhfocuz

@uhfocuz lib는 iOS 및 Android 용으로 작성되었습니다. 그러나 Win32 용으로 컴파일하는 데 도움을 줄 수 있습니다. 너무 어렵지 않습니다
— fnc12

나는 그것이 나의 사용을 위해 얼마나 가볍고, 그것은 나의 Mac에서 잘 컴파일되었지만 Windows에서 libs는 당신이없는 것처럼 다르 netdb.h므로 도움이 필요합니다
— uhfocuz

@uhfocuz 당신이해야 할 일은 같은 조건 #ifdef _WIN32을 추가하고 거기에 윈도우 특정 코드를 추가하는 것입니다. 봐 여기에 - 유닉스 소켓 및 Windows 소켓 사이에 큰 차이가 없습니다. 1) WSAStartup첫 번째 호출 과 2)closesocketclose
— fnc12

@uhfocuz 내 repo에 문제를 작성하십시오-충분한 시간이 있으면 win32 지원을 추가합니다
— fnc12

3

HTTP 프로토콜은 매우 간단하므로 HTTP 클라이언트를 작성하는 것은 매우 간단합니다. 여기 하나

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

HTTP GET을 사용하여 웹 서버에서 파일을 검색합니다. 서버와 파일은 모두 명령 행 매개 변수입니다. 원격 파일은 로컬 사본에 저장됩니다.

면책 조항 : 나는 저자입니다

수정 : 수정 된 URL


주어진 URL이 유효하지 않습니다.
— Shravan40

3

libcurl, Windows.h 또는 WinSock이 필요하지 않습니다. 라이브러리 컴파일, 프로젝트 구성 등이 없습니다.이 코드는 Windows 10의 Visual Studio 2017 c ++에서 작동합니다.

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

간단한 API 액세스 스크립트를 원했기 때문에 libcurl과 같은 라이브러리로 인해 모든 종류의 문제가 발생했습니다 (방향을 따랐을 때도 ...). WinSock은 너무 낮고 복잡합니다. .

나는 모든 IStream 읽기 코드 에 대해 잘 모르겠지만 (특히 while 조건-수정 / 개선 할 수 있음을 느낍니다), 이봐, 그것은 작동 하고 번거 로움이 없습니다! (그것은, 내가 그에게 의미가 내가 사용하기 때문에 차단 (동기) 호출 이 그 괜찮 bytesRead항상 것> 0U 스트림까지 ( 대해 ISequentialStream는 ?) 완성 된 존재가 읽을 수 있습니다,하지만 누가 알 겠어.)

참조 : URL 모니 커와 비동기 플러그 프로토콜 참조


그것은 밖으로 편집했지만, C 경험의 공정한 금액 후에 ++, 나는이 것을 내 원래의 주장 대기거야 아마 에서 (당신이 이제까지 C ++에서 이런 종류의 일을 할 수있을 것입니다 가장 쉬운 방법은 ... 최소한 지금은 ...)
— Andrew

방금 badssl.com의 URL 몇 개를 사용하여 URLOpenBlockingStream을 테스트했으며 (SSL) 매우 좋지만 이 작업은 실패합니다. 테스트 한 모든 경우 (몇 개만) 위의 코드 출력은 빈 문자열 (스트림 데이터 없음)이됩니다. 꽤 멋지다.
— 앤드류

나는 그것이 가장 쉬운 것이라고 동의하지만 어떻게 http 응답 코드를 얻을 수 있습니까?
— Robin

@ 로빈 하! 메시지 와 응답 코드를 원하십니까?! 문서를 보거나 더 많은 수동 URL 모니 커 자료를 보면 답을 찾을 수 있다는 점을 제외하고는 당신도 알고 있습니다. URLOpenBlockingStream을 수동으로 구현하여 온라인으로 코드를 게시하는 사람이 더 많은 구성을 허용한다고 생각합니다. 당신이 무언가를 알아내는 경우 알려주십시오!
— 앤드류


2

다음은 libCURL을 사용하여 URL의 내용을 다음으로 다운로드하는 간단한 C ++ 11 코드입니다 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

일반적으로 cURL, POCO 또는 Qt와 같은 크로스 플랫폼을 권장합니다. 그러나 Windows 예제는 다음과 같습니다.

#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

Restful 웹 서비스를 사용하기 위해 여러 플랫폼 (Linux, Windows 및 Mac)에서 지원되는 C ++에서 HTTP 클라이언트 라이브러리를 찾고있는 경우 아래 옵션을 가질 수 있습니다.

  1. QT 네트워크 라이브러리 -애플리케이션이 네트워크 요청을 보내고 응답을받을 수 있도록합니다.
  2. C ++ REST SDK -PPL을 지원하는 새로운 타사 HTTP 라이브러리
  3. Libcurl- 아마도 아마도 세계에서 가장 많이 사용되는 http lib 중 하나 일 것입니다.

1

조금 늦었지만. https://github.com/Taymindis/backcurl을 선호 할 수 있습니다 .

그것은 당신이 모바일 C ++ 개발에 http 호출을 할 수 있습니다. 모바일 게임 개발에 적합

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를 사용하여 다음을 수행 할 수 있습니다.

#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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.