C언어 소켓프로그래밍 - Http request - response
허허 .... 이것 저것 하다 보니 포스팅 한다는 것을 까먹고 있었네요..... 죄송합니다... ㅠ
바로 포스팅하도록 하겠습니다!!!
(★ 아... 그리고 해당 소스코드말고 동적으로 http request 를 보낼 수 있게 하고 받아오는 response
가 클 경우 짤리는 것, 작을시 쓰레기값 출력 이 부분을 수정한 코드도 있으니 포스팅하고 바로
올리도록 하겠습니다. ㅎ)
참조 사이트 모음
☞ http://mintnlatte.tistory.com/
☞ http://forum.falinux.com/zbxe/?mid=network_programming
☞ http://m.blog.naver.com/sarah7_2000/80203615486
☞ http://kyh1026.tistory.com/76
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 | #include <stdio.h> #include <WinSock2.h> #include <Windows.h> #pragma comment(lib,"ws2_32.lib") #define BUFF_SIZE 1024 int main() { WSADATA wsa; struct hostent *host; char msg[BUFF_SIZE]; //나중에 결과값을 저장하기 위한 선언 if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { perror("WSAStart Error "); system("pause"); return -1; } SOCKET sock = socket(PF_INET, SOCK_STREAM, 0); SOCKADDR_IN addr; if (sock == INVALID_SOCKET) { perror("Sock Error "); system("pause"); return -1; } host = gethostbyname("www.google.co.kr"); addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list)); if(connect(sock,(SOCKADDR*)&addr,sizeof(addr)) == SOCKET_ERROR) { printf("Not Connect \n"); system("pause"); return 1; } send(sock, "GET / HTTP/1.1\r\nHost: www.google.co.kr\r\n\r\n", strlen("GET / HTTP/1.1\r\nHost: www.google.co.kr\r\n\r\n"), 0); recv(sock,msg,BUFF_SIZE,0); printf("%s \n",msg); closesocket(sock); WSACleanup(); system("pause"); return 0; } | cs |
1편이 어디서 끝났지?? 라는 분이 계실까봐 1편의 마지막 부분도 살짝 첨부하도록 하겠습니다.
SOCKET 변수 로 소켓을 생성해볼까요?
레퍼런스: int socket(int domain, int type, int protocol); 으로 사용됩니다.
● PF_INET 는 IPv4 프로토콜을 뜻함.
● SOCK_STREAM 은 연결지향을 뜻함. (SOCK_DGRAM 은 비연결성을 뜻함)
● 0 은 특정 프로토콜을 사용하는 부분인데 보통 0을 쓴다고 합니다.
socket() 에 관련해 제가 참조한 사이트를 링크 걸겠습니다.
정말 자세하게 나와있으므로 세부적인 부분은 여기서 공부하시면 될 듯 합니다.
☞ http://forum.falinux.com/zbxe/index.php?document_srl=429387&mid=C_LIB
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
레퍼런스 MS 링크
☞ https://msdn.microsoft.com/ko-kr/library/zx63b042(v=vs.110).aspx
자!! 이제 진짜 2편 으로 가보도록 하겠습니다.
sock == INVALID_SOCKET 이란 조건을 일단 확인 하실 수 있으실 겁니다.
이 조건은 소켓생성이 실패하였을떄 INVALID_SOCKET 라는 반환값을 반환하게 됩니다 (-1 값)
그렇게 때문에 오류확인을 위해 넣어준 코드 중 하나라고 보시면 되십니다.
이 구문을 이제 살펴 보도록 하죠. gethostbyname 이라는 레퍼런스를 사용하였네요.
나중에 www.google.co.kr 의 주소를 ip 주소로 변경해 주기 위한 작업중 하나에요.
만약 '네이버' 를 타켓으로 설정하실려면 '네이버' 도메인 주소로 설정해 주면 되겠죠?
struct hostent *host;
host 는 코드상단부분의 이 부분입니다.
그렇다는 것은?! hostent 의 구조체의 내용을 살펴봐야한다는 뜻이 되겠죠?ㅎ
struct hostent { char *h_name; /* Official name of host. */ char **h_aliases; /* Alias list. */ int h_addrtype; /* Host address type. */ int h_length; /* Length of address. */ char **h_addr_list; /* List of addresses from name server. */ #define h_addr h_addr_list[0] /* Address, for backward compatibility. */ };
이제 거의 제일 중요한 설정 부분이라고 할수 있겠습니다.
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
포스트 상단에서 확인 하실 수 있드시 socketaddr_in 구조체를 사용한 부분 입니다.
};struct sockaddr_in{ short sin_family; // 인터넷 주소체계 IPv4 or IPv6 unsigned short sin_port; // Port 주소 struct in_addr sin_addr; // ip 주소를 나타내는 구조체 char sin_zero[8]; // sockaddr 과 같은 크기 유지
이런 구조를 가지고 있구요 상단보다 더 자세하죠? 지금 표현하는게 나은 것 같아서요 ㅎ
addr.sin_family 주소체계를 나타난다고 했죠? 저희는 IPv4 를 사용하니 AF_INET 를 사용해 주는 겁니다.
주소쳬계 및 프로토콜 체계는 아래의 블로그에서 확인해 주시면 되실 것 같습니다.
(AF_INET 와 같은 것들)
링크: http://bbolmin.tistory.com/25
addr.sin_port 포트를 지정해주는곳이니 포트를 지정해주도록 합니다.
그런데 왜 htons 라는 레퍼런스를 사용할까요? 그것은 바로 PC 와 네트워크의 데이터 저장 방식이 다르기 때문입니다.
PC의 경우 리틀엔디안 방식으로 데이터를 저장하게 됩니다.
네트워크의 경우 빅엔디안 방식으로 데이터를 저장하게 됩니다.
(구글에 해당 방식을 검색하면 자세히 나오더라구요 ㅎ 제가 포스팅하는거보다 더 이해하시기 편하 실 것 같습니다. 절대 귀찮아서 그런것이 아닙니다!! ㅎ)
저희는 PC 에서 데이터를 저장하고 있기 때문에 리틀엔디안 방식으로 저장이 되죠 그렇기 때문에 빅엔디안 방식으로 변경해 줘야할 필요가 있습니다.
htons() 함수가 그 역활을 담당하고 있죠. 그렇기에 사용해 주는 것 입니다.
send(sock, "GET / HTTP/1.1\r\nHost: www.google.co.kr\r\n\r\n", strlen("GET / HTTP/1.1\r\nHost: www.google.co.kr\r\n\r\n"), 0);
부분의 차례군요 보기만 해도 짜증나시죠?... 이 부분만 하면 90퍼센트 끝입니다. 조금만 더 해보도록 하죠!!
(일단 이 부분은 정형화 되어있기 때문에 저거 그대로 쓰시면 됩니다.)
host 에서 지정해 준 도메인 주소를 socketaddr_in 구조체의 ip 부분으로 옮겨줍니다.
그리고 inet_ntoa 함수를 이용해 주소값으로 변환을 시켜주게 됩니다.
자세한 내용은 아래 링크를 참조해주세요.
링크 : http://mintnlatte.tistory.com/272
이제 끝입니다...!!
recv(sock,msg,BUFF_SIZE,0);
부분 밖에 남지가 않았네요.
send() 레퍼런스 부터 알아보도록 하죠.
request 구문을 서버로 쏴주는 역활을 하는 작업입니다.
(구문중에 \r\n 이 있는데 이 것이 뜻하는 것은 엔터를 뜻하는 것 입니다.)
int send(int s, const void *msg, size_t len, int flags); 의 형태로 사용이 되어집니다.
자세한 내용은 아래 링크를 참조해주세요.
링크 : http://forum.falinux.com/zbxe/index.php?document_srl=441104&mid=C_LIB
recv() 레퍼런스도 알아보도록 하죠.
int recv(int s, void *buf, size_t len, int flags); 형태로 사용이 되어지고요.
이 함수의 특징은 찾기가 힘드실꺼 같아서 제가 직접 간략하게 포스팅을 하도록 하겠습니다.
recv 의 기능은 send 함수로 request 를 보내면 respond 를 받아오는 역활을 담당하고 있습니다. 해당 레퍼런스를 보시면 버퍼사이즈 만큼의 크기만 받아오도록 설정이 되어있습니다.
만약 버퍼크기를 1024로 가정을 한다고 해보도록 하겠습니다. 그렇다면 받아오는 respond 의 크기가 1024 사이즈보다 크면 어찌할까요?? 의아해 하실겁니다 아마.....ㅎㅎ
recv 의 특징은 만약 respond 가 123456789 라고 가정을 하고 버퍼크기는 5라고 가정을 하겠습니다.
.
그럼 12345 를 받아오게 되겠죠? 6789 는 그럼 버리게 될까요?? 그게 아닙니다. recv 는 계속해서 6789 를 가지고 있습니다.
그러면 이제 슬슬 감이 잡히실 겁니다 2048 의 크기의 respond 가 들어오면 반복을 이용해서 나머지 부분도 출력을 해주면 되겠죠?
그럼 또! 의아점이 생깁니다. 크기를 모르니깐 무한루프를 돌려야하고 크기가 끝이면 나오는 루틴이 필요한데 이건 어찌 해결을 해야하냐?! 라는 의아점이죠.
recv 의 특징 중 또다른 하나를 사용하면 해결이 가능 합니다.
recv 의 반환값을 살펴보는 것이죠. 만약 respond 의 크기가 50 이면 50을 반환하고 60이면 60을 반환하게 됩니다. 그리고 아무런 값이 존재하지 않을때는 0 을 반환하게 됩니다.
이 특징을 딱 보니 감이 오시죠?!! 무한루틴에 0을 반환하면 탈출! 이라는 루틴을 설정해주면 완벽하게 respond 를 출력 할 수 있게 되는 것이죠.
(상단의 코드는 그 부분이 존재하지 않아 짤리려서 나오게 됩니다. 짤리지 않는 소스는 추후에 업로드 하겠습니다.)
아래 부분은 열어주엇던 소켓들을 닫아주는 작업 입니다. ㅎ
후... 드디어 끝이 났습니다!!!
closesocket(sock);
'프로그래밍 > C 언어' 카테고리의 다른 글
C언어 소켓프로그래밍 1편- Http request - response (3) | 2017.01.10 |
---|---|
c언어 파일 암호화 복호화 (53) | 2014.05.30 |
C 언어로 만든 암호화 복호화프로그램 (48) | 2014.01.03 |