[C 언어] 소켓통신 코드 분석 2번째

Date:     Updated:

카테고리:

태그:

분석하기위한 서버의 소켓통신 코드

#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;
}

C 카테고리 내 다른 글 보러가기

댓글 남기기