사용자 도구

사이트 도구


kb:httpget

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:httpget [2014/11/08 14:57] (현재)
줄 1: 줄 1:
 +====== HTTP / GET ======
 +HTTP를 이용해서 파일을 다운로드받기
 +
 +
 +====== 소켓을 이용하는 방법 ======
 +내부적으로 **GET** 명령어를 이용해서 파일 내용을 받아온다.
 +
 +파일 사이즈만을 알아내기 위해서는 **HEAD** 명령어를 사용하면 된다. 보통 다음과 같은 모양의 데이터가 날아오는데,​ 필요한 부분을 파싱해서 쓰면 된다.
 +<​code>​
 +HTTP/1.1 200 OK 
 +Accept-Ranges:​ bytes 
 +Date: Sat, 27 Jul 2002 10:58:14 GMT 
 +Content-Length:​ 776635 ​
 +Content-Type:​ image/​jpeg ​
 +Server: Apache/​1.3.12 (Unix) mod_ssl/​2.6.5 OpenSSL/​0.9.5a ​
 +Last-Modified:​ Mon, 24 Jul 2000 00:13:24 GMT 
 +ETag: "​9b656-bd9bb-397b8a24" ​
 +Via: 1.1 cache-6100-edi (NetCache NetApp/​5.2.1R1D12) ​
 +</​code>​
 +
 +<code cpp>
 +// GetHTTP.cpp
 +//
 +// Retrieves a file using the Hyper Text Transfer Protocol
 +// and prints its contents to stdout.
 +//
 +// Pass the server name and full path of the file on the 
 +// command line and redirect the output to a file. The program
 +// prints messages to stderr as it progresses.
 +//
 +// Example:
 +//      GetHTTP www.idgbooks.com /index.html > index.html
 +
 +#include <​stdio.h>​
 +#include <​fcntl.h>​
 +#include <​io.h>​
 +#include <​winsock.h>​
 +
 +void GetHTTP(LPCSTR lpServerName,​ LPCSTR lpFileName);​
 +
 +// Helper macro for displaying errors
 +#define PRINTERROR(s) ​  \
 +        fprintf(stderr,"​\n%s:​ %d\n", s, WSAGetLastError())
 +
 +void main(int argc, char **argv)
 +{
 +    WORD wVersionRequested = MAKEWORD(1,​1);​
 +    WSADATA wsaData;
 +    int nRet;
 +
 +    // Check arguments
 +    if (argc != 3)
 +    {
 +        fprintf(stderr,​
 +            "​\nSyntax:​ GetHTTP ServerName FullPathName\n"​);​
 +        return;
 +    }
 +
 +    // Initialize WinSock.dll
 +    nRet = WSAStartup(wVersionRequested,​ &​wsaData);​
 +    if (nRet)
 +    {
 +        fprintf(stderr,"​\nWSAStartup():​ %d\n", nRet);
 +        WSACleanup();​
 +        return;
 +    }
 +    ​
 +    // Check WinSock version
 +    if (wsaData.wVersion != wVersionRequested)
 +    {
 +        fprintf(stderr,"​\nWinSock version not supported\n"​);​
 +        WSACleanup();​
 +        return;
 +    }
 +
 +    // Set "​stdout"​ to binary mode
 +    // so that redirection will work
 +    // for .gif and .jpg files
 +    _setmode(_fileno(stdout),​ _O_BINARY);
 +
 +    // Call GetHTTP() to do all the work
 +    GetHTTP(argv[1],​ argv[2]);
 +
 +    // Release WinSock
 +    WSACleanup();​
 +}
 +
 +void GetHTTP(LPCSTR lpServerName,​ LPCSTR lpFileName)
 +{
 +    // Use inet_addr() to determine if we're dealing with a name
 +    // or an address
 +    IN_ADDR ​    ​iaHost;​
 +    LPHOSTENT ​  ​lpHostEntry;​
 +
 +    iaHost.s_addr = inet_addr(lpServerName);​
 +    if (iaHost.s_addr == INADDR_NONE)
 +    {
 +        // Wasn't an IP address string, assume it is a name
 +        lpHostEntry = gethostbyname(lpServerName);​
 +    }
 +    else
 +    {
 +        // It was a valid IP address string
 +        lpHostEntry = gethostbyaddr((const char *)&​iaHost, ​
 +                        sizeof(struct in_addr), AF_INET);
 +    }
 +
 +    if (lpHostEntry == NULL)
 +    {
 +        PRINTERROR("​gethostbyname()"​);​
 +        return;
 +    }
 +
 +    // Create a TCP/IP stream socket
 +    SOCKET Socket = socket(AF_INET,​ SOCK_STREAM,​ IPPROTO_TCP);​
 +    if (Socket == INVALID_SOCKET)
 +    {
 +        PRINTERROR("​socket()"​); ​
 +        return;
 +    }
 +
 +    // Find the port number for the HTTP service on TCP
 +    LPSERVENT lpServEnt;
 +    SOCKADDR_IN saServer;
 +    lpServEnt = getservbyname("​http",​ "​tcp"​);​
 +    if (lpServEnt == NULL) saServer.sin_port = htons(80);
 +    else                   ​saServer.sin_port = lpServEnt->​s_port;​
 +
 +    // Fill in the rest of the server address structure
 +    saServer.sin_family = AF_INET;
 +    saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->​h_addr_list);​
 +
 +    // Connect the socket
 +    int nRet;
 +    nRet = connect(Socket,​ (LPSOCKADDR)&​saServer,​ sizeof(SOCKADDR_IN));​
 +    if (nRet == SOCKET_ERROR)
 +    {
 +        PRINTERROR("​connect()"​);​
 +        closesocket(Socket);​
 +        return;
 +    }
 +    ​
 +    // Format the HTTP request
 +    char szBuffer[1024];​
 +    sprintf(szBuffer,​ "GET %s\n", lpFileName);​
 +    nRet = send(Socket,​ szBuffer, strlen(szBuffer),​ 0);
 +    if (nRet == SOCKET_ERROR)
 +    {
 +        PRINTERROR("​send()"​);​
 +        closesocket(Socket); ​   ​
 +        return;
 +    }
 +
 +    // Receive the file contents and print to stdout
 +    while(1)
 +    {
 +        // Wait to receive, nRet = NumberOfBytesReceived
 +        nRet = recv(Socket,​ szBuffer, sizeof(szBuffer),​ 0);
 +        if (nRet == SOCKET_ERROR)
 +        {
 +            PRINTERROR("​recv()"​);​
 +            break;
 +        }
 +
 +        fprintf(stderr,"​\nrecv() returned %d bytes",​ nRet);
 +
 +        // Did the server close the connection?
 +        if (nRet == 0) break;
 +
 +        // Write to stdout
 +        fwrite(szBuffer,​ nRet, 1, stdout);
 +    }
 +
 +    closesocket(Socket); ​   ​
 +}
 +</​code>​
 +
 +====== Windows Internet API를 이용한 방법 ======
 +[[WindowsInternetApi]] 페이지의 HTTP 항목을 참고
 +
 +
 +====== 인터넷 익스플로러를 이용하는 방법 ======
 +URLDownloadToFile 함수를 이용하면 인터넷 익스플로러를 통해 파일을 다운로드받을 수 있다. 인터넷 익스플로러가 이어받기/​캐시 처리 등을 자동으로 해주기 때문에 편하다. ​
 +
 +URLDownloadToFile 함수를 이용하기 위해서는 IBindStatusCallback 인터페이스를 상속받은 클래스를 하나 만들어서,​ OnProgress 함수를 구현해줘야한다. OnProgress 함수는 다운로드 상태를 사용자에게 보여주기 위한 콜백 함수다. 사용자에게 진행 상황을 보여주지 않아도 된다면, 빈 상태로 둬도 상관 없지만, 기본적인 에러 처리는 집어넣어 주는 것이 좋다.
 +
 +**StatusCallBack.h**
 +<code cpp>
 +#!cpp
 +#ifndef __STATUSCALLBACK_H__
 +#define __STATUSCALLBACK_H__
 +
 +#include <​windows.h>​
 +#include <​urlmon.h>​
 +#include <​string>​
 +
 +//////////////////////////////////////////////////////////////////////////////​
 +/// \class CCallback
 +/// \brief 인터넷 익스플로러를 이용해 HTTP로 파일을 다운로드받기 위한 ​
 +/// COM 인터페이스 클래스.
 +//////////////////////////////////////////////////////////////////////////////​
 +
 +class CCallback : public IBindStatusCallback
 +{
 +private:
 +    std::string m_URL; ​    ///<​ 다운로드받을 파일의 URL
 +    DWORD       ​m_Timeout;​ ///< 타임아웃
 +
 +public:
 +    /// \brief 생성자
 +    CCallback(const std::​string&​ url, DWORD timeout=INFINITE);​
 +
 +    /// \brief 소멸자
 +    ~CCallback();​
 +
 +public:
 +    STDMETHOD(OnStartBinding)(
 +        /* [in] */ DWORD dwReserved,
 +        /* [in] */ IBinding __RPC_FAR *pib)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(GetPriority)(
 +        /* [out] */ LONG __RPC_FAR *pnPriority)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(OnLowResource)(
 +        /* [in] */ DWORD reserved)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(OnProgress)(
 +        /* [in] */ ULONG ulProgress,
 +        /* [in] */ ULONG ulProgressMax,​
 +        /* [in] */ ULONG ulStatusCode,​
 +        /* [in] */ LPCWSTR wszStatusText);​
 +
 +    STDMETHOD(OnStopBinding)(
 +        /* [in] */ HRESULT hresult,
 +        /* [unique][in] */ LPCWSTR szError)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(GetBindInfo)(
 +        /* [out] */ DWORD __RPC_FAR *grfBINDF,
 +        /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(OnDataAvailable)(
 +        /* [in] */ DWORD grfBSCF,
 +        /* [in] */ DWORD dwSize,
 +        /* [in] */ FORMATETC __RPC_FAR *pformatetc,​
 +        /* [in] */ STGMEDIUM __RPC_FAR *pstgmed)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD(OnObjectAvailable)(
 +        /* [in] */ REFIID riid,
 +        /* [iid_is][in] */ IUnknown __RPC_FAR *punk)
 +        { return E_NOTIMPL; }
 +
 +    STDMETHOD_(ULONG,​ AddRef)() { return 0; }
 +    STDMETHOD_(ULONG,​ Release)() { return 0; }
 +
 +    STDMETHOD(QueryInterface)(
 +    /* [in] */ REFIID riid,
 +    /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
 +        { return E_NOTIMPL; }
 +};
 +
 +#endif //​__BINDSTATUSCALLBACK_H__
 +</​code>​ **<​code>​StatusCallBack.h</​code>​**
 + <​code>​
 +#!cpp
 +#include "​StatusCallback.h"​
 +#include <​shlwapi.h>​
 +#include <​mmsystem.h>​
 +#include <​iostream>​
 +#include <​assert.h>​
 +
 +//////////////////////////////////////////////////////////////////////////////​
 +/// \brief 생성자
 +/// \param url 다운로드받을 파일의 URL
 +/// \param timeout 타임아웃
 +//////////////////////////////////////////////////////////////////////////////​
 +CCallback::​CCallback(const std::​string&​ url, DWORD timeout)
 +: m_URL(url), m_Timeout(timeout)
 +{
 +    assert(!m_URL.empty());​
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////​
 +/// \brief 소멸자
 +//////////////////////////////////////////////////////////////////////////////​
 +CCallback::​~CCallback()
 +{
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////​
 +/// \brief 진행 상황을 알려주기 위해 인터넷 익스플로러가 호출하게 되는
 +/// 콜백 함수
 +///
 +/// \param ulProgress 다운로드받은 바이트 수
 +/// \param ulProgressMax 다운로드받아야할 전체 바이트 수
 +/// \param ulStatusCode 상태 코드
 +/// \param wszStatusText 상태 문자열
 +/// \return HRESULT 인터넷 익스플로러에게 알려줘야하는 결과값. 다운로드를
 +/// 계속해야하는 경우에는 S_OK를 반환해야하고,​ 중단하려면 E_ABORT를 반환해야 ​
 +/// 한다.
 +//////////////////////////////////////////////////////////////////////////////​
 +HRESULT STDMETHODCALLTYPE CCallback::​OnProgress(ULONG ulProgress, ​
 +                                                ULONG ulProgressMax,​
 +                                                ULONG ulStatusCode, ​
 +                                                LPCWSTR wszStatusText)
 +{
 +    // 이 함수 내부에서 혹시 사용자가 중지 버튼을 눌렀다던가, ​
 +    // 타임아웃이 되었다던가,​ ulStatusCode가 이상하던가 하면,
 +    // E_ABORT를 반환해서,​ 다운로드를 중단해야 한다.
 +    ​
 +    if (m_Timeout < timeGetTime())
 +    {
 +        std::cout << "​대기 시간이 초과되었습니다"​ << std::endl;
 +        return E_ABORT;
 +    }
 +    ​
 +    // 다운로드를 중지해야하는 상황이 아니라면, ​
 +    // ulProgress 값과, ulProgressMax 값을 이용해 ​
 +    // 프로그레스바 등을 적당히 업데이트하면 된다.
 +    if (0 != ulProgressMax)
 +    {
 +        std::cout << "​다운로드 중입니다 - " << m_URL 
 +            << "​("​ << int( 100.0 * ulProgress / ulProgressMax) ) << "​%)"​ << std::endl;
 +    }
 +    else
 +    {
 +        std::cout << "​다운로드 중입니다 - " << m_URL << std::endl;
 +    }
 +
 +    return S_OK;
 +}
 +</​code>​
 +
 +**샘플**
 +<code cpp>
 +string URL = "​http://​somewhere.com/​something.zip";​
 +string filename = "​c:​\something.zip";​
 +
 +CCallback callback(URL,​ timeGetTime() + 60 * 1000);
 +HRESULT hr = ::​URLDownloadToFile(
 +    NULL, URL.c_str(),​ filename.c_str(),​ 0, &​callback);​
 +</​code>​
 +
 +:!: [[http://​www.codeproject.com/​internet/​urldownload.asp | CodeProject]]에 괜찮은 예제가 있으니 이를 참고하면 좋다.
 +
 +:!: 캐시에 있는 파일을 무시하고,​ 새로 받기를 원한다면 DeleteUrlCacheEntry 함수를 이용하자.
  
kb/httpget.txt · 마지막으로 수정됨: 2014/11/08 14:57 (바깥 편집)