Dev/C, C++
http socket client
newtype
2006. 11. 23. 10:47
급하게 필요해 날림으로 하루만에 뚝딱 만들었습니다.
아주 기본적인 동작만 합니다.
청크드 모드는 아직 구현되지 않았습니다.
-- 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; }
반응형