본문 바로가기

Creation/Programming

Socket Programming - BrowserClient.c

Socket Programming



  Socket은 L5의 Application Layer에서 다른 컴퓨터로의 접근을 가능하게 해줍니다. L3의 IP와 L4의 Port정보를 가지고 있는 것이 Socket이고, 이것을 만들게 됨으로써 Application은 마치 이 소켓에 Read와 Write를 하는 것으로 다른 컴퓨터와 데이터를 주고 받을 수 있게됩니다. 계층화의 장점이라고 할까요.. 프로그래머는 어떤 식으로 컴퓨터가 데이터를 주고받게 되는지 전혀 알 필요도 없고, 신경쓸 필요도 없습니다. 소켓을 만들고 데이터를 넣어주면 그 다음부터는 알아서 진행되는 것이지요. L5->L4->L3->L2->L1을 거치고, 중간의 라우터를 거쳐서, 건너건너 상대컴퓨터로 도착하게 되겠죠.

  아무튼 오늘 소개하는 BrowserClient.c는 이 Socket을 사용한 간단한 프로그램입니다. 리눅스기반에서 gcc를 사용해서 컴파일하여 실행하셔야 합니다. 브라우저처럼 80번 Port를 사용하여 서버에서 페이지를 가져온다음 source들을 추출합니다.





BrowserClient.c

	

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <signal.h> #include <fcntl.h> #include <time.h> #include <sys/stat.h> int MakeSocket( char *webAddress, int portNumber ); void RunHTTPInteraction( char *webAddress); int SendGetReq( int sock, char *HostName, char *ObjectName ); void RecvResponse( int sock, char *FileName, char *HostName ); int ParseObject( char *FileName, char *hostAddress[], char *objectName[] ); #define HTTP_PORT 80 #define BUF_SIZ 1024 #define LARGE_BUF_SIZ 20480 #define TRUE 1 #define FALSE 0 #define MAXCOL 30000 /////////////////////////main int main( int argc, char** argv ) { if( argc == 2){ RunHTTPInteraction( argv[1]); return 0; } else { printf(" usage : ./gen URL \n"); } return 0; } void RunHTTPInteraction( char *webAddress ) { int sock; char *objectName[BUF_SIZ]; char *hostAddress[BUF_SIZ]; int objectCounter; int id=1; char FileName[BUF_SIZ]; int i, j; if( ( sock = MakeSocket( webAddress, HTTP_PORT ) ) < 0 ) { return; } sprintf( FileName, "%d.html", id ); if( SendGetReq( sock, webAddress, "/" ) == TRUE ) { RecvResponse( sock, FileName, webAddress ); close( sock ); } else { close( sock); return; } objectCounter = ParseObject( FileName, hostAddress, objectName ) ; if( objectCounter <= 0 ) return ; printf( "=============objects=============\n"); printf(" the number of objects : %d\n", objectCounter); for ( i= 0; i<objectCounter; i++){ printf ( "host : %s , object : %s\n",hostAddress[i],objectName[i]); } for( i = 0; i < objectCounter; i++ ) { free( hostAddress[i] ); free( objectName[i] ); } } int SendGetReq( int sock, char *HostName, char *ObjectName ) { char GetReq[ BUF_SIZ ]; sprintf( GetReq, "GET %s HTTP/1.1\r\nAccept: */*\r\nHost: %s:%d\r\nConnection: close\r\n\r\n", ObjectName, HostName, HTTP_PORT ); if ( write( sock, GetReq, strlen(GetReq)+1 ) < 0 ) { perror( "write error" ) ; return FALSE; } return TRUE; } /* Make a socket that is connected to a server */ int MakeSocket( char *webAddress, int portNumber ) { int sock ; /* A socket connects to server */ struct sockaddr_in server_addr ; struct hostent *host_name ; /* server host name */ unsigned long addr_number ; /* server address ( network byte order ) */ uint16_t port ; /* server port number ( network byte order ) */ /* get the ip address by hostname, also check it is a correct host name */ host_name = gethostbyname( webAddress ) ; if ( host_name == NULL ) { perror( "gethostbyname error" ); return -1 ; } addr_number = *(unsigned long*)host_name->h_addr ; /* network byte order */ port = htons( (uint16_t)portNumber ) ; /* create a socket to connet server * IPv4, TCP */ if ( ( sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) { perror( "socket error" ) ; return -1 ; } /* server address setting * IPv4, server address and port by user inputs ( network byte order ) */ memset( &server_addr, 0, sizeof( server_addr ) ) ; server_addr.sin_family = AF_INET ; server_addr.sin_addr.s_addr = addr_number ; server_addr.sin_port = port ; /* connect to the server * If the server does not serve it, it is refused */ if ( connect( sock, (struct sockaddr*)&server_addr, sizeof( server_addr ) ) < 0 ) { perror( "connect error" ) ; return -1 ; } /* return the socket number */ return sock ; } void RecvResponse( int sock, char *FileName, char *hostName ) { char buf[BUF_SIZ+1]; char Temp[BUF_SIZ+1]; int nbytes; int html; if ( ( html = open( FileName, O_WRONLY | O_CREAT | O_TRUNC ) ) < 0 ) { perror( "open error" ) ; return; } chmod(FileName, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH); if( ( nbytes = read( sock, buf, BUF_SIZ ) ) < 0 ) { printf( "read error!\n" ); return; } buf[nbytes]='\0'; strcpy( Temp, buf ); *(strstr( Temp, "\r\n\r\n" )) = '\0'; printf( "=============header==============\n"); printf( "%s\n", Temp ); while( TRUE ) { if ( write( html, buf, nbytes ) < 0 ) { perror( "write error" ) ; close( html ) ; return; } if ( ( nbytes = read( sock, buf, BUF_SIZ ) ) == 0 ) break; } close( html ); } int ParseObject( char *FileName, char *hostAddress[], char *objectName[] ) { int i = 0; FILE* response; char Temp[LARGE_BUF_SIZ]; char *pChar_0; char *pChar_1; char *pChar_2; int hostLength; int objectLength; if ( ( response = fopen(FileName,"rb") ) == NULL ) return -1; while( fgets(Temp, MAXCOL, response) != NULL ){ if( ( pChar_0 = strstr( Temp, "img src=\"http://" ) ) != NULL ) { pChar_0 += 16; if( ( pChar_1 = strchr( pChar_0, '/' ) ) != NULL ) { if( ( pChar_2 = strchr( pChar_1, '\"' ) ) != NULL ) { hostLength = pChar_1 - pChar_0; objectLength = pChar_2 - pChar_1; hostAddress[i] = (char*)malloc( hostLength + 1 ); objectName[i] = (char*)malloc( objectLength + 1 ); strncpy( hostAddress[i], pChar_0, hostLength ); hostAddress[i][hostLength] = '\0'; strncpy( objectName[i], pChar_1, objectLength ); objectName[i][objectLength] = '\0'; i++; } } } } fclose( response ); return i; }




  일단 기본값으로는 http://www.naver.com/ 을 가져오는 프로그램입니다. 물론 원한다면 다른 페이지를 가져올 수도 있지만.. 간단하게 src="~~~" 부분을 가져와서 소스들을 보여줍니다.



 

  원한다면 빨간색으로 되어있는 부분을 수정하셔서 좀 더 세부적인 태그만 가져올 수도 있을 것입니다. 위 스크린샷은 src="~~"를 가져오던 것을 img src="~~"만을 가져오도록 수정을 한 것입니다. int ParseObject 함수가 파싱을 하게되는 함수입니다. 이 부분을 수정하시면 좀 더 세부적으로 페이지를 가지고 올 수 있겠죠.



  물론 위 작업은 파이썬으로 수행하면 훨씬 간단하게 수행이 가능합니다만 (BeautifulSoup과의 조합은 정말 편합니다), 속도는 c로 하는 것이 훨씬 빠르긴 하더군요. 특히 BeautifulSoup은 사용은 간단하지만 모든 태그를 해체하는 과정이 들어가기 때문에 속도가 더뎌지는 듯 합니다.