전에 올렸었던
http://blog.kim82536.pe.kr/entry/%EC%9B%B9-%EC%9D%91%EB%8B%B5%EC%97%90%EC%84%9C-%EC%84%9C%EB%B2%84-%EC%A0%95%EB%B3%B4-%EC%B6%94%EC%B6%9C
의 프로그램을 약간 수정해 보았다.
원래 소스
project.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include "header/create_request.h" #include "header/dns.h" #include "header/parsing.h" #define BUFF_SIZE 400 int main(int argc,char **argv){ char request[100]="\0"; // To make request // char URL[]="www.naver.com"; char *URLaddr; int s,n; char *haddr; struct sockaddr_in server_addr; // socket address struct //인자값 체크 if(argc<2){ printf("Usage :\n%s [URL]\n",argv[0]); return 1; } // char buf[BUFF_SIZE+1]; char *buf; buf=(char *)malloc(BUFF_SIZE+1); // make buffer + NULL size //dns()함수에서 리턴 문자열을 받기 위한 동적 메모리 할당 URLaddr=(char *)malloc(20); //IP주소 담아오기 URLaddr=dns(argv[1]); if(!strcmp(URLaddr,"error")){ printf("dns() error!!!\n"); return 1; } //요청문자 담아오기 make_request(argv[1],request); s=socket(AF_INET,SOCK_STREAM,0); if(s < 0){ printf("SOCK ERROR!\n"); return 1; } bzero((char *)&server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(URLaddr); server_addr.sin_port = htons(80); if(-1 == connect(s,(struct sockaddr *)&server_addr,sizeof(server_addr))){ printf("Connect() Failed..\n"); 1; } memset(buf,'\0',BUFF_SIZE+1); // 정보 출력 printf("Request : \n%s\n\n",request); printf("Target URL : %s\nTarget IP : %s\n",argv[1],URLaddr); send(s,request,strlen(request)+1,0); if(0==read(s,buf,BUFF_SIZE)){ printf("read() Failed...\n"); return 1; } //printf("Recv : \n%s\n",buf); //Recv 에서 서버 정보만 자르기 char *result; result=(char *)malloc(strlen(buf)); result=buf; result=strfin(result,"<!doctype"); printf("\n\n\n------------------Result-------------------\n"); printf("%s\n",result); close(s); return 0; } /* Recv : * Target URL : www.naver.com * Target IP : 125.209.222.142 * Recv : * HTTP/1.1 200 OK * Server: nginx * Date: Sun, 04 Jan 2015 04:54:16 GMT * Content-Type: text/html; charset=UTF-8 * Transfer-Encoding: chunked * Connection: close * Cache-Control: no-cache, no-store, must-revalidate * Pragma: no-cache * P3P: CP="CAO DSP CURa ADMa TAIa PSAa OUR LAW STP PHY ONL UNI PUR FIN COM NAV INT DEM STA PRE" * X-Frame-Options: SAMEORIGIN */ | cs |
create_request.h
1 2 3 4 5 6 | #include <string.h> void make_request(char host[],char recv[]){ sprintf(recv,"GET / HTTP/1.1\nHost: %s\nUser-Agent: Mozilla4.0\nConnection: close\r\n\n",host); } | cs |
dns.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> char *dns(char host[]){ struct hostent *host_entry; int ndx; char *back; back=(char *)malloc(20); host_entry = gethostbyname(host); if(!host_entry){ printf("gethostbyname() failed...\n"); return "error"; } strcpy(back,inet_ntoa(*(struct in_addr*)host_entry->h_addr_list[0])); return back; } | cs |
erase_data.h
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> #include <string.h> char *erase_data(char data[],char start[]){ int i; for(i=0;i<strlen(start);i++) data++; return data; } | cs |
parsing.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include <stdio.h> #include <string.h> #include <stdlib.h> #include "strfin.h" #include "erase_data.h" /* char *strsta(char data[],char sta[]){ char *data_copy; char *sta_copy; int i; data_copy=data; sta_copy=sta; printf("before_data_copy = %s\n",data_copy); for(i=0;i<strlen(data);i++){ if(!strcmp(data_copy,sta_copy)){ //same for(i=0;i<strlen(sta_copy);i++) data_copy++; printf("data_copy = %s\nsta_copy = %s\n",data_copy,sta_copy); printf("Same!\n"); } else{ data_copy++; printf("data_copy = %s\nsta_copy = %s\n",data_copy,sta_copy); printf("NoNo!\n"); } } printf("data_copy = %s\n",data_copy); return data_copy; } */ char *parsing(char data[],char start[],char fin[]){ //printf("\n\n----------------parsing.h------------\n"); //printf("data = \n%s\n\n\n\n",data); //printf("start = \n%s\n\n\n\n",start); //printf("fin = \n%s\n\n\n\n",fin); data=strstr(data,start); //printf("strstr()_after_data = \n%s\n\n\n\n\n",data); //is_testing_string data=erase_data(data,start); //printf("strstr()_erase_data()_after_data = \n%s\n\n\n\n\n",data); //_testing_string data=strfin(data,fin); //printf("strstr()_erase_data()_strfin()_after_data = \n%s\n\n\n\n\n",data); //_testing return data; } | cs |
strfin.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> char *strfin(char data[],char fin[]){ char *data_copy; int i; char *trash='\0'; data_copy=data; for(i=0;i<strlen(data);i++){ if(!strncmp(data_copy,fin,strlen(fin))){ *data_copy='\0'; } else{ data_copy++; } } return data; } | cs |
기존 소스는 일단
1. 헤더 파일의 무분별한 사용. --> 소스의 가독성이 떨어짐
2. GET 의 사용으로 헤더만 추출하는 연산과정이 너무 복잡함.
3. 중복 #include 가 너무 심함.
이러한 문제점을 해결하기 위해
일단 HEAD 를 사용해 서버의 정보만 오도록 하고, 헤더파일의 사용은 최소한으로 줄임.
그리고 Standard Input Output 이 필요할 경우에만 stdio.h 를 포함시킴.
그리고 함수에 대한 필요 헤더파일만 선언해두고, 나머지 필요 없는 헤더 포함은 없앰.
+++) 함수를 만들고, 인자값과 반환값을 교환할 때 주의할 점.
strfin을 만들 때, 무작정 문자열을 넣고 수정하고 난 후에 반환을 하게 했는데,
큰 오산이었다.
1 | strfin("test"); | cs |
이렇게 호출하게 되면,
저 test 라는 문자열은 const 형이 되어버려서 수정이 되질 않는다.
따라서 호출시에
1 2 | char buffer[]="test"; strfin(buffer); | cs |
이렇게 호출을 하거나,
아니면 받는 쪽에서 변수를 하나 만들어 문자열을 담은 후에 연산하고 반환해야 한다.
저 프로그램을 만들때는 그냥 malloc 으로 힙 메모리에 박아 넣었었는데, 호출 시에 buffer 에 담아서 const 형이 되지 않게 넘겨주는 방법은 나중에 안 방법이다.
이 const 형 때문에 Segmentation fault 가 났었다.
(애꿎은 널바이트 넣는 루틴만 계속 수정하고..)
------- 호스트 정보 추출 -------
2014.05 기준으로 gethostbyname() 함수는 구식방법으로, 표준이 아니라고 한다.
해당 함수 대신 getnameinfo() 함수를 사용하려 한다.
- getnameinfo() 함수에 대한 정보가 거의 영문이라 애 먹고 있다.
추후에 단어사전 꺼내서 하나하나 번역해가면서 소스를 봐야 할듯.
해당 addr 에 대한 구조체를 선언후에 return 값을 받는 함수인 것 같다.
--------------------------
좀더 쉬운 소켓 연결과 간결한 소스 작성을 위해 파이썬으로 제작하기로 했다.
+) 파이썬은 도메인으로도 소켓 연결이 된다. 확실히 소켓 프로그래밍이 편한 언어인 것 같다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #-*- coding:utf-8 -*- import sys from socket import * # argc check if len(sys.argv)<3: print "Usage : %s [URL] [PORT]" %(sys.argv[0]) exit() request="HEAD / HTTP/1.1\nHost:" + sys.argv[1] + "\n\n" target=(sys.argv[1],int(sys.argv[2])) s=socket(AF_INET,SOCK_STREAM) s.connect(target) s.send(request) get=s.recv(1024) print get.split("\r\n")[1] | cs |
argv 로 URL 과 PORT 를 받고, HEAD 리퀘스트를 날려서 받은 응답에서 서버 정보만 추출해서 보여준다.
파이썬 자체에서는 argc 가 없어 len(sys.argv) 를 이용했다.
+) 서버에 따라 Server 헤더가 순서가 바뀌는 것을 확인했다
따라서 print get.split("\r\n")[1] 처럼 서버 정보를 추출 하는 것은 약간 위험하다고 보고, split 으로 나눈 뒤에 "Server" 라는 문자열을 비교하는 방식으로 변경해야 할 것 같다.
C언어에서는 도메인으로 소켓 연결시 연결이 되지 않았는데, 파이썬에서는 기본적으로 연결이 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import sys from socket import * # argc check if len(sys.argv)<3: print "Usage : %s [URL] [PORT]" %(sys.argv[0]) exit() request="HEAD / HTTP/1.1\nHost:" + sys.argv[1] + "\n\n" target=(sys.argv[1],int(sys.argv[2])) s=socket(AF_INET,SOCK_STREAM) s.connect(target) s.send(request) get=s.recv(1024).split("\r\n") i=0 while(1): try: tmp=get[i] except IndexError: break if tmp[:7]=="Server:": print tmp i=i+1 | cs |
$ python main.py localhost 80
Server: apache
$
성공적으로 나오는 것을 볼 수 있다.
이제 응답이 올 때, 헤더의 순서가 아닌 Server: 뒤에 붙는 문자열을 파싱하므로, 다른 서버에게 요청해도 서버의 정보만 정확하게 받아올 수 있다.
----- 마치며 -----
원래 저 프로그램은 처음에 방학을 한 후에, 나태해 질까봐 매일 2 시간씩 짬짬이 만들던 프로그램이었다. 하지만 저때 소켓 소스도 많은 블로그를 봐가며 코드도 가져와서 써보고 여러 오류도 고쳐가며 만든 프로그램 이었다.
확실히 시간이 얼마 지나고 파이썬으로 같은 프로그램을 작성해 보니까 한결 더 간결하고 간단한 코드로 짤 수 있었다.
그동안 놀지만은 않은 것 같아 다행이라 생각했다.
'전공 과목 시험정리 > C 프로그래밍' 카테고리의 다른 글
Fate 서버 소스 (미완성) (0) | 2015.04.25 |
---|---|
fork 로 프로세스 생성 후, 프로세스간 변수 공유 (0) | 2015.04.25 |
쓰레드 프로그램 (0) | 2015.04.25 |
알고리즘 대결 서버 소스 (0) | 2015.04.25 |
동기 소켓 vs 비동기 소켓 (0) | 2015.04.24 |