[UNIX] TCP/IP 프로토콜, 소켓 프로그래밍

Taegang Yun·2023년 12월 9일
1

Unix 프로그래밍

목록 보기
18/19

네트워크 계층 구조의 기준이라고 할 수 있는 ISO의 OSI 7 계층과 달리 TCP/IP는 5개 계층이다.

  • 응용 계층
    • 사용자에게 서비스를 제공하기 위한 계층
    • Telnet, FTP, HTTP, SMTP, DNS
  • 전송 계층
    • 패킷의 전송을 담당하는 계층
    • TCP, UDP
  • 네트워크 계층
    • 패킷이 전달되는 경로를 담당
    • IP
  • 네트워크 접속 계층, 하드웨어 계층
    • 물리적인 네트워크와의 연결을 담당. 이더넷 카드, 랜 카드

TCP / IP 프로토콜

TCP

  • 전화를 걸 때처럼 데이터를 주고받기 전에 송신측과 수신측이 연결되어 있어야 함.
  • 송신한 데이터가 수신측에 도착했는지 확인하는 과정을 거침
  • 데이터를 주고 받는 속도를 조절할 수 있음

UDP

  • 사전에 목적지와 연결을 설정하지 않고, 상대방이 데이터를 수신 했는지 확인하지 않음
  • 단순히 목적지 주소를 지정해 네트워크로 전송
  • 중도에 데이터가 분실되어도 신경 쓰지 않음
  • 신뢰성보단 속도

소켓 인터페이스

  • 응용 계층에서 전송 계층의 기능을 사용할 수 있도록 제공하는 응용 프로그램 인터페이스(API)
  • 응용 프로그램과 TCP 계층을 연결하는 역할

IP 주소와 호스트명

  • IP 주소
    • 인터넷을 이용할 때 사용하는 주소로, 점(.)으로 구분된 32비트 숫자로 표시
    • IP 주소를 네트워크 주소라고도 함
  • 호스트명
    • IP주소 외에 이름을 지정
    • 인터넷에서 사용하는 호스트명은 호스트명+도메인명 형태로 구성
    • DNS : 호스트명과 도메인명을 관리하는 시스템

호스트명과 IP주소 읽어오기 : gethostent(), sethostent(), endhostent()

  • h_name : 호스트명을 저장
  • h_aliases : 호스트를 가리키는 다른 이름들을 저장
  • h_addrtype : 호스트 주소의 형식을 지정
  • h_length : 주소의 길이를 저장
  • h_addr_list : 해당 호스트의 주소 목록을 저장,이 항목의 값을 해석하려면 2절에서 배우는 함수들이 필요
int main(){
	struct hostent *hent;
	
	sethostent(0);

	while((hent = gethostent()) != NULL)
		printf("Name=%s\n", hent->h_name);

	endhostent();
}

호스트명으로 정보 검색 : gethostbyname()

IP 주소로 정보검색 : gethostbyaddr()

포트 번호

  • IP 주소 구분의 필요성
    • IP 주소는 데이터가 전송될 목적지 호스트를 알려주는 역할을 수행.
  • 포트 번호
    • 2바이트 정수로 되어 있으므로 0~65535까지 사용가능
    • 대표적인 포트 번호는 SSH 프로토콜이 22, FTP가 21, HTTP가 80
    • 일반 프로그램 1024~65535
    • 이미 정해진 포트 번호는 /etc/services 파일에 등록

포트정보 읽어오기 : getservent(), setservent(), endservent()

포트 정보 읽어오기

int main(){
	struct servent *port;
	int n;

	setservent(0);

	for(n = 0; n < 5; n++){
		port = getservent();
		printf("Name = %s, Port = %d\n", port->s_name, port->s_port);
	}


	endservent();
}

서비스명으로 정보 검색 : getservbyname(name, proto)

  • proto에는 “tcp”나 “udp” 혹은 NULL

포트 번호로 정보 검색 : getservbyport()

소켓의 종류

  • 유닉스 도메인 소켓
    • 같은 호스트에서 프로세스 사이에 통신할 때 사용
    • 패밀리명 : AF_UNIX
  • 인터넷 소켓
    • 인터넷을 통해 다른 호스트와 통신할 때 사용
    • 패밀리명 : AF_INET

소켓의 통신 방식

  • 프로토콜
    • 소켓을 이용할 때도 하부 프로토콜로 TCP를 사용할 것인지 UDP를 사용할 것인지 지정해야 하는데 이 때 정의된 상수 사용
    • SOCK_STREAM : TCP 사용
    • SOCK_DGRAM : UDP 사용
  • 소켓의 네 가지 통신 유형
    • AF_UNIX - SOCK_STREAM
    • AF_UNIX - SOCK_DGRAM
    • AF_INET - SOCK_STREAM
    • AF_UNIX - SOCK_DGRAM

바이트 순서 함수

  • 빅 엔디언
    • 각각 바이트를 순서대로 저장
    • 메모리의 낮은 주소에 정수의 첫 바이트를 위치
  • 리틀 엔디언
    • 각각 바이트를 거꾸로 저장
    • 메모리의 높은 주소에 정수의 첫 바이트를 위치
  • 네트워크 바이트 순서(NBO), 호스트 바이트 순서(HBO)
    • TCP/IP 에서는 무조건 빅 엔디언 방식 채택
    • 통신을 통해 데이터를 내보낼 때 HBO → NBO, 받으면 NBO→HBO 변환 후 처리

NBO를 HBO로 변환

int main(){
	struct servent *port;
	int n;

	setservent(0);

	for(n = 0 ; n < 5; n++){
		port = getservent();
		printf("Name = %s, Port = %d\n", port->s_name, ntohs(port->s_port));
	}

	endservent();

}

HBO를 NBO로 변환

int main(){
	struct servent *port;
	
	port = getservbyname("ssh", "tcp");
	printf(("Name = %s, Port = %d\n", port->s_name, ntohs(port->s_port));

	port = getservbyname(htons(21), "tcp");
	printf(("Name = %s, Port = %d\n", port->s_name, ntohs(port->s_port));



}

포트 번호로 검색하려면 포트 번호를 NBO로 지정해야 함

getservbyport() 함수의 첫 번째 인자는 htons() 함수를 사용해 포트 번호를 HBO에서 NBO로 변환해 지정

문자열 IP 주소를 숫자로 변환 : inet_addr()

ip 주소를 문자열로 받아 이를 이진값으로 바꿔서 리턴

구조체 IP 주소를 문자열로 변환 : inet_ntoa()

소켓 인터페이스 함수의 종류

  • socket( ) : 소켓 파일 기술자 생성
  • bind( ) : 소켓 파일 기술자를 지정된 IP 주소/포트 번호와 결합
  • listen( ) : 클라이언트의 연결 요청 대기
  • connect( ) : 클라이언트가 서버에 접속 요청
  • accept( ) : 클라이언트의 연결 요청 수락
  • send( ) : 데이터 송신(SOCK_STREAM)
  • recv( ) : 데이터 수신(SOCK_STREAM)
  • sendto( ) : 데이터 송신(SOCK_DGRAM)
  • recvfrom( ) : 데이터 수신(SOCK_DGRAM)
  • close( ) : 소켓 파일 기술자 종료

유닉스 도메인 소켓(서버)


#define SOCKET_NAME "hbsocket"

int main(){
	char buf[256];
	struct sockaddr_un ser, cli;
	int sd, nsd, len, clen;

	sd = socket(AF_UNIX, SOCK_STREAM, 0);

	memset(&ser, 0, sizeof(struct sockaddr_un));
	ser.sun_family = AF_UNIX;
	strcpy(ser.sun_path, SOCKET_NAME);
	len = sizeof(ser.sun_family) + strlen(ser.sun_path);

	bind(sd, &ser, len);
	listen(sd, 5);

	nsd = accept(sd, &cli, &clen);
	recv(nsd, buf, sizeof(buf));

	printf("Received message : %s\n", buf);
	close(nsd);
	close(sd);
}

유닉스 도메인 소켓(클라이언트)

#define SOCKET_NAME "hbsocket"

int main(){
	int sd, len;
	char buf[256];
	struck sockaddr_un ser;

	sd = socket(AF_UNIX, SOCK_STREAM, 0);

	memset(&ser, '\0', sizeof(ser));
	ser.sun_family = AF_UNIX;
	strcpy(ser.sun_path, SOCKET_NAME);
	len = sizeof(ser.sun_family) + strlen(ser.sun_path);

	connect(sd, &ser, len);

	strcpy(buf, "Unix Domain Socket Test");
	send(sd, buf, sizeof(buf));

	close(sd);
}
profile
언젠간 전문가가 되겠지

0개의 댓글