2009년 9월 1일 화요일

MFC 인터넷 프로토콜 프로그래밍

WinInet API


WinInet(The Microsoft Win32 Internet functions) 클래스는 Win32

반의 WinInet API들을 캡슐화하고 있는 클래스의 모임을 말하며 클

라이언트 쪽에서 인터넷의 표준 프로토콜인 HTTP, Gopher, FTP

등을 지원하기 위한 클래스이다. MFC에서 제공하는 WinInet 관련

클래스들은 네트워크상에서 발생하는 여러 가지 복잡한 처리 작업

을 내부적으로 수행하고 상당히 안정적인 코드를 제공한다. 이러한

프로토콜을 사용한 전송을 쉽게 할 수 있도록 운영체제 확장의 일부

로 wininet.dll을 제공한다. MFC 응용 프로그램에서 wininet.dll은

CInternetSession 개체에 의해 표현된다. CInternetSession은 작업

자가 인터넷에 정확히 어떻게 연결하려고 했는지 wininet.dll에게

알리도록 한다.

 

CInternetSession(LPCTSTR pstrAgent = NULL, DWORD dwCont

ext = 1, DWORD dwAccessType = INTERNET_OPEN_TYPE_PRE

CONFIG, LPCTSTR pstrProxyName = NULL, LPCTSTR pstrProxy

Bypass = NULL, DWORD dwFlags = 0);

 

첫 번째 인자는 클라이언트 쪽 소프트웨어 이름을 가리키는 포인터

이다. 디폴트값 NULL은 생성자가 AfxGetAppName() 호출을 통해

응용프로그램의 이름을 찾게 할 것이다. dwAccessType 인자는 Wi

nInet에게 인터넷을 어떻게 연결할 계획인지 알려준다. 디폴트 값인

INTER_OPEN_TYPE_PRECONFIG는 레지스트리에서 인터넷 연결

에 관하여 이전에 입력된 정보를 찾는다. 인트라넷을 사용하고 있거

나 컴퓨터가 인터넷에 직접 연결되어 있다면 INTERNET_OPEN_TY

PE_DIRECT를 지정할 수 있다. 방화벽을 통한 연결은 GATEWAY_I

NTERNET_ACCESS, 프록시를 통해 인터넷에 요청을 전달하려고

하려면 INTERNET_OPEN_TYPE_PROXY를 지정한다. 이것을 지정

하지 않으면 pstrProxyName과 pstrProxyBypass 인자를 NULL로

지정할 수 있다. 그리고 dwFlags 인자를 사용하여 다른 방법으로

연결에 영항을 줄 수 있다. 비동기 동작을 지원하는 INTERNET_FL

AG_ASYNC 플래그와 세션을 통해 기존 연결을 재사용하려는 INTE

R_FLAG_EXITING_CONNECT 플래그가 OR 연산자를 사용하여 결

합된다.

 

dwContext라는 인자가 있는데 인터넷에 있는 다른 컴퓨터와 대화

하는 것은 오랜 시간이 걸릴 수 있다. 일부 프로토콜 등은 많은 오버

헤드를 발생시키고, 인터넷 응용 프로그램의 사용자들은 종종 느린

링크를 통해서 연결된다. 따라서 윈도우 인터넷 API는 언제, 어떤

것이 진행되었는지 응용프로그램이 알 수 있게 하는 콜백을 제공한

다. dwContext 인자는 단순히 응용 프로그램이 실행하고 있는 각

동작들을 구분할 수 있게 해준다. MFC는 콜백 함수처럼 복잡한 것

들에 대해 염려하지 않아도 되게 해준다. CInternetSession으로부

터 클래스를 유도하고 OnStatusCallback() 함수를 오버라이드할

수 있다.


또 CInternetSession 듀도 개체에 EnableStatusCallback() 함수를

호출하여 상태 콜백을 받고 싶어한다는 것을 시스템에게 알릴 필요

가 있다. 이 인자가 TRUE(디폴트)이면 함수가 콜백을 활성화시킬

것이고, FALSE이면 인자가 콜백을 비활성화시킬 것이다. CInternet

Session을 열고 FTP 프로토콜을 이용하여 파일을 전송할 경우 Get

FtpConnection() 멤버를 호출함으로써 CFtpConnection 개체를 생

성할 수 있다. GetFtpConnection()은 이 새 CFtpConnection을 가

리키는 포인터를 반환한다.


CFtpConnection* GetFtpConnection( LPCTSTR pstrServer, LPC

TSTR pstrUserName = NULL, LPCTSTR pstrPassword = NULL, I

NTERNET_PORT nPort = INTERNET_INVALID_PORT_NUMBER, B

OOL bPassive = FALSE );

 

첫 번째 인자는 연결하고자 하는 서버의 이름이고 두 번째 인자와

세 번째 인자는 연결에 사용될 사용자 이름과 암호를 나타낸다. 디

폴트 값인 NULL을 전달하면 익명(anonynous)으로 접속요청을 하

게 된다. nPort 인자는 포트 번호를 나타내는데 디폴트 값인 INTER

NET_INVALID_PORT_NUMBER을 사용하면 이 프로토콜의 디폴트

TCP/IP 포트 번호(HTTP:80, FTP:21, Gopher:70, HTTPS:443)를 사

용한다. 이를 이용한 FTP 서버 접속은 리스트 3과 같다.

 

리스트 3 : FTP 서버 접속
------------------------------------------------------------

CInternetSession m_Session;

CFtpConnection *m_pConnection = NULL;
try
{
//Anonymous 로 로그인
m_pConnection = m_Session.GetFtpConnection("
ftp.pserang.co.

kr");
// 사용자명과 패스워드 로그인
/* m_pConnnection = m_Session.GetFtpConnection("
ftp.pseran

g.co.kr","User_Name","Password"); */
//연결시 작업
m_pConnection->Close();
}
catch (CInternetException *m_pEx)
{
m_pEx->ReportError(MB_ICONEXCLAMATION);
m_pConnection = NULL;
m_pEx->Delete();
}
delete m_pConnection;

------------------------------------------------------------CInternetException은 운영체제로부터 문제를 기술하는 에러 텍스

트를 얻어내는 에외처리 클래스이다. 연결이 이루어 졌을 때 GetCu

rrentDirectory()를 호출하여 FTP 컴퓨터의 현재 디렉토리를 얻을

수 있고 SetCurrentDirectory()로 상대편 디렉토리를 설정할 수 있

다. 그리고 GetFile()로 파일을 다운로드 할 수 있고 PutFile()로 파

일을 업로드 할 수 있다. 사용자에게 권한이 주어지면 CreateDirect

ory()/RemoveDirectory() 및 Remove()/Rename()를 호출하여 디

렉토리를 생성/추가할 수 있고 파일을 제거/이름변경을 할 수 있다.

예를 들어 파일을 다운로드 할 때의 코드는 다음과 같다.

 

m_pConnection->GetFile("RemoteFile", "localFile", FALSE,

                FILE_ATTRIBUTE_NORMAL,
                FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_

                RELOAD | INTERNET_FLAG_NO_CACHE_WRITE);

 

네 번째 인자(DWORD dwAttributes)는 파일 복사 후 로컬 컴퓨터에

생성되는 파일의 속성을 나타낸다. 읽기 전용은 FILE_ATTRIBUTE_

READONLY로 지정할 수 있다. 다섯 번째 인자(DWORD dwFlags)

는 원시 이진 파일들을 처리할 수 없는 연결을 통해 올바로 데이터

를 전달하기 위해 FTP 프로토콜에 사용되는 의미를 바꾼다. CFtpFi

leFind란 클래스가 있는데 FTP 서버를 검색하는 기능을 수행한다.

이 클래스는 CFileFind 클래스에서 파생되었다. CFtpFileFind 클래

스는 두 개의 인자가 있는데 첫 번째 인자는 검색할 서버를 위한 CF

tpConnectiondmf 가리키는 포인터이고 두 번째 인자는 dwContext

값을 나타낸다.

 

CFtpFileFind *m_pFileFind = NULL;
m_pFileFind = new CFtpFileFind(m_pConnection);
BOOL bContinue = TRUE;
CString m_file_name;
m_pFileFind->FindFile("RemoteDir");
while(bContinue)
{
bContinue = m_pFileFind->FindNextFile();
m_file_name = m_pFileFind->GetFileName();
if(m_pFileFind->IsDirectory())
{
//디렉토리일 경우 루틴
}
// 파일인 경우
else
{
//파일일 경우 루틴
}
}

FTP 서버로 연결이 되면 CFtpFileFind 클래스를 이용하여 지정된

경로의 파일을 계속 찾는 구문이다. HTTP 서버 즉, 일반적으로 말

하는 웹 서버에 접속하기 위해서 MFC 계층에서 CFtpConnection

과 비슷한 위치를 차지하는 CHttpConnection를 사용해 연결할 수

있다. CInternetSession의 GetHtpConnection() 멤버를 사용하여 C

HttpConnection을 열 수 있다.

 

CHttpConnection* GetHttpConnection( LPCTSTR pstrServer, INT

ERNET_PORT nPort = INTERNET_INVALID_PORT_NUMBER, LPC

TSTR pstrUserName = NULL, LPCTSTR pstrPassword = NULL );

 

각 인자는 GetFtpConnection()과 거의 동일하다. 웹서버에 접속하

는 코드는 다음과 같다.

 

CInternetSession m_Session;
CHttpConnection *m_pHttpConnection = NULL;
CHttpFile *m_pFile = NULL;
try
{
m_pHttpConnection = m_Session.GetHttpConnection("
www.pser

ang.co.kr");
m_pFile = m_pHttpconnection->OpenRequest(CHttpConnection::

HTTP_VERB_GET,"/");
m_pFile -> AddRequestHeaders("Accept:text/*\r\nAccept-La

nguage:kr\r\n");
m_pFile ->SendRequest();
//...
}
catch(CInternetException *m_pEx)
{
m_pEx->ReprotError();
m_pEx->Delete();
}
delete m_pFile;
delete m_pHttpConnection;

 

CHttpFile이라는 것이 있는데 CInternetFile로부터 유도되었고 CInt

ernetFile은 CStdioFile로부터 유도되었다. CHttpFile의 특징은 자신

만의 파일 데이터 버퍼링을 수행한다. SetReadBufferSize()를 호출

하여 버퍼 크기를 설정할 수 있고, 버퍼를 설정하지 않고서 ReadStr

ing()을 사용하면 자동으로 버퍼가 설정된다. 위 코드 중 서버에게

요청하는 OpenRequest()의 첫 번째 인자는 CHttpConnection 내부

의 enum에서 정의된 기호이다. 이 인자는 요청에서 사용하고자 하

는 메소드를 지정한다. HTTP_VERB_GET이라는 메소드는 서버에

있는 개체의 전체 내용을 요청하는데 쓰인다. 그 외 특정 개체와 연

결된 개체를 전달하고자 할 때는 HTTP_VERB_POST 인자가 쓰이

고 마지막 인자인 POST 대신 HEAD, PUT, LINK, DELETE, UNLINK

등이 사용된다. 

요청은 CHttpFile::SendRequest() 호출과 함께 전달된다. 요청을

전달하기 전에 헤더를 추가하고 싶으면 CHttpFile::AddHeaders()

를 호출할 수 있다. 서버가 요청에 대한 개체를 갖고 있지만 클라이

언트의 Accept: 헤더에서 지정된 형식 요청을 충족시킬 수 없으면

일반 "404 Not Found"(HTTP_STATUS_NOT_FUOND: 404)" 에러와

"HTTP_STATUS_NONE_ACCEPTABLE: 406" 에러를 반환할 것이

다. 요청이 전달되고 반응이 성공적으로 수신되었으면 SendReque

st()가 TRUE를 반환한다. 그렇지 않으면 예외가 발생할 것이다. 이

것은 내부 서버 에러가 있다는 것을 의미한다. CHttpFile::QueryInfo

StatusCode()를 호출함으로써 CHttpFile 개체로 들어온 정보로부

터 반환 코드를 커낼 수 있다.

 

m_pFile->SendRequest();
DWORD m_dwStatusCode;
m_pFile->QueryInfoStatusCode(m_dwStatusCode);

 

m_dwStatusCode는 요청 결과에 반영하는 정수를 갖고 있다. 예를

들어 200은 HTTP_STATUS_OK를 나타내며 요청이 게이트웨이로

갔지만 게이트웨이와 서버와의 통신이 타임아웃일 경우는 504(HTT

P_STATUS_GATEWAY_TIMEOUT)를 나타낸다
리스트 4는 프록시를 통하여 HTTP 파일을 다운로드 하는 코드이다.

댓글 없음:

댓글 쓰기