newtype
::: newtype의 지식창고 :::
newtype
전체 방문자
오늘
어제
  • 분류 전체보기 (392)
    • Dev (214)
      • C, C++ (43)
      • Go (5)
      • Web (49)
      • DBMS (21)
      • DevOps (8)
      • Java (2)
      • Windows, Win32 (4)
      • Visual Basic (5)
      • C# (2)
      • Mobile (25)
      • SQL CE (7)
      • Google Map (6)
      • Python (2)
      • cygwin (2)
      • 기타 (32)
      • Ruby (1)
    • 명언 (10)
    • 모임 (18)
      • 붕주회 (3)
      • 신흥컴정 (14)
      • 웹20기 (1)
    • 사진 (8)
    • 불펌 (29)
    • 막글 (98)
    • 게임 (6)
    • 여행 (8)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 관리

공지사항

  • whoami
05-09 06:54
hELLO · Designed By 정상우.
newtype

::: newtype의 지식창고 :::

Dev/C, C++

http socket client

2006. 11. 23. 10:47
급하게 필요해 날림으로 하루만에 뚝딱 만들었습니다.
아주 기본적인 동작만 합니다.

청크드 모드는 아직 구현되지 않았습니다.


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



CHttpSocket.hxx


반응형

'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
    'Dev/C, C++' 카테고리의 다른 글
    • Registry 읽고 쓰기 재활용
    • 애플리케이션 개발시의 메모리 디버깅 : 메모리 누수 발견 기법
    • 유용한 소켓 강좌
    • 함수 포인터 관련..
    newtype
    newtype
    지극히 개인적인 지식의 창고

    티스토리툴바