본문 바로가기
Programming/열혈 TCP, IP 소켓 프로그래밍(저자 윤성우)

Ch 02. 소켓의 타입과 프로토콜의 설정

by minjunkim.dev 2020. 8. 4.

    모든 내용은 [윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어] 를 기반으로 제 나름대로 이해하여 정리한 것입니다. 다소 부정확한 내용이 있을수도 있으니 이를 유념하고 봐주세요!


# 프로토콜이란?

- 대화에 필요한 통신규약

- 컴퓨터 상호간의 대화에 필요한 통신규약

- 서로 데이터를 주고받기 위해 정의해놓은 약속

 

# 소켓의 생성

#include <sys/socket.h>

int socket(int domain, int type, int protocol);
// -> 성공시 파일 디스크립터, 실패시 -1 반환

- domain : 소켓이 사용할 프로토콜 체계(Protocol Family) 정보 전달

- type : 소켓의 데이터 전송방식에 대한 정보 전달

- protocol : 두 컴퓨터간 통신에 사용되는 프로토콜 정보 전달

 

# 프로토콜 체계란?

- 소켓이 통신에 사용하는 프로토콜의 부류는 다양함

- 따라서, 소켓 생성시 사용할 프로토콜의 부류정보를 전달해야함(socket 함수의 첫번째 인자로 전달함)

- 이러한 부류정보를 "프로토콜 체계"라고 함

이름 프로토콜 체계(Protocol Family)
PF_INET IPv4 인터넷 프로토콜 체계
PF_INET6 IPv6 인터넷 프로토콜 체계
PF_LOCAL 로컬 통신을 위한 UNIX 프로토콜 체계
PF_PACKET Low Level 소켓을 위한 프로토콜 체계
PF_IPX IPX 노벨 프로토콜 체계

[표 : 헤더파일 sys/socket.h 에 선언되어있는 프로토콜 체계]

- 실제 소켓이 사용할 최종 프로토콜 정보는 socket 함수의 세번째 인자로 전달하는데,

socket 함수의 첫번째 인자인 프로토콜 체계 범위 내에서 세번째 인자를 결정해야 함

 

# 소켓의 타입

- 소켓의 데이터 전송방식을 의미

- socket 함수의 두번째 인자로 전달함

- 프로토콜 체계에도 둘 이상의 데이터 전송방식이 존재하기 때문에,

이것을 통해 명확하게 결정해주어야 함

 

# 대표적 데이터 전송방식

1) 연결지향형 소켓(SOCK_STREAM) : "신뢰성 있는 순차적인 바이트 기반의 연결지향 데이터 전송 방식의 소켓"

- 중간에 데이터가 소멸되지 않고 목적지로 전송됨

- 전송 순서대로 데이터가 수신됨

- 전송되는 데이터의 경계가 존재하지 않음 -> read, write 함수 호출횟수가 큰 의미를 가지지 않음

- 자신과 연결된 상대 소켓의 상태를 파악해가며 데이터를 전송함
(수신소켓의 입력버퍼가 꽉찬 경우에, 송신소켓은 데이터 전송을 멈춤)
(데이터가 제대로 전송되지 않으면, 송신소켓은 데이터를 재전송함)
- 소켓에 존재하는 버퍼가 꽉차는 경우에도 데이터는 소멸하지 않음
- 소켓 대 소켓의 연결은 반드시 1:1

2) 비연결지향형 소켓(SOCK_DGRAM) : "신뢰성과 순차적 데이터 전송을 보장하지 않는,

고속의 데이터 전송을 목적으로 하는 소켓"

- 전송된 순서에 상관없이 가장 빠른 전송을 지향함
- 전송된 데이터는 손실의 우려가 있고, 파손의 우려가 있음
- 전송되는 데이터의 경계가 존재함 -> read, write 함수 호출횟수가 서로 동일해야 함
- 한번에 전송할 수 있는 데이터의 크기가 제한됨
- 연결지향형 소켓과 다르게 연결이라는 개념이 존재하지 않음

 

# 프로토콜의 최종 선택(socket 함수의 세번째 인자)

- 대부분의 경우는 세번째 인자로 0을 넘겨주어도 무방
- 그러나, "하나의 프로토콜 체계(socket의 첫번째 인자) 안에 데이터의 전송방식(socket이 두번째 인자)이

동일한 프로토콜이 둘 이상 존재하는 경우"에는 반드시 필요

 

# TCP socket : "IPv4 인터넷 프로토콜 체계에서 동작하는 연결지향형 데이터 전송 소켓"

int tcp_socket=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

# UDP socket : "IPv4 인터넷 프로토콜 체계에서 동작하는 비연결지향형 데이터 전송 소켓"

int udp_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);

# 연결지향형 소켓인 TCP 소켓의 예시(소스코드)

- 전송되는 데이터의 경계가 존재하지 않으므로, 이를 확인하기 위해

read, write 함수 호출횟수를 일부러 다르게 구현

- 서버 : 데이터 전체를 write 함수를 한번 호출하여 모두 전달

- 클라이언트 : read 함수를 여러번 호출하여 데이터 전체를 수신

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char* message);

int main(int argc, char *argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    char message[30];
    int str_len=0;
    int idx=0,read_len=0;

    if(argc!=3) // 인자로 실행파일명/(서버의)IP/(서버의)Port번호 를 전달받아야 함
    {
        printf("Usage : %s <IP> <port>\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    sock=socket(PF_INET,SOCK_STREAM,0); // TCP 클라이언트소켓 생성
    if(sock==-1)
        error_handling("socket() error");

	// 서버소켓의 주소정보를 설정(자세한 내용은 뒤의 챕터에서 설명)
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));

	// 클라이언트소켓이 주소정보를 토대로 서버소켓에게 연결요청
    // 이때, 클라이언트소켓의 주소정보가 할당됨
    // IP는 호스트IP로, Port번호는 임의로.
    if(connect(sock,(struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
        error_handling("connect() error!");

	// read 함수를 통해 서버소켓에서 보내는 데이터를
    // 1바이트씩 읽어들임
    while(read_len=read(sock,&message[idx++],1))
    {
        if(read_len==-1) // read 함수가 -1을 반환하면 오류
            error_handling("read() error!");
        
        str_len+=read_len; // 총 읽어들인 데이터 바이트수를 계산
        				   // (1바이트씩 읽었으므로 read 함수 호출 횟수를 의미하기도 함)
    }

    printf("Message from server: %s \n",message);
    printf("Function read call count: %d \n",str_len);
    close(sock);
    
    return 0;
}

void error_handling(char *message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(EXIT_FAILURE);
}

# 윈도우 운영체제의 socket 함수

#include <winsock2.h>

SOCKET socket(int af, int type, int protocol);
// -> 성공시 소켓 핸들, 실패시 INVALID_SOCKET 반환

- 전달하는 인자의 종류와 의미가 리눅스의 socket 함수와 완전히 동일

- 반환형만 조금 차이가 있는데, 이는 정수로 표현되는 소켓의 핸들값을 저장하기 위해

윈도우에서 정의된 자료형의 이름(SOCKET)

- INVALID_SOCKET의 값은 오류발생을 알리는 하나의 상수로, 이 값은 -1로 정의되어 있음


[출처] : 윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어