[C 언어] 소켓통신 코드 분석 2번째
카테고리: C
분석하기위한 서버의 소켓통신 코드
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
void error_handling(char* message);
int main(int argc, char* argv[])
{
// 선언
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
// error check
if(argc != 2)
{
printf("Usage: %s <port> \n", argv[0]);
exit(1);
}
// 소켓 생성
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if(serv_sock == SOCK_ERROR)
error_handling("socket() error ! ");
// member 설정
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
// 소켓의 주소 할당
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)-1) == -1)
error_handling("bind() error! ");
// 연결 요청이 가능한 상태로 만들기
if(listen(serv_sock, 5) == -1)
error_handling("listen() error! ");
// 연결 요청의 수락
clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_szie);
if(clnt_sock == -1)
error_handling("accept() error ! ");
write(clnt_sock, message, sizeof(message) );
close(serv_sock);
close(clnt_sock);
return 0;
}
void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
1. 멤버 설정
1. memset(&serv_addr, 0, sizeof(serv_addr));
struct sockaddr_in {
short sin_family; // 주소 체계: AF_INET
u_short sin_port; // 16 비트 포트 번호, network byte order
struct in_addr sin_addr; // 32 비트 IP 주소
char sin_zero[8]; // 전체 크기를 16 비트로 맞추기 위한 dummy
};
- serv_addr의 자료형인 구조체 sockaddr_in 이다.
이 구조체를 먼저 분석할 필요가 있다.
1-1. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
32bit 의 IP 번호 저장. Network byte order 로 저장
sin_addr의 자료형이 구조체안에서 또 다른 구조체여서 살펴볼 필요가 있다.
struct int_addr {
uint32_t s_addr; /* address in network byte order */
}
즉, s_addr이라는 변수의 자료형이 unsigned int -> 32비트 라는 것을 확인할 수 있다 !
- htonl에 대해
#include <ara/inet.h> unsigned short htons(unsigned short); unsigned short ntohs(unsigned short); unsigned long htonl(unsigned long); unsigned long ntohl(unsigned long);
h: Host byte order
n: Network byte order
s: short
l: long - INADDR_ANY에 대해
자기자신의 IP를 넣는 것과 같다 -> Localhost와 같은 뜻이라고 보자.
1-2. serv_addr.sin_port = htons(atoi(argv[1]));
16bit 의 port 번호 저장. Network byte order 로 저장
- atoi
ASCII 를 Int 형으로 변환하는 것
1-3. sin_zero와 sin_family
- sin_zero는 특별한 것은 없으나 반드시 0 을 넣어줘야한다 !
- sin_family도 특별하지 않으면 AF_INET 으로 IPv4의 주소 체계로 대입해주자 :)
2. 바이트의 변환
bind함수와 accept함수에서 사용한 struct sockaddr 이라는 구조체의 구조를 살펴보자
struct sockaddr
{
sa_family_t sa_family; // Address family
char sa_data[14]; // Address data
}
sa_data의 크기는 14byte이다.
IP주소는 4byte(32bit)이고, port는 2byte(16bit)이다.
IP주소와 port 모두 sa_data 안에 들어가기에 크기가 충분하지만,
주소 정보의 저장의 디테일에 불편함이
struct sockaddr_in 을 만들게 되었다.
그렇게 만들어진 sockaddr_in 정의
struct sockaddr_in {
short sin_family; // 주소 체계: AF_INET
u_short sin_port; // 16 비트 포트 번호, network byte order
struct in_addr sin_addr; // 32 비트 IP 주소
char sin_zero[8]; // 전체 크기를 16 비트로 맞추기 위한 dummy
};
- Host byte order: Little Endian을 보통으로 사용하지만 CPU에 따라 달라질 수 있다.
(Little Endian은 Intel 기준이다 ) - Network byte order: Big Endian을 기준으로 한다.
3. <arpa/inet.h> 헤더안에 있는 함수들
1. inet_addr
in_addr_t inet_addr(const char* string);
-> 성공 시 Big Endian으로 변환된 32비트 정수 값 반환, 실패 시 INADDR_NONE 반환
- Q1. 왜 32비트 정수값으로 값을 반환할까?
A1.
typedef uint32_t in_addr_t; 이 것을 참고하면 unsigned int가 in_addr_t로 typedef 되어있었다는 걸 알 수 있다.
2. inet_aton
int inet_aton(const char* string, struct in_addr* addr);
-> 성공 시 1(true)반환, 실패 시 0(false)반환
3. inet_ntoa
char* inet_ntoa(struct in_addr adr);
-> 성공 시 변환된 문자열의 주소 값 반환, 실패 시 -1 반환
4. 아주 중요한 예제 2개
1. 예제 1번
addr. 127.232.124.79로 선동초
inet_aton 함수로 변환.
변환 실패 시 "Conversion error"출력
잘되면 결과값 출력
정답
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char* addr = "127.232.124.79";
struct sockaddr_in sock_addr;
int result;
result = inet_aton(addr, &sock_addr.sin_addr);
if(result == 0)
{
printf("Conversion error");
exit(1);
}
printf("Network byte ordered : %#x \n", sock_addr.sin_addr.s_addr)
return 0;
}
2.
주소 값 저장할 변수 addr1, addr2 선언
addr1에 0x1020304를 네트워크 바이트 오더로 변경해서 저장
addr2에 0x1010101를 네트워크 바이트 오더로 변경해서 저장
addr1을 inet_ntoa함수를 사용해서 변경하고 str_ptr에 저장
// 복사, 붙여넣기 할 수 있는 함수 사용해서 출력을 밑에서 할 수 있도록 해보자.
addr2을 inet_ntoa함수를 사용해서 변경하고 str_ptr에 저장
str_ptr 출력
str_ptr 출력
정답
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char* argv[])
{
struct sockaddr_in addr1;
struct sockaddr_in addr2;
char* str_ptr[3];
addr1.sin_addr.s_addr = htonl(0x1020304);
addr2.sin_addr.s_addr = htonl(0x1010101);
str_ptr[0] = inet_ntoa(addr1.sin_addr);
// 출력을 밑에서 하기 위한 strcpy 활용
strcpy(str_ptr[2], str_ptr[0]);
str_ptr[0] = inet_ntoa(addr2.sin_addr);
// 출력을 여기서 모두 하기
printf("%s \n", str_ptr[2]);
printf("%s \n", str_ptr[0]);
return 0;
}
댓글 남기기