급하게 필요해 날림으로 하루만에 뚝딱 만들었습니다.
아주 기본적인 동작만 합니다.
청크드 모드는 아직 구현되지 않았습니다.
-- History -------------------------------------------------------------------------
-----------------------------------------------------------------------------------
아주 기본적인 동작만 합니다.
청크드 모드는 아직 구현되지 않았습니다.
-- History -------------------------------------------------------------------------
2006.08.31 : 생성 by newtype
2006.11.24 : CHttpSocket::Request 메모리 릭 수정.
2007.03.31 : CHttpReqHeader::toString 메모리 덮어 쓰는 버그 수정
Windows에서도 컴파일 되게 수정
2007.05.29 : Connection 시점에도 timeout 적용(nonblocking socket사용)
2011.05.13 : Transfer-Encoding: chunked 모드 지원
-----------------------------------------------------------------------------------
bool httpRequest(const char *url, CHttpBody &body)
{
CHttpReqHeader reqHeader;
CHttpResHeader resHeader;
CHttpBody reqBody;
CHttpSocket http( 1000000 ); // timeout 1초
char host[MAXLENGTH]={0,};
int len =0;
int result;
sprintf( host, "%s:%d", m_host, m_port ); // 접속할 ip, port
// 인증을 위한 http 해더 생성
reqHeader.setHeader( "GET", (char*)url, "HTTP/1.1" );
reqHeader.add( "Host", host );
reqHeader.add( "Accept", "text/xml" );
len = strlen( errMsg );
snprintf( errMsg +len, sizeof(errMsg) -len, " Host[%s], URL[%s]", host, url );
len = strlen( errMsg );
// 연결
if ( http.Connection( m_host, m_port ) < 0 )
{
snprintf( errMsg + len, sizeof(errMsg)-len,
" Connection Fail(%s:%d) ", m_host, m_port );
return false;
}
// 요청
result = http.Request( reqHeader, reqBody );
if ( result == SOCKERR_ERROR )
{
snprintf( errMsg + len, sizeof(errMsg)-len,
" Request Fail(H=[%s] B=[%s]) ",
reqHeader.toString(), reqBody.toString() );
return false;
}
else if ( result == SOCKERR_TIMEOUT )
{
snprintf( errMsg + len, sizeof(errMsg)-len, " Request Timeout");
return false;
}
// 응답
result = http.Response( resHeader, body );
if ( result == SOCKERR_ERROR )
{
snprintf( errMsg + len, sizeof(errMsg)-len,
" Response Fail(H=[%s] B=[%s]) ",
resHeader.toString(), body.toString() );
return false;
}
else if ( result == SOCKERR_TIMEOUT )
{
snprintf( errMsg + len, sizeof(errMsg)-len, " Response Timeout");
return false;
}
return true;
}
/*CHttpSocket.hxx*/
/**********************************************************************
*
* * CSocket class
*
*
* * Capacity
* - recv, send 함수를 재정의해서 원하는 크기 만큼 받을 때까지 loop처리
* - select를 이용해 timeout처리
* - http Request, Respone 구현
*
*
* * History
* - 2006.08.31 : 생성 by newtype
* - 2006.11.24 : CHttpSocket::Request 메모리 릭 수정.
* - 2007.03.31 : CHttpReqHeader::toString 메모리 덮어 쓰는 버그 수정
* Windows에서도 컴파일 되게 수정
* - 2007.05.29 : Connection 시점에도 timeout 적용(nonblocking socket사용)
* - 2011.05.13 : Transfer-Encoding: chunked 모드 지원
*
**********************************************************************/
#ifndef __CHTTPSOCKET_HEADER_430DFF12_BB12_4922_B61B_C191E71F9D7F__
#define __CHTTPSOCKET_HEADER_430DFF12_BB12_4922_B61B_C191E71F9D7F__
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <Windows.h>
#endif
#include <stdlib.h>
#define SOCKERR_TIMEOUT -2
#define SOCKERR_ERROR -1
#define MAX_HEADER 100 // 해더 item 총 갯수
#define MAXBUF 4096 // 소캣 송수신의 최대 크기
#ifndef NULL
#define NULL 0
#endif
#ifdef WIN32
#define close closesocket
#define strlen (int)strlen
#define socklen_t int
#define EINPROGRESS WSAEINPROGRESS
#define EWOULDBLOCK WSAEWOULDBLOCK
#define ETIMEDOUT WSAETIMEDOUT
#pragma warning(disable : 4996)
#endif
class CNameValue
{
public:
CNameValue();
CNameValue(char *name, char *value);
~CNameValue();
void release();
void set(char *name, char *value);
char* getName();
char* getValue();
protected:
void initMember();
char *m_name;
int m_nameLength;
char *m_value;
int m_valueLength;
};
class CnvList
{
public:
CnvList();
~CnvList();
void add(char *name, char *value);
char* getValue(char *name);
void getItem(int index, char *name, int lengthName, char *value, int lengthValue);
int getSize();
protected:
CNameValue *m_list[MAX_HEADER];
int m_count;
};
class CHttpHeader : public CnvList
{
public:
CHttpHeader();
~CHttpHeader();
virtual void release();
virtual bool fromString( char *s )=0;
virtual void setHeader( char *sp1, char *sp2, char *sp3 )=0;
virtual char* toString()=0;
int getStringSize();
enum HttpMethod { GET = 0, POST };
protected:
char *m_header;
int m_size;
};
class CHttpReqHeader : public CHttpHeader
{
public:
CHttpReqHeader();
CHttpReqHeader(char *method, char *url, char *version);
~CHttpReqHeader();
virtual void release();
virtual bool fromString( char *s );
virtual void setHeader( char *method, char *url, char *version );
virtual char* toString();
protected:
void initMember();
private:
char *m_method;
char *m_url;
char *m_version;
};
class CHttpResHeader : public CHttpHeader
{
public:
CHttpResHeader();
CHttpResHeader(char *version, char *resultCode, char *resultMsg);
~CHttpResHeader();
virtual void release();
virtual bool fromString( char *s );
virtual void setHeader(char *version, char *resultCode, char *resultMsg);
virtual char* toString();
int getResultCode();
protected:
void initMember();
private:
char *m_version;
int m_resultCode;
char *m_resultMsg;
};
class CHttpBody
{
public:
CHttpBody();
~CHttpBody();
void set(char *body, int size);
char* toString();
int getStringSize();
private:
char *m_body;
int m_size;
};
class CHttpSocket
{
public:
CHttpSocket();
CHttpSocket(int usecTimeout);
~CHttpSocket();
void init(int usecTimeout);
int Connection( const char* host, int port);
int Request( CHttpReqHeader &header, CHttpBody &body );
int Response( CHttpResHeader &header, CHttpBody &body );
void Close();
protected:
void initMember();
int Send(char *data, int size);
int Receive(char *data, int size);
int recvHeader( CHttpResHeader &header );
int recvBody( CHttpBody &body );
int recvBody( CHttpBody &body, int size );
private:
int connect_nonb(int sockfd, const struct sockaddr *saptr,
socklen_t salen, struct timeval tval);
int sendEx(int sock, char *data, int size);
int recvEx(int sock, char *data, int size);
int getChunkedLength();
char* m_host;
int m_port;
struct timeval m_timeout;
int m_socket;
};
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CHttpSocket.hxx"
/********************************************************
* CLASS CNameValue
*
* Name, Value로 이뤄진 구조체
********************************************************/
CNameValue::CNameValue()
{
initMember();
}
CNameValue::CNameValue(char *name, char *value)
{
initMember();
set( name, value );
}
CNameValue::~CNameValue()
{
release();
}
void CNameValue::initMember()
{
m_name = NULL;
m_nameLength = 0;
m_value = NULL;
m_valueLength =0;
}
void CNameValue::release()
{
if ( m_name != NULL )
delete [] m_name;
if ( m_value != NULL )
delete [] m_value;
m_name = NULL;
m_nameLength = 0;
m_value = NULL;
m_valueLength =0;
}
void CNameValue::set(char *name, char *value)
{
if ( m_name != NULL )
delete [] m_name;
if ( m_value != NULL )
delete [] m_value;
m_nameLength = strlen(name);
m_name = new char[m_nameLength+1]; // NULL 포함
memset( m_name, 0, m_nameLength+1);
strncpy( m_name, name, m_nameLength );
m_valueLength = strlen(value);
m_value = new char[m_valueLength +1]; // NULL 포함
memset( m_value, 0, m_valueLength +1 );
strncpy( m_value, value, m_valueLength );
}
char* CNameValue::getName()
{
return m_name;
}
char* CNameValue::getValue()
{
return m_value;
}
/********************************************************
* CLASS CnvList
*
* CNameValue 리스트
********************************************************/
CnvList::CnvList()
{
memset( m_list, 0, sizeof(m_list) );
m_count = 0;
}
CnvList::~CnvList()
{
int i;
for(i=0; i<m_count; i++)
{
m_list[i]->release();
delete m_list[i];
m_list[i] = NULL;
}
m_count = 0;
}
void CnvList::add(char *name, char *value)
{
m_list[m_count] = new CNameValue();
m_list[m_count]->set( name, value );
m_count++;
}
char* CnvList::getValue(char *name)
{
int i;
for(i=0; i<m_count; i++)
{
if( strcmp( name, m_list[i]->getName() ) == 0 )
return m_list[i]->getValue();
}
return NULL;
}
int CnvList::getSize()
{
int i, size=0;;
for(i=0; i<m_count; i++)
{
size += strlen(m_list[i]->getName()) +2; // ": "
size += strlen(m_list[i]->getValue()) +2; // "\r\n"
}
return size;
}
void CnvList::getItem(int index, char *name, int lengthName,
char *value, int lengthValue)
{
strncpy( name, m_list[index]->getName(), lengthName );
strncpy( value, m_list[index]->getValue(), lengthValue );
}
/********************************************************
* CLASS CHttpHeader
********************************************************/
CHttpHeader::CHttpHeader()
{
m_header = NULL;
m_size = 0;
}
CHttpHeader::~CHttpHeader()
{
release();
}
void CHttpHeader::release()
{
if ( m_header != NULL )
delete [] m_header;
m_header = NULL;
m_size = 0;
}
int CHttpHeader::getStringSize()
{
return m_size;
}
/********************************************************
* CLASS CReqHttpHeader
********************************************************/
CHttpReqHeader::CHttpReqHeader()
{
initMember();
}
CHttpReqHeader::CHttpReqHeader(char *method, char *url, char *version)
{
initMember();
setHeader( method, url, version );
}
CHttpReqHeader::~CHttpReqHeader()
{
release();
}
void CHttpReqHeader::initMember()
{
m_method = NULL;
m_url = NULL;
m_version = NULL;
}
void CHttpReqHeader::release()
{
CHttpHeader::release();
if ( m_method != NULL )
delete [] m_method;
if ( m_url != NULL )
delete [] m_url;
if ( m_version != NULL )
delete [] m_version;
initMember();
}
void CHttpReqHeader::setHeader( char *method, char *url, char *version )
{
release();
m_method = new char[strlen(method) +1]; // NULL 포함
memset( m_method, 0, strlen(method) +1);
strcpy( m_method, method );
m_url = new char[strlen(url) +1]; // NULL 포함
memset( m_url, 0, strlen(url) +1);
strcpy( m_url, url );
m_version = new char[strlen(version) +1]; // NULL 포함
memset( m_version, 0, strlen(version) +1);
strcpy( m_version, version );
}
inline char *strstrEx( char *org, char *fi )
{
if ( org == NULL || org[0] == NULL )
return NULL;
if ( fi == NULL || fi[0] == NULL )
return org;
int i;
int max = strlen(org) - strlen(fi) +1;
int len = strlen(fi);
for( i=0; i<max; i++)
{
if( strncmp( org+i, fi, len ) == 0 )
return org +i;
}
return NULL;
}
bool CHttpReqHeader::fromString( char *s )
{
int length = strlen(s);
char *spos = NULL, *epos = NULL;
char *name = NULL;
char *value = NULL;
release();
// Method
spos = strstrEx( s, " " );
if ( spos <= 0 || s+length < spos )
return false;
m_method = new char[spos - s +1]; // NULL 포함
memset( m_method, 0, spos - s +1);
strncpy( m_method, s, spos - s );
spos++; // 공백 다음
// url
epos = strstrEx( spos, " " );
if( epos <=0 || s+length < epos )
return false;
m_url = new char[epos - spos +1]; // NULL 포함
memset( m_url, 0, epos - spos +1);
strncpy( m_url, spos, epos - spos );
spos = epos +1; // 공백 다음
// version
epos = strstrEx( spos, "\r\n" );
if( epos <=0 || s+length < epos )
return false;
m_version = new char[epos - spos +1]; // NULL 포함
memset( m_version, 0, epos - spos +1);
strncpy( m_version, spos, epos - spos );
spos = epos +2; // "\r\n" 포함
// ": "를 기준으로 파싱해 name, value를 넣는다.
name = new char[length+1];
memset( name, 0, length+1 );
value = new char[length+1];
memset( value, 0, length+1 );
// "\r\n"이 두번 나오면 해더 끝!
while( spos[0] != '\r' && spos[1] != '\n' )
{
if ( s+length < spos )
break;
// name
epos = strstrEx( spos, ": ");
if( epos <=0 || s+length < epos )
break;
strncpy( name, spos, epos -epos );
spos = epos +2; // ": " 포함
//value
epos = strstrEx( spos, "\r\n" );
if( epos <=0 || s+length < epos )
break;
strncpy( value, spos, epos -epos );
spos = epos +2; // "\r\n"
add( name, value );
}
delete [] name;
name = NULL;
delete [] value;
value = NULL;
return true;
}
char* CHttpReqHeader::toString()
{
int i;
if ( m_header == NULL )
{
// 해더 크기 계산
m_size = strlen(m_method) + strlen(m_url) + strlen(m_version) +4; // " \r\n"
m_size += getSize();
// 해더 만들기
m_header = new char[m_size+1]; // NULL 포함
memset( m_header, 0, m_size+1 );
sprintf( m_header, "%s %s %s\r\n", m_method, m_url, m_version );
for(i=0; i<m_count; i++)
{
strcat( m_header, m_list[i]->getName() );
strcat( m_header, ": " );
strcat( m_header, m_list[i]->getValue() );
strcat( m_header, "\r\n" );
}
}
int len = strlen(m_header);
return m_header;
}
/********************************************************
* CLASS CHttpResHeader
********************************************************/
CHttpResHeader::CHttpResHeader()
{
initMember();
}
CHttpResHeader::CHttpResHeader(char *version, char *resultCode, char *resultMsg)
{
initMember();
setHeader( version, resultCode, resultMsg );
}
CHttpResHeader::~CHttpResHeader()
{
release();
}
void CHttpResHeader::initMember()
{
m_version = NULL;
m_resultCode = 0;
m_resultMsg = NULL;
}
void CHttpResHeader::release()
{
CHttpHeader::release();
if ( m_version != NULL )
delete [] m_version;
if ( m_resultMsg != NULL )
delete [] m_resultMsg;
initMember();
}
void CHttpResHeader::setHeader( char *version, char *resultCode, char *resultMsg )
{
release();
m_version = new char[strlen(version) +1]; // NULL 포함
memset( m_version, 0, strlen(version) +1 );
strcpy( m_version, version );
m_resultMsg = new char[strlen(resultMsg) +1]; // NULL 포함
memset( m_resultMsg, 0, strlen(resultMsg) +1);
strcpy( m_resultMsg, resultMsg );
m_resultCode = atoi(resultCode);
}
bool CHttpResHeader::fromString( char *s )
{
int length = strlen(s);
char *spos = NULL, *epos = NULL;
char *name = NULL;
char *value = NULL;
release();
// Version
spos = strstrEx( s, " " );
if ( spos <= 0 || s+length < spos )
return false;
m_version = new char[spos - s +1]; // NULL 포함
memset( m_version, 0, spos - s +1 );
strncpy( m_version, s, spos - s );
spos++; // 공백 다음
// Result Code
epos = strstrEx( spos, " " );
if( epos <=0 || s+length < epos )
return false;
value = new char[epos - spos +1]; // NULL 포함
memset( value, 0, epos - spos +1);
strncpy( value, spos, epos - spos );
m_resultCode = atoi( value );
delete [] value;
value = NULL;
spos = epos +1; // 공백 다음
// Result Msg
epos = strstrEx( spos, "\r\n" );
if( epos <=0 || s+length < epos )
return false;
m_resultMsg = new char[epos - spos +1]; // NULL 포함
memset( m_resultMsg, 0, epos - spos +1);
strncpy( m_resultMsg, spos, epos - spos );
spos = epos +2; // "\r\n" 포함
// ": "를 기준으로 파싱해 name, value를 넣는다.
name = new char[length+1];
memset( name, 0, length+1 );
value = new char[length+1];
memset( value, 0, length+1 );
// "\r\n"이 두번 나오면 해더 끝!
while( spos[0] != '\r' && spos[1] != '\n' )
{
if ( s+length < spos )
break;
// name
epos = strstrEx( spos, ": ");
if( epos <=0 || s+length < epos )
break;
memset( name, 0, length);
strncpy( name, spos, epos -spos );
spos = epos +2; // ": " 포함
//value
epos = strstrEx( spos, "\r\n" );
if( epos <=0 || s+length < epos )
break;
memset( value, 0, length);
strncpy( value, spos, epos -spos );
spos = epos +2; // "\r\n"
add( name, value );
}
delete [] name;
name = NULL;
delete [] value;
value = NULL;
return true;
}
char* CHttpResHeader::toString()
{
int i;
char buf[100];
if ( m_header == NULL )
{
sprintf( (char*)buf, "%d", m_resultCode );
// 해더 크기 계산
m_size = strlen(m_version) + strlen(buf) + strlen(m_resultMsg) +4; // " \r\n"
m_size += getSize();
// 해더 만들기
m_header = new char[m_size+1];
memset( m_header, 0, m_size+1 );
sprintf( m_header, "%s %s %s\r\n", m_version, buf, m_resultMsg );
for(i=0; i<m_count; i++)
{
strcat( m_header, m_list[i]->getName() );
strcat( m_header, ": " );
strcat( m_header, m_list[i]->getValue() );
strcat( m_header, "\r\n" );
}
}
return m_header;
}
int CHttpResHeader::getResultCode()
{
return m_resultCode;
}
/********************************************************
* CLASS CHttpBody
********************************************************/
CHttpBody::CHttpBody()
{
m_body = NULL;
m_size = 0;
}
CHttpBody::~CHttpBody()
{
if ( m_body != NULL )
delete [] m_body;
m_body = NULL;
m_size = 0;
}
void CHttpBody::set(char *body, int size)
{
m_size = size;
m_body = new char[m_size+1];
memcpy( m_body, body, m_size );
m_body[m_size] = 0;
}
char* CHttpBody::toString()
{
return m_body;
}
int CHttpBody::getStringSize()
{
return m_size;
}
/********************************************************
* CLASS CHttpSocket
* - recv, send 함수를 재정의해서 원하는 크기 만큼 받을 때까지 loop처리
* - select를 이용해 timeout처리
* - http Request, Respone 구현
* - 일단은 GET 방식으로만 구현
********************************************************/
CHttpSocket::CHttpSocket()
{
#ifdef WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
return;
}
#endif
initMember();
}
CHttpSocket::CHttpSocket(int usecTimeout)
{
#ifdef WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
return;
}
#endif
initMember();
init( usecTimeout );
}
CHttpSocket::~CHttpSocket()
{
if ( m_host != NULL )
delete [] m_host;
m_host = NULL;
if ( m_socket != 0 )
Close();
#ifdef WIN32
WSACleanup();
#endif
}
void CHttpSocket::initMember()
{
m_host = NULL;
m_port = 0;
memset( &m_timeout, 0, sizeof(m_timeout) );
m_timeout.tv_usec = 0;
m_timeout.tv_sec = 10;
m_socket = 0;
}
void CHttpSocket::init(int usecTimeout)
{
memset( &m_timeout, 0, sizeof(m_timeout) );
if ( usecTimeout >= 1000000 )
{
m_timeout.tv_sec = usecTimeout / 1000000;
} else {
m_timeout.tv_usec = usecTimeout;
}
}
int CHttpSocket::Request( CHttpReqHeader &header, CHttpBody &body )
{
char* pStrHeader = header.toString();
char* pStrBody = body.toString();
int size = header.getStringSize() + body.getStringSize() +2; // "\r\n" "\0" 포함
char* pData = new char[size+1];
memset( pData, 0, size+1 );
memcpy( pData, pStrHeader, header.getStringSize() );
memcpy( pData + header.getStringSize(), "\r\n", 2 );
memcpy( pData + header.getStringSize() +2, pStrBody, body.getStringSize() );
size = Send( pData, size );
delete pData;
pData = NULL;
return size;
}
int CHttpSocket::recvHeader( CHttpResHeader &header )
{
char buf[MAXBUF];
int nRecv=0, nTotal=0;
int i=0;
char *lastCrLf = NULL;
memset( buf, 0, sizeof(buf) );
// 1. 해더를 1 byte씩 받는다.
nRecv = Receive( &buf[nTotal], 2 ); // 최초는 2byte
if ( nRecv <= 0 )
return nRecv;
while(nRecv > 0)
{
nTotal += nRecv;
// "\r\n"이 들어오면 긴장 하자
if ( buf[nTotal-2] == '\r' && buf[nTotal-1] == '\n' )
{
// 두번 연속이 아니면 마지막 주소 저장
if ( lastCrLf != &buf[nTotal-4] ) lastCrLf = &buf[nTotal-2];
// 두번 연속이면 해더 끝
else break;
}
nRecv = Receive( &buf[nTotal], 1 );
if ( nRecv <= 0 )
break;
}
if ( header.fromString( buf ) == false )
nTotal = SOCKERR_ERROR;
return nTotal;
}
// 바디에 사이즈를 읽는다.
int CHttpSocket::getChunkedLength()
{
char buf[MAXBUF];
char *endptr = 0;
int nRecv=0, nTotal=0;
memset( buf, 0, sizeof(buf) );
// 1. 해더를 1 byte씩 받는다.
nRecv = Receive( &buf[nTotal], 2 ); // 최초는 2byte
if ( nRecv <= 0 )
return nRecv;
while(nRecv > 0)
{
nTotal += nRecv;
// "\r\n"이 들어오면 긴장 하자
if ( buf[nTotal-2] == '\r' && buf[nTotal-1] == '\n' )
{
// 빈줄이면 한번 더 읽자
if ( nTotal == 2 )
{
nTotal = 0;
memset( buf, 0, sizeof(buf) );
nRecv = Receive( &buf[nTotal], 2 ); // 최초는 2byte
if ( nRecv <= 0 )
return nRecv;
continue;
} else {
break;
}
}
nRecv = Receive( &buf[nTotal], 1 );
if ( nRecv <= 0 )
return nRecv;
}
return strtol( buf, &endptr, 16 );
}
// 바디에 사이즈가 있는경우 loop을 돌려 body를 받는다.
int CHttpSocket::recvBody( CHttpBody &body )
{
int nRecv = 0, nBodySize = 0, nBodyTotalSize = 0;
char *buf = 0;
bool isContinue = true;
while(isContinue)
{
// 1. body size를 읽는다.
nBodySize = getChunkedLength();
if ( nBodySize <= 0 ) break;
#if (_MSC_VER<=1200)
/* Notice :: warning
realloc은 Visual C++ 6.0 에서 정상 동작하지 않는다.
Service Pack 4 이상 패치 하여 사용할 것.
*/
#pragma message("CHttpSocket Warning Msg: realloc used."
"Service Pack 4 or later should be used")
#endif
// nBodySize 크기 만큼 재할당
if ( buf ) buf = (char*)realloc( buf, nBodyTotalSize + nBodySize );
else buf = (char*)malloc( nBodySize );
memset( &buf[nBodyTotalSize], 0, nBodySize );
//body를 읽는다.
nRecv = Receive( &buf[nBodyTotalSize], nBodySize );
if ( nRecv <= 0 ) break;
nBodyTotalSize += nRecv;
}
if ( nBodyTotalSize > 0 )
{
body.set( buf, nBodyTotalSize );
free(buf);
}
return nBodyTotalSize;
}
// 해더에 사이즈가 있는경우 body를 받는다.
int CHttpSocket::recvBody( CHttpBody &body, int size )
{
int nRecv = 0;
char *buf = new char[size+1];
memset(buf, 0, size+1);
nRecv = Receive( buf, size );
body.set( buf, size );
delete [] buf;
return nRecv;
}
// 응답을 받을 함수
int CHttpSocket::Response( CHttpResHeader &header, CHttpBody &body )
{
int nRecv = 0;
int bodySize = 0;
char *p = 0;
// 1. 해더를 받는다.
nRecv = recvHeader( header );
if ( nRecv <= 0 )
{
return nRecv;
}
// 2. chunked 모드 인지 확인한다.
p = header.getValue("Transfer-Encoding");
if ( p && strcmp(p, "chunked") == 0 )
{
// 2.2 바디에 사이즈가 있는경우 loop을 돌려 body를 받는다.
nRecv = recvBody( body );
if ( nRecv <= 0 )
{
return nRecv;
}
} else {
p = header.getValue( "Content-Length" );
if ( !p ) return 0;
else bodySize = atoi( p );
// 2.2 해더에 사이즈가 있는경우 body를 받는다.
nRecv = recvBody( body, bodySize );
if ( nRecv != bodySize )
{
return nRecv;
}
}
if ( header.getResultCode() != 200 )
return SOCKERR_ERROR;
return nRecv;
}
// 지정한 사이지를 모두 처리할 함수
int CHttpSocket::Send(char *data, int size)
{
int nSend=0, result =0;
while( nSend < size )
{
result = sendEx( m_socket, data + nSend, size - nSend );
nSend += result;
if ( result <= 0 )
{
if ( nSend <= 0 )
return result;
else
return nSend;
}
}
return nSend;
}
// 지정한 사이지를 모두 처리할 함수
int CHttpSocket::Receive(char *data, int size)
{
int nRecv=0, result = 0;
while( nRecv < size )
{
result = recvEx( m_socket, data + nRecv, size - nRecv );
nRecv += result;
if ( result <= 0 )
return nRecv;
}
return nRecv;
}
// 타임아웃 처리를 한 함수
int CHttpSocket::sendEx(int sock, char *data, int size)
{
int sockNum = sock +1;
fd_set sockSet;
int result;
if ( sock == -1 )
return -1;
FD_ZERO(&sockSet);
FD_SET(sock, &sockSet);
result = select( sockNum, NULL, &sockSet, NULL, &m_timeout );
if ( result == 0 )
{
return SOCKERR_TIMEOUT; // timeout(-2)
}
else if ( result ==-1 )
{
return SOCKERR_ERROR; // error(-1)
}
return send( sock, data, size, 0);
}
// 타임아웃 처리를 한 함수
int CHttpSocket::recvEx(int sock, char *data, int size)
{
int sockNum = sock +1;
fd_set sockSet;
int result;
if ( sock == -1 )
return SOCKERR_ERROR;
FD_ZERO(&sockSet);
FD_SET(sock, &sockSet);
result = select( sockNum, &sockSet, NULL, NULL, &m_timeout );
if ( result == 0 )
{
return SOCKERR_TIMEOUT; // timeout(-2)
}
else if ( result ==-1 )
{
return SOCKERR_ERROR; // error(-1)
}
return ::recv( sock, data, size, 0);
}
// non-blocking connect
int CHttpSocket::connect_nonb(int sockfd, const struct sockaddr *saptr,
socklen_t salen, struct timeval tval)
{
#ifndef WIN32
int flags
socklen_t len;
#endif
int n, error;
fd_set rset, wset;
#ifdef WIN32
int nonblocking =1;
ioctlsocket(sockfd, FIONBIO, (unsigned long*) &nonblocking);
#else
flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
#endif
error = 0;
if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0)
{
#ifdef WIN32
errno = WSAGetLastError();
#endif
if (errno != EINPROGRESS && errno!= EWOULDBLOCK)
{
return(-1);
}
}
/* Do whatever we want while the connect is taking place. */
if (n == 0)
goto done; /* connect completed immediately */
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
wset = rset;
if ( (n = select(sockfd+1, &rset, &wset, NULL,
((tval.tv_sec>0) || (tval.tv_usec>0))? &tval : NULL)) == 0)
{
close(sockfd); /* timeout */
errno = ETIMEDOUT;
return(-1);
}
if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
{
#ifndef WIN32
len = sizeof(error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
return(-1); /* Solaris pending error */
#endif
} else
return(-1); //err_quit("select error: sockfd not set");
done:
#ifdef WIN32
nonblocking =0;
ioctlsocket(sockfd, FIONBIO, (unsigned long*) &nonblocking);
#else
fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
#endif
if (error) {
close(sockfd); /* just in case */
errno = error;
return(-1);
}
return(0);
}
// 소켓 핸들 반환
int CHttpSocket::Connection(const char* host, int port)
{
struct hostent *st_host = NULL;;
struct sockaddr_in st_addr;
unsigned long iaddr;
int nTry =0;
memset( &st_addr, 0, sizeof(st_addr) );
if ( m_socket > 0 )
{
return m_socket;
}
if( (m_socket = (int)socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
return m_socket = -1;
}
if((iaddr = inet_addr(host)) != INADDR_NONE)
{
// dotted-decimal
memcpy((char *)&st_addr.sin_addr, (char *)&iaddr, sizeof(iaddr));
}
else
{
// hostname
if((st_host = gethostbyname(host)) == NULL)
{
close(m_socket);
return m_socket = -1;
}
memcpy((char *)&st_addr.sin_addr, st_host->h_addr, st_host->h_length);
}
st_addr.sin_family = AF_INET;
st_addr.sin_port = htons( port );
for( nTry=0; nTry<3; nTry++ )
{
if ( connect_nonb(m_socket, (struct sockaddr *)&st_addr,
sizeof(st_addr), m_timeout) >= 0 )
{
break;
}
}
/* 시도가 초과하였으면 에러 */
if( nTry >= 3)
{
close( m_socket );
m_socket = SOCKERR_ERROR; // error(-1)
}
return m_socket;
}
void CHttpSocket::Close()
{
close(m_socket);
m_socket = 0;
}
반응형
'Dev > C, C++' 카테고리의 다른 글
| Registry 읽고 쓰기 재활용 (0) | 2007.03.31 |
|---|---|
| 애플리케이션 개발시의 메모리 디버깅 : 메모리 누수 발견 기법 (0) | 2007.03.19 |
| 유용한 소켓 강좌 (0) | 2006.09.20 |
| 함수 포인터 관련.. (0) | 2006.09.19 |
| UTF-8 을 EUC-KR 로 변환 (0) | 2006.09.04 |
CHttpSocket.hxx