공부함

애플리케이션 계층 본문

네트워크/한양대 강의

애플리케이션 계층

찌땀 2023. 12. 22. 01:27

http://www.kocw.net/home/cview.do?cid=6166c077e545b736

 

컴퓨터네트워크

인터넷을 동작시키는 컴퓨터네트워크 프로토폴을 학습한다.

www.kocw.net

Socket Programming

소켓은 클라이언트 프로세스와 서버 프로세스의 통신을 위한 API다. (OS에서 제공)

OS에는 애플리케이션레이어말고도 하위 레이어들이 구현되어있다. 애플리케이션레이어에서는 트랜스포트레이어의 기능을 가져다 사용하고, 따라서 TCP 소켓, UDP 소켓 두 종류 중 하나를 사용하게 된다. 사용하고 싶은 프로토콜에 알맞은 소켓을 사용하면 된다.  TCP소켓이 socket_stream, UDP 소켓이 socket_dgram(datagram)이다.

  • 서버
    • socket : 생성
    • bind : 소켓을 특정 포트에 바인딩, 웹 서버라면 80 포트 
    • listen : 소켓을 리슨 용도로 사용(서버니까)
    • accept : 클라이언트로부터 요청받을 준비 완료 
      • 서버는 클라이언트로부터 connection이 들어올 때까지 block
  • 클라이언트
    • socket : 생성
    • connect : 원하는 서버와 연결 

  • 연결 이후로는 연결된 socket끼리 read-write하면서 데이터 요청, 응답함
  • 끝나면 close

API를 자세하게 알아보자

소켓 생성, 셋업

int socket(int domain, int type, int protocol)

type : 어떤 소켓을 생성할지 결정(UDP,TCP)

반환값 : 소켓의 id값, 이 값으로 이 소켓을 지칭함

int bind(int socketfd, struct sockaddr* myaddr, int addrlen)

소켓의 id값으로 소켓을 특정 포트에 바인딩한다.

참고로 socketfd = socket file descriptor (socket id)임

int listen(int socketfd, int backlog)

소켓을 listen 용도로 사용, 큐에는 최대 backlog만큼의 request 저장

int accept(int socketfd, struct socketaddr* cliaddr, int* addrlen)

준비가 끝나서 클라이언트로부터 연결을 기다림. accept를 호출하면 대기하고 있다가 클라이언트와 연결이 되면 리턴함. 리턴될 때 clidaddr에 클라이언트의 ip와 포트번호가 저장됨

int connect(int socketfd, struct sockaddr* servaddr, int addrlen);

클라이언트는 똑같이 소켓을 생성하고 connect함.

클라이언트는 특정 포트에 바인딩 할 필요가 없어서 bind는 호출하지 않음.(아무 포트나 사용하면 됨)

클라이언트는 bind하지 않음

UDP  소켓은 위에서 설명한 TCP 소켓에 비해 훨씬 단순하다.(설명 X)

int close(int socketfd)

데이터 교환이 다끝나면 close를 호출해 socket을 해제해서 다른 프로세스가 쓸 수 있도록 한다.

mutiplexing/demultiplexing

좌 : mutiplexing 우 : demultiplexing

  •  mutiplexing
    • sender 측 
    • 애플리케이션 레이어의 여러 소켓을 통해 받은 메시지를 하나의 세그먼트로 만들어 아랫 단으로 내려줌
    • 세그먼트는 데이터와 헤더로 이루짐
  • demultiplexing
    • receiver 측 
    • 세그먼트를 알맞은 소켓으로 보내줌 -> 세그먼트의 헤더를 이용함

세그먼트 구조

  • 세그먼트는 위와 같이 헤더, 데이터로 이루어짐
  • 헤더는 소스포트, 데스티네이션포트가있음
    • 이것을 이용해 demultiplexing을 함
  • 실제로 데이터는 헤더에 비해 매우 큼 

UDP에서 demultiplexing

datagramsocket : udp에서 쓰는 소켓

  • 세그먼트를 만들면서 헤더에 소스포트 필드에 출발지 포트번호, 데스티네이션포트 필드에 목적지 포트번호가 적힘
  • 목적지의 IP는 네트워크레이어에서 패킷을 만들면서 헤더의 IP 필드에 쓰임
  • UDP에서는 디멀티플렉싱을 dest IP, dest port #로 진행. 즉 목적지 IP와 포트넘버가 같으면 같은 곳으로 demultiplexing됨

TCP에서 demultiplexing

  • TCP에서는 src IP, src port #, dest IP, dest port #로 디멀티플렉싱을 함
  •  넷 중 하나만 달라도 다른 소켓으로 demultiplexing 됨
  • 이러한 이유로 TCP가 connection oriented이다
    • 특정소켓을 통해 받는 데이터는 무조건 특정 프로세스가 보낸 것이므로
    • 반면  UDP에서는 특정 소켓으로 아무 프로세스나 데이터를 보낼 수 있음(connection X)

실제 웹 서버라면 위 그림처럼 프로세스4(서버)당 프로세스1,2,3(각 클라이언트)를 담당하는 소켓이 있음

서버가 각 클라이언트를 위해 소켓을 생성하고 관리하기 때문에 자원을 많이 소모한다.

UDP

UDP 세그먼트

각 프로토콜이 무슨 일을 하는지는 헤더를 봐야 알 수 있다. 

UDP의 헤더는 4개의 필드를 갖는다. 각 필드가 16bit이므로 포트번호는 0~2^16-1번까지 존재한다.(약 6만)

src port#와 dest port#를 보고 demultiplexing을해준다. length는 세그먼트의 길이다.

checksum은 데이터가 전송 도중 오류가 발생했는지 확인하기 위한 것이다.

수신했을 때 checksum을 통해 에러를 확인하고 에러가 있으면 socket으로 올리지 않고 drop시킨다.

즉, UDP도 최소한의 멀티플렉싱/디멀티플렉싱, 에러체킹은 해준다.

Reliable data transfer의 원리

실제 channel은 unreliable하다. unreliable하다는 것은 메세지 에러, 메세지 손실이 발생할 수 있음을 뜻한다. 이것들을 해결해야 reliable해질 수 있다.

 

우선 간단한 reliable data transfer protocol에 대해 생각해보자. 이 프로토콜은 패킷을 보내고, 리시버가 패킷을 정상적으로 받은 것이 확인되면 다음 패킷을 보내는 방식이다. 줄여서 RDT라 하자. 각 상황별로 어떻게 RDT가 신뢰성을 제공할 수 있을지 생각해 보자. 

RDT1.0

underline channel이 reliable해서 에러, 손실이 발생하지 않는다면 RDT 1.0은 그냥 보내고 받는 역할만 하면 된다.

RDT2.0

underline channel이 packet error가 발생 가능한 채널이라고 가정하자. reliable 하기 위해 어떤 메커니즘이 필요할까?

  • 에러 검사 
    • checksum bit를 추가해서 에러 여부를 판단할 수 있게 함
  • feedback
    • ACKs : 잘 받았다
    • NAKs : 에러 있다 
  • retransmission
    • NAK에 대해 sender는 패킷 재송신

에러가 있는 상황에서는 위 3가지 메커니즘이 필요함. 친구랑 전화하면서 응, 응, 어,.. 뭐라고?를 하는 상황을 생각해보면 이해가 쉽다. 하지만 에러가 발생하는 상황에서 위 3가지 메커니즘 만으로 완벽히 reliable 해질까?

feedback에 에러가 있는 경우가 있을 수 있다!

RDT2.1

feedback에 에러가 있는 경우를 해결한 것을 RDT 2.1이라 한다.

sendner(왼쪽)이 보낸 packet에 대해 receiver(오른쪽)이 ACK를 feedback한다. 하지만 feedback에 에러가 있을 수 있다. 

feedback packet에도 checksum이 있기 때문에 sender는 에러가 발생한 것을 알 수 있다. 아무튼 에러가 발생했으니 ACK인지 NAK인지 알 수 없다. 따라서 receiver가 NAK을 보낸것이라고 가정하고, 다시 보낸다. 

receiver 입장에서는 똑같은 내용이 2번 오는데, 이게 둘 다 내용에 해당하는지 잘못 보내서 다시 보낸건지 알 수 가 없다!

이것을 구분하기 위해 packet에 번호를 붙이게 되고 그게 seq#이다. 위의 예시에서는 sender가 보낸 packet 2개 모두 0번일테니 receiver는 duplicated임을 파악할 수 있다.

즉 feedback에 error가 생겼을 경우 sender는 무조건 재전송 한다. 대신 패킷에 seq#가 있기 때문에 receiver는 duplicated일 경우 해당 패킷을 버린다.    

 

seq#는 부수정보이므로 패킷의 헤더 필드에 위치하게 된다.  seq#를 패킷에 0,1,2,....와 같이 붙이는 것이 자연스럽다. 하지만 이렇게 붙일 경우 seq#의 범위가 무한해지게 된다. 헤더는 작을수록 좋고 헤드의 필드에 속하는 seq# 역시 작을수록 좋다. RDT2.1에서 seq#는 2가지 수만 있으면 된다. 0,1 2가지만 쓰면 구분할 수 있기 때문이다. 

 

RDT2.1 예시

RDT2.2

RDT2.1은 ACK, NAK를 둘다 사용하는데, ACK만 사용할 수도 있다.

리시버가 PKT을 수신하면 무조건 ACK를 응답한다. 하지만 ACK에도 0,1의 seq#를 사용한다. feedback의 seq#는 리시버가 마지막으로 정상적으로 수신한 PKT의 seq#이다. 이것을 통해 sender는 정상 수신 여부를 판단할 수 있다.

rdt3.0

underline channel에서 loss와 error가 다 발생할 수 있는, 현실적인 상황을 생각해보자.

sender가 메세지를 보냈는데 유실된다면? sender는 어떤 feedback을 못받는다. 

이 상황을 해결하기 위해 Timer를 도입해야 한다. sender는 패킷을 보내면서 타이머를 설정, 타이머가 다할 때까지 피드백이 오지 않으면 메세지가 유실되었다고 판단하고 재전송한다. timer의 시간 설정은 적절하게 잘 정해야 한다.

타이머를 짧게 설정하면 리커버리가 빠르다는 장점이 있다. 하지만 유실되지 않았는데도 유실되었다고 판단해 네트워크 상에 중복된 패킷을 마구 보내 네트워크의 오버헤드가 커질 수 있다는 단점이 있다. 물론 중복해서 보낸 패킷을 리시버가 버리는 판단은 할 수 있다. 타이머를 길게 설정하면 네트워크의 오버헤드는 적어지지만, 로스가 발생했을 때 리커버리하는 속도가 느리다. 

rdt 3.0은 완벽히 reliable하게 작동한다.

RDT 정리

  • unrelialbe한 channel에서 발생할 수 있는 문제들
    • packet error
    • packet loss
  • packet error를 해결하는 메커니즘
    • error detection
    • feedback
    • retransmission
    • sequence#
  • packet loss를 해결하는 메커니즘
    • timeout

RTT동안 아무일도 안함

RDT를 현실세계에 적용할 수 있을까? NOPE. 

RDT는 너무 단순하다. 한번에 하나의 패킷밖에 전송하지 못한다. 성능이 떨어진다.

sender는 패킷을 보내고 receiver의 feedback이올때까지 기다렸다가 다음 패킷을 보내기 때문이다.

실제 TCP는 pipelining을 통해 이용률을 증가시킨다.(한번에 여러개 패킷을 보냄)

'네트워크 > 한양대 강의' 카테고리의 다른 글

링크 계층  (0) 2024.01.09
네트워크 계층  (0) 2024.01.01
복습  (0) 2024.01.01
전송계층  (2) 2023.12.29
컴퓨터네트워크 기본  (1) 2023.12.20