[지식창고]/네트워크

[컴퓨터 네트워크] 3.4 (1) principles of reliable data transfer

개발새발주발 2023. 4. 24. 20:48
728x90

Transport layer에서 가장 중요한 역할은 end-to-end 통신의 신뢰성을 확보하는 것이다. 그럼 데이터를 어떻게 reliable하게 보낼까 ?에 관한 고민이 담긴 것이 바로 이 챕터이다. 3.4는 아주아주 .. 방대하고 ... 어렵고 양이 많은 챕터이다 (학생인 나는 죽어난다 ㅜㅜ ~ ) 조금 더 자세하지만 이해하기 쉽게 천천히 다루어보겠다 ! 


1. principles of reliable data transfer 

  • application layer입장에서는 보내기만 하면 reliable이 보장 된다고 생각한다. 
    → transport layer에서는 reliable을 보장해주어야한다. (3장 포스팅마다  쓰는 것 같다 .. ) 

  • 신뢰성있는 데이터 전송 프로토콜을 구현할 때, 통신 채널의 신뢰성 여부가 프로토콜의 복잡성을 결정한다. 

= 예를 들어 만약 데이터 전송 채널이 신뢰적이지 않다면 데이터 패킷이 손상되거나 유실될 가능성이 높아진다. 이를 보완하기 위한 RDT 프로토콜은 더 복잡하고 안정적인 오류 제어 및 재전송 매커니즘이 필요하다 ~~ 

신뢰적이지 않을 수록 우리가 커버해줘야하는 양이 많아진다... 커버해준 만큼 덜 복잡해지기 때문에 통신 채널의 신뢰성 여부가 프로토콜의 복잡성을 결정한다고 하는 것이다. 

 

네가지 동작하는 함수들을 통한 reliablility 보장 

 

  • rdt_send() : application layer에서 transport layer를 호출하여 데이터를 전송
    데이터를 rdt 패킷으로 분할하여 receiver에게 전송, 패킷을 전송하기 전 체크섬 등의 오류 검출 기법을 사용하여 데이터의 유효성 검증
  • udt_send() : transport layer에서 network layer으로 데이터 전송 
    rdt패킷을 network layer으로 전송하기 위해 사용
  • rdt_rcv(): 수신측에서 받기 
    network계층에서 수신한 rdt 패킷을 받아서 데이터를 재조립하고 유효성을 검증 
  • deliver_data() : 수신측에서 호출, 재조립된 데이터를 application으로 전달 

Reliable data transfer : Errors & Solutions 

  • Corruption(Bit-error : 비트 깨짐) 
    → segment당 checksum 
    → 수신자로부터 ACK받기(성공적인 수신확인)
  • Packet Loss (아예 유실이 될 수도 .. 그렇다면 받는 쪽에서는 몰라 ~ ) 
    → Time-out (Sender Timer)
    → Retransmission
    → Sequence Number
          - In-order Delivery(순차적 전송)
          - Preventing Duplication (중복 예방) 

2. rdt 

getting start : 들어가기에 앞서 .. 

FSM모형

프로토콜을 말로 설명하면 .. 엄청 복잡하다 

그래서 우리는 프로토콜 알고리즘을 설명하기 위한 가장 쉬운 방법인 FSM(Finite State Machine)을 통해 학습해보려고 한다. 

FSM은 state, event, action으로 이루어져있다. FSM을 이용하여 프로토콜의 알고리즘을 잘 살펴보자 !!~~~ ! 

 

 

rdt 1.0 : reliable transfer over a reliable channel 

= reliable한 채널 위에 만든 경우(아래 layer가 reliable한 경우) 

  • underlying channel perfectly reliable : 아래 채널(레이어)가 완벽하게 reliable하다 
    • bit에러가 없고, 패킷의 손실이 없다 
  • separate FSMs for sender, receiver : 

- sender는 application later가 데이터를 보내주면 패킷을 만들고(데이터에 헤더를 붙임) 데이터를 underlying channel에 보냄 , 

- receiver는 데이터를 underlying channel으로부터 읽어와서 패킷에서 헤더를 떼고 데이터를 추출하고 데이터를 전달함 ~~ 

 

→ rdt 1.0에서는 underlying channel이 완벽하게 reliable하다는 가정이 있었기에 rdt가 하는 일은 2.0, 2.1,... 등 다음 버전에 비해 많지 않았다 하지만 패킷을 전송할 때 체크섬 등의 오류 검출 기법을 사용하여 데이터의 유효성을 검증하는 것 외에는 별다른 방법을 사용하지 않음 !! 

 

하지만.. 이게 되겠냐고 ~ 

ㅜ - ㅜ

현실적으로는 underlying channel이 완벽하게 reliable하지 않은 경우가 많기 때문에 이런 가정이 현실적으로 작용하기 어려웠다 !! 비정상적인 상황에서의 처리가 불안정하고, 흐름 제어와 혼합 제어를 고려하지 않았기 때문에, 패킷 손실이나 네트워크 지연 등의 문제가 발생한 경우 신뢰성 있는 데이터 전송을 보장할 수 없음 ~~ 

 

그렇기에 1.0에서 발생한 문제를 커버해주기위해서 .. 2.0이 나왔다 ! 

편-안

 

rdt 2.0 : channel with bit errors 

: 최소 bit 에러를 해결할 수 있는 algorithm 

  • 비트 에러가 발생하는 경우 
    → underlying channel이 패킷의 비트를 바꾼 경우 
    → checksum을 통해 bit error을 안다 ! (오류가 있다는 사실만을 알고 어디서 에러가 발생했는지는 모른다.. )
  • 에러를 복구하는 방법 
    → ACKs(acknowledgements) : sender가 receiver에게 패킷을 보내면 receiver가 잘 받았다는 OK사인을 sender에게 보낸다 ! 
    → NAKs(negative acknowledgemnets) : 패킷이 왔는데 checksum을 보니 에러가 있다... 패킷에 에러가 있다는 내용을 sender쪽으로 보냄 

rdt2.0에서의 새로운 매커니즘 ! (rdt 1.0과 비교 ~ )

✅ 에러 감지 

✅ 피드백 : receiver가 sender에게 ACK,NAK메세지를 통해 피드백을 준다. 

 

rdt 2.0 : FSM specification 

 

 

그런데 rdt 1.0에서 문제점을 보완했음에도 불구하고 rdt 2.0에서도 문제가 발생한다. !! 

 

⚠️ rdt 2.0의 문제점 

- ACK, NAK마저 비트에러가 발생할 가능성 

  • 송신자는 수신자측에서 어떤 일이 일어났는지 몰라 ~ 
  • ack이던, nak이던 다시 전송하면 된다 .. 그런데 이렇게 다시 전송된 결과가 어디서 왔지? receiver에서 이미 왔던 건지, 새 건지 알 수 없음 ㅜㅜ ~ 그런데 이 상황에서 그냥 다시 전송하면 duplicate될 가능성이 있다 ! 

- duplicate 해결 방법 

  • sender는 ACK, NAK에서 corrupt가 일어나면 패킷 재전송을 한다. 
  • sender는 각각의 패킷에 sequence number를 붙임 
  • duplicate인지 아닌지 알 수 없음 ! receiver는 받은 패킷이 왔으면 전송하지 않고 버린다!!! 
예시 :
sender가 receiver에 패킷을 보내서 receiver가 ACK을 보내줬는데 sender에서 못받았다고 가정한다. 
→ 다시 패킷을 재전송하면 receiver 입장에서 새 패킷인지 왔던 패킷인지 모른다. 
→ 그래서 sender가 숫자를 붙여서 0번 패킷을 보낸다. 
→ receiver가 받아서 ACK을 보내줬는데 만약 sender가 못받으면 
→ sender는 다시 0번 패킷을 보낸다. 
→ receiver 입장에서 0번은 왔던 거니까 버리고 그냥 ACK만 보내준다. 

: 출처 : 
https://velog.io/@kms9887/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-3.-Transport-Layer2#rdt-20-channel-with-bit-errors

 


rdt 2.1 : handles grabled ACK/NAKs 

✅ 왜곡된 ACK/NAKs 처리하기 = 위에서 발생한 문제점 해결을 위해 sequence number 이용 ! 
→ sequence number를 나타내기 위해 1비트만 사용한다(0,1 만 구분이 가능함)
→ 이것을 통해 ACK/NAK 손실을 해결한다. 

 

rdt 2.1 : FSM specification

 

Sender 

 

 

receiver 

< 문제점 > 

sender 

  • 패킷에 sequence number를 붙인다. 
  • stop- and -wait : 0,1 만 써도 충분하다 
  • 받은 ACK/NAK 이 corrupte 인지 확인해야한다.
  • ACK이 와야 다음 패킷을 보낸다
  • 상태가 2배 많다(전에 온 넘버 기억, 어떤 넘버가 올 지 대기) 

receiver 

  • 받은 패킷이 duplicate인지 아닌지 확인해야한다. 

** 참고 : receiver 는 마지막 ACK/NAK가 sender에게 OK를 받았는지 아닌지 모른다 ~~ 

 


rdt 2.2 : a NAK-free protocol

NAK 말고 다 ACK로 쓰자 ! 

* 송신측이 수신측 패킷을 정상적으로 수신하지 못한 경우에만 패킷을 재전송하는 기능을 추가하자 ( 네트워크 대역폭 효율적으로 사용 가능함) 

 

✅ 기능은 rdt 2.1과 같지만 ACK만을 사용한다(NAK를 사용하지 않음) 

✅ NAK 대신 ACK활용 - OK받은 마지막 패킷에 대해 ACK 보내줌 

→ 수신자가 ACK메세지를 보낼 때, ACK메시지에 포함된 시퀀스 번호가 수신자가 받은 패킷 중에서 어떤 패킷을 ACK하고 있는지 명확하게 표시해야한다 ( = ACK에 sequence number 넣어서 보내준다 !) 

✅ sender에서 받은 중복된 ACK이 NAK의 기능을 한다. 

→ 중복된 ACK 받으면 현재 패킷을 전송함 

2.1에서 바뀐 부분만 표현

sender 

→ ACK 0을 기다리는 상태에서 corrupt이거나 받은 ACK이 1번이면 재전송하고 상태 유지 

→ corrupt 아니고 0번 ACK을 받았으면 다음 상태로 상태 변경 

 

receiver 

→ 1번 패킷을 받으면 상태 변경(1 → 0 을 기다리는 상태) 

→ sndpkt(ACK 1 가진 패킷) 만들고 상태 변경 

→ 0 기다리는 상태에서 오류가 있거나 sender에게서 받은 패킷이 1번이면 sndpkt를 보내고 상태 유지 

→ sndpkt에는 ACK 1 저장되어 있으니 sender입장에서는 계속 재전송해줘야함

* (sender가 receiver 패킷을 정상적으로 수신하지 못한 경우에만 패킷을 재전송하는 기능을 추가하는 것이 2.2니까 !)


rdt 3.0 : channels with errors and loss

rdt 2.x의 프로토콜이 가지고 있는 손상된 패킷 재전송 기능 개선

 

new assumption : 

데이터와 ACK 메시지 모두가 전송 중 손실될 수 있다 

 * 이전에 사용되었던 checksum, seq #, ACKs, retransmissions 메커니즘 등의 오류제어 기술들은 도움이 되지만 .. 충분하지 않아 ! 

 

approach : 

sender는 ACK를 resonable한 시간동안 기다릴거야 

➡️ 송신자가 ACK 메시지를 기다리는 동안 일정 시간이 지나도록 ACK 메시지를 받지 못하면 재전송을 수행한다

➡️ 패킷이나 ACK 메시지가 지연되는 경우 : 

→ 재전송은 중복이 될 것이지만 seq# 가 이것을 처리해줄 것이니 문제가 발생하지 않아 ! 

→ 수신자는 ACK메시지에서 명시적으로 ACK하고 있는 패킷의 시퀀스 번호를 지정해야함 

➡️ 이러한 방법을 구현하기 위해서는 카운트다운 타이머가 필요하다

 

초기 상태 : 패킷 만들고 전송한 후 타이머 시작해서 다음 상태로 넘어감 

→ 오긴 왔는데.. 시퀀스 넘버가 다르거나 문제가 있으면 타이머는 그냥 흘러가도록 두고 상태 유지 

→ 타임아웃 걸리면 타이머 다시 시작하고 상태 유지

→ ACK이 제대로 오면 타이머 스탑하고 다음 상태로 넘어감 

 

동작 과정 

(d)에 대해서 조금 더 자세히 보자 

타이머를 너무 짧게 잡으면 제대로 보내지지만 그만큼 불필요한 재전송이 많아진다. 

→ performance 측면에서 좋지 않음 

타이머를 너무 길게 잡으면 loss가 발생했을 때 복구까지 너무 오래 걸린다. 

→ 적정한 시간의 타이머를 구현하는 것이 좋음 ! 


rdt3.0의 성능 

: rdt 3.0은 기술적으로 정확하고 오류가 없지만 .. 성능이 좋지 않다 ~ 

왜 성능이 안좋은가.. stop and wait방식을 선택해서 사용하기 때문 ~~ ! 

전송속도 1Gbps, 전파속도 15ms, 8000bit의 패킷을 보낸다, sender-receiver사이가 회선으로 연결되어있다고 가정하자 

➡️ Dtrans : 패킷을 내보내는데 걸리는 시간 = L/R = 8microsecs

→ 전파시간 : 한 패킷이 끝까지 가는데 걸리는 시간 : 15ms

→ 보내는 데 총 시간 = 전송시간 + 전파시간 = 15.008ms

→ACK는 비트수가 작아 전송시간이 매우 작다고 판단해서 무시하고 전파시간만 고려 

→ 따라서 패킷이 가는 데 걸리느 시간 + ACK이 오는데 걸리는 시간 = 15.008 + 15 = 30.008ms 

➡️ Usender : 송신자가 전송 작업을 수행하는 종안 송신자가 바쁘게 일하는 시간의 분수를 나타내는 값 

 = (L/R) / (RTT + L)/R = 0.008/30.008 = 0.00027 

8000비트 보내고 ACK 받는데 30.008ms 걸린다. 

= 효율성이 매우 떨어진다 ! ( 효율성은 1일때가 가장 좋음) 

➡️ 하나 보내고 하나 돌려 받는 동안 기다리는 시간때문에 효율성이 낮아진다 ! 

 

stop-and-wait operation 

패킷 하나 보내고 다시 받고 하나 보내고 받고.. 하는 방식 

→ 전체 시간(RTT + L/R)에 비해 파일 전송에 걸리는 시간 L/R이 너무 짧다 = 효율성이 떨어진다 ! 

 

Pipelined protocols 파이프라이닝 프로토콜

pipelining : 송신자가 여러개의 'in-flight(아직 확인되지 않은)' 패킷을 전송할 수 있게한다. 

▪ 일련 번호(sequence numbers)의 범위를 증가시켜야한다. (seq#를 통해 보내지는 여러 패킷들을 구별하기 위해) 

▪ 송신자 와/or 수신자에서 버퍼링을 수행해야한다. 

go-Back-N, selective repeat이라는 두가지 일반적인 파이프라인 프로토콜 형태가 있음 ! 

정해진 개수의 여러개의 패킷을 보내고 ACK이 오는 대로 다음 패킷을 보낸다

+ 효율성이 정해진 개수배만큼 늘어난다.

+ performance 측면에서 좋아진다.

- but, 프로토콜이 복잡해진다 ! ! 

 

Pipeline protocols : overview 

 

앞서 파이프라인 프로토콜에는 Go-back-N, Selective Repeat이 있다고 했다 ~ ! 

이 두 파이프라인 프로토콜에 대해서 살펴보자 

 

Go-Back-N 

: 손상된 패킷을 제외한 모든 패킷이 성공적으로 수신되어야만 다음 패킷을 전송할 수 있는 형태의 파이프라인 프로토콜

❖ 송신자는 최대 n개의 확인되지 않은 (unacked) 패킷을 파이프라인 상에 가지고 있음 

→ 이 때 N은 윈도우 크기(window size)로도 불린다 ! 

❖ 수신자는 누적응답(cumulative ack)만을 보낸다

→ 빈 공간(gap)이 있을 경우 해당 패킷을 확인하지도, 응답하지 않음 (ack 안함)

❖ 송신자는 가장 오래 확인되지 않은 패킷에 대한 타이머(timer)를 가지고 있으며, 타이머가 만료되면 모든 확인하지 않은 패킷을 다시 전송한다. 

 

Selective Repeat

: 패킷 손실이 발생해도 다른 패킷들은 계속 전송될 수 있으므로 전송 시간을 최적화할 수 있다 ! 

송신자는 최대 n 개의 확인되지 않은 패킷을 보낼 수 있다. 

수신자는 각 패킷에 대한 개별 확인 응답(individual ack)을 보낸다. 

송신자는 각 확인되지 않은 패킷에 대한 타이머를 유지한다. 

타이머가 만료되면 송신자는 해당 확인되지 않은(unacked)패킷만 재전송한다. 

 

 

Pipeline protocols : Go-Back-N 

 

- Sender

k-bit 를 써서 패킷들을 seq num을 통해 구별한다. 

필요한 parameter : send_base, window size, nextseqnum

window_size: 한번에보낼 수 있는 패킷의 개수 

send_base : 보낼수 있는 윈도우의 맨 처음

nextseqnum : 다음에보내질 패킷의 seq# 

 

 

- ACK (n) : n번까지 ACK 다 받았다는 의미 = cumulative ACK 

→ ack을 못받더라도 패킷만 잘 전달되었으면 그 다음 ack이 오면서 앞 ack까지 acknowledge할 수 있음 이렇게 누적 ack사용

- oldest in-flight packet에 대한 타이머 

→ in-flight = outstanding = set but not acked 

: 아직 ack 못받은 패킷 중 seq#가 가장 앞에 있는 것 

이 패킷에 대한 타이머를 가진다 = 타이머 하나면 된다 

- timeout(n) : n이라는 시퀀스 넘버에서 타임아웃이 걸렸으면 n부터 전부 다시 보냄 

 

* GBN이용한 송신-수신 과정 

1. 송신자는 패킷을 전송하고, 전송된 패킷의 번호를 seq번호로 할당한다. 그리고 패킷을 내부적인 버퍼에 유지
2. 송신자는 N개의 패킷을 전송하고 이들 각각에 대해 타이머를 시작함. 이 타이머는 패킷이 수신특으로부터 확인 응답을 받을 때까지 대기한다. 
3. 수신자는 패킷을 수신하고, 시퀀스 번호를 확인함 수신한 패킷이 현재 수신특에서 기대하는 다음 패킷일 경우, 수신측은 해당 패킷을 받아들이고 다음으로 기대되는 패킷의 번호를 확인 응답 msg에 포함시켜 송신측에게 전달 
4. 송신자는 확인 응답 메시지를 수신하고 해당 메시지에 포함된 번호보다 작은 모든 패킷이 수신측에서 확인되었다는 것을 알고, 해당 패킷들을 버퍼에서 제거
5. 송신자는 타이머가 만료되기 전에 수신측으로부터 확인 응답을 수신하지 못한 모든 패킷을 재전송한다. 이때, 송신자는 시퀀스 번호를 다시 할당하여, 재전송된 패킷이 이전에 전송된 패킷과 구별될 수 있도록한다. 
6. 수신자는 재전송된 패킷을 다시 수신하게 되면 이미 수신한 패킷이므로 무시
7. 송신자는 모든 패킷이 수신측에 의해 확인될 때까지 이 과정 반복 

 

GBN : sender extended FSM 

GBN : receiver extended FSM 

초기 상태 : expectseqnum = 1로 해주고 그 번호를 ACK로 만든다

올 것 잘 오면 처리 잘 해주고 보내고 expectedseqnum ++

default(에러, 패킷 깨짐 등 ) : 이전에 만든 패킷 보내줌 

 

동작 과정 

 

 

Pipeline protocols : selective repeat 

 

❖ 수신자는 각각의 올바르게 수신한 데이터 패킷에 대해 개별적으로 확인 응답(ACK)을 보내며 필요한 경우에는 상위 레이어에 순서대로 전달할 수 있도록 패킷을 버퍼링한다. 

❖ 송신자는 ACK를 받지 못한 패킷만 다시 전송하며, 각각의 전송한 패킷에 대한 타이머를 가지고 있음 

❖ 송신자는 ACK를 받지 못한 패킷만 다시 전송하며, 각각의 전송한 패킷에 대한 타이머를 가지고 있음 

❖ 송신자 window는 연속적인 일련번호를 갖는 N개의 패킷으로 구성되어 있으며, 전송된 순서대로 ACK를 받지 못한 패킷의 seqnum을 제한한다. = 제일 작은 것 부터 보냄 ! 

 

sender : 

data from above 

- 만약 윈도우 내에서 사용 가능한 다음 seq#가 있다면 패킷 전송 

timeout(n)

- timeout(n)함수는 패킷 n에 대한 타이머가 만료되면, 패킷 n을 다시 전송하고 타이머를 재시작함 

ACK(n) in [sendbase, sendbase+N-1] 

- 패킷 n이 수신되었다는 것을 표시 

- 패킷 n이 가장 작은 확인되지 않은 패킷이라면, 윈도우 기반을 다음 확인되지 않은 일련 번호로 이동한다. 이를 통해 순서대로 수신된 패킷만 상위 레이어로 전달할 수 있음 ! 

 

 

receiver :

pkt n in [rcvbase,rcvbase+N-1] (패킷 n의 일련 번호가 [rcvbase,rcvbase+N-1] 범위 내에 있다면,)

- send ACK(n) (수신확인 패킷 보냄)

- out-of-order : buffer 

- in-order : 순서대로 수신된다면 버퍼링된 순서대로 패킷을 전달하고 윈도우를 다음 아직 수신되지 않은 패킷으로 이동시킨다. 

 

pkt n in [rcvbase-N,rcvbase-1]

- send ACK(n) 

그렇지 않은 경우에는 무시한다 

 

 

* worst case 

 

*selective repeat in action 

 

 

* selective repeat dilemma 

seqnum을 재사용하기에 발생하는 문제다 ! 

receiver는 sender side를 볼 수 없다 ~ ! 

 

Q - A : seqnum가 window의 크기보다 2배 더 커야한다 → 보내는 쪽에서는 최악인 경우, 받는 쪽 best경우 따져보기