network 3강 - Transport layer
Multiplexing/Demultiplexing
- multiplexing : 애플리케이션 계층에서 내려온 message들을 세그먼트로 만들어주는 것
- demultiplexing : 네트워크 게층에서 올라온 segment를 올바른 프로세스의 소켓으로 보내주는 것
- demultiplexing 할 때 어느 프로세스로 보낼 지 정하는 것은 들어온 segment의 헤더 중 source port와 dest port로 결정한다.
UDP의 Demultiplexing
- UDP는 오직 dest ip와 dest port를 이용해 demultiplexing이 이루어진다.
TCP의 multiplexing
TCP는 dest ip, dest port 뿐만 아니라 src ip, src port를 이용해 demultiplexing 한다. 즉, 위 4 중 하나라도 다르면 다른 소켓으로 보낸다.
위의 두 protocol을 살펴보면 TCP의 경우 1대1로 소켓이 연결되고, UDP는 어디서 오든 상관없이 dest ip와 dest port만을 보고 해당 소켓으로 보내기 때문에 다른 특정 프로세스와 연결되어 있지 않다(1대1이 아님). 이렇듯 TCP는 연결형, UDP는 비연결형이란 사실을 알 수 있다.
UDP
- UDP는 최소한의 서비스만 제공
- multiplexing/demultiplexing과 에러 발생 여부를 판단하는 checksum만이 존재한다.
TCP
- TCP가 제공하는 여러 서비스는 환상 같은 일 -> TCP 아래의 계층들은 사실 신뢰성을 보장하지 못해줌(라우터 큐가 가득차 패킷 로스 등)
- 하지만 2가지 문제(패킷 에러, 패킷 로스)만 해결하면 reliable한 전송이 가능하다.
TCP의 Reliable data transfer를 위한 매커니즘
패킷 에러 해결방안
checksum bits를 통해 에러 detection
잘 받았다와 같은 feedback 필요
- ACKs / NAKs
재전송
문제 : 만약 feedback에 에러가 발생하면?
- 에러가 발생하면 그냥 재전송
- 하지만 재전송했을 때도 문제가 있다
- receiver 입장에서 재전송된 패킷 에러 때문에 재전송한거지 중복된 메시지인지 알 수 없음 (ex) potato potato의 경우 두번째로 온 potato가 에러인지, 제대로 된 메시지인지 모름)
- 이런 문제를 해결하기 위해 메시지에 번호를 붙임 -> sequence number
NAK없이 ACK만을 사용 -> ACK에 받은 seq number를 포함해서 확인
패킷 로스 해결방안
- Timer를 사용
- feedback이 일정시간 오지 않으면 재전송
- Timer의 시간 설정은 중요한 문제
- Timer가 짧으면 로스가 발생 시 빨리 재전송, 하지만 정상적으로 가고 있지만 돌아가느라 오래 걸린 경우 로스가 아님에도 재전송(네트워크 오버헤드 발생)
- Timer가 긴 경우 로스가 발생했을 때 receiver가 오래 기다림, 하지만 정상 흐름의 경우 오버헤드 적게 발생
네트워크 이용률
- 만약 TCP 통신을 하면서 하나의 패킷만을 주고 받으면 어떻게 될까?
- sender가 패킷을 보낸 후 receiver가 ACK가 올 때까지 sender는 쉬게 된다 -> 네트워크 이용률 낮음
- 이용률을 높이기 위해서는 패킷을 여러개 보낼 수 있어야 한다(reliable을 지키면서).
- 패킷을 보내고 피드백이 오기 전에 또 다른 패킷을 보내는 것을 파이프라인 방식이라고 한다.
- 이 파이프라인은 2가지 방식으로 구현된다 -> go-Back-N, selective repeat
go-Back-N
- 패킷을 얼마나 보낼지를 정한 window size만큼 피드백을 받기 전에 패킷을 보내는 방식이다.
go-back-n은 ACK에 sequence 번호를 붙여서 보내는데 이 번호의 의미는 해당 번호까지 잘 받았다는 의미 -> ACK 11이라면 12번 받을 차례
만약 위도우 사이즈가 5일 때, 각 패킷에는 타이머가 각각 존재하는데 1번 패킷이 타이머가 터지면 1~4번을 모두 재전송한다.(윈도우에 있는 터이머 터진 패킷보다 더 큰 번호를 가진 패킷 다 재전송)
receiver의 입장에서는 단순하다.
- PKT 0 받음 -> ACK 0을 보내고 1번을 기다림
- PKT 1번보다 2번이 더 빨리옴 -> 1번을 받아야하기 때문에 2번은 버림 다시 ACK 0
- PKT 1번이 뒤늦게 도착 -> 잘 받음 ACK 1
- PKT 3이 옴 -> 2번 패킷을 받아야하기 때문에 3번은 버림
- PKT 2번이 ACK 2를 받지 못해서 Timeout 터짐 -> sender는 PKT 2번부터 다시 다 재전송
- 이때 window는 0번과 1번은 잘 받았기 때문에 한 칸 씩 전진함 -> 위의 상황에서 timeout시 재전송하는 PKT[2, 3, 4, 5] (window size = 4)
- window안에 있는 pkt들은 결국 receiver가 받았는지 확인이 필요한 pkt이므로 저장 -> window = buffer와 같은 의미
- go-back-n의 경우 하나의 패킷만 유실되도 n만큼 재전송해야한다. 실제 window 사이즈는 매우 크므로 그 사이즈 만큼 재전송하는 것은 매우 비효율 -> 이걸 보안한게 Selective repeat
Selective Repeat
- go-back-n과 다르게 재전송 해야하는 패킷만 재전송하는 방식
- selective repeat의 ACK는 go-back-n과 다르게 사용
- go-back-n : ACK의 번호는 해당 번호까지 잘 받았다는 의미
- selective repeat : ACK의 번호는 해당 번호의 패킷을 잘 받았다는 의미 -> ACK 온 번호의 패킷은 타이머 해제
- go-back-N과 다르게 receiver의 역할이 더 생김
- 순서에 맞지 않게 온 패킷들도 받아들이고 buffer에 저장 후 ACK를 보냄
- 이런 sequence 번호를 사용할 때 주의할 점은 sequence 번호를 무한정 늘리면 그만큼 오버헤드가 발생한다는 것이다. 그래서 sequence 번호를 적절히 설정하는 것이 중요한데 보통 window size * 2 정도로 생각하자
- 실제 TCP에서는 이 두 방식의 장점을 합쳐서 사용한다. selective repeat과 같은 방식은 좋아보이지만 모든 패킷에 타이머를 달아야한다는 단점이 있다. 실제 window 크기는 매우 크기 때문에 모든 패킷에 타이머를 다는 것은 현실적으로 불가능하다.
TCP에 대해
- 특징
- point-to-point : 한 쌍의 프로세스들(socket) 간의 통신을 권장
- full duplex data : 데이터를 양방향 주고 받음, 모두가 sender이며 receiver임
- send & receive buffers : 모두가 sender이며 receiver이기 때문에 send, receive buffer를 각각 가지고 있다.(send buffer는 재전송을 위한 버퍼, receive buffer는 제대로 전송된 패킷을 저장하기 위한 버퍼)
- flow controlled : receiver가 받을 수 있을만큼 패킷을 전송
- congestion control : 네트워크 상황을 고려해서 패킷을 전송
- reliable, in-order byte stream
TCP segment structure
TCP seq / ACKs
- TCP에서 sequence 넘버는 보내고자 하는 데이터의 첫번째 byte의 번호를 뜻한다.
- 만약 100byte짜리 데이터를 보낸다면 처음에는 0부터 시작해서 첫 패킷의 sequence 넘버는 0이 되고, 그 다음 바이트의 번호가 10번이면 seq 10이 된다.
- TCP에서 ACK는 go-back-n에서의 ACK와 비슷하다. go-back-n에서는 ACK 10의 의미는 10번까지 다 받았다는 뜻이지만, TCP에서는 ACK 10의 의미는 9번까지 다 받았으니 10번이 필요하다는 의미이다.
- 그때 위의 그림을 보면서 TCP가 어떻게 통신하는지 알아보자
- Host A가 ‘C’를 보내면서 C가 몇번째 byte인지 알려준다 : Seq=42 / Host A send buffer에 42번 패킷 저장
- 동시에 Host A가 몇번 데이터부터 필요한지 ACK를 함께 보낸다 : ACK=79 / Host A의 receive buffer에서 몇번째 데이터 필요한지 확인
- Host B는 들어온 요청의 Seq번호를 통해 42번이 데이터가 온 것을 확인하고 데이터의 길이만큼 ACK를 더해준다 : ACK=43(‘C’ 한 바이트라 1 증가) / Host B의 receive buffer에 42번 패킷 저장
- Host B는 ‘C’ 데이터를 애플리케이션에 올려준 뒤 Host A가 보낸 요청에서 ACK를 확인한 후 해당 번호부터 시작하는 데이터를 보냄 : Seq=79 / Host B의 sender buffer에 79번 패킷 저장
- 반복
- 이렇게 되면 Host A의 send buffer는 Host B의 receive buffer와 Host B의 send buffer는 Host A의 receive buffer와 짝을 이뤄서 ACK와 Seq를 보내는 것을 알 수 있다.
- 실제 TCP는 요청이 들어왔을 때 바로 ACK 하지 말고 500ms 정도 기다리라고 한다. 그 이유는 다음과 같다.
- 만약 Host A가 데이터를 받은 뒤 바로 ACK하는 것보다 Host A에 요청이 들어올 가능성이 있으므로 그때 ACK와 데이터를 함께 보내면 이득이므로
- 실제 TCP는 파이프라인으로 여러 패킷을 보내는데 들어오는 패킷에 일일이 ACK를 보내는 것보다 파이프라인으로 들어온 패킷을 모두 받은 뒤 ACK를 보내는 것이 이득이기 때문
TCP timeout
TCP에서는 timeout 시간을 어떤 식으로 결정할까? 너무 짧거나 길면 오버헤드가 발생하기 때문에 적절한 timeout 시간을 선정해야한다.
보통 데이터를 보내고 ACK를 받는데 까지의 시간을 RTT라고 하는데 RTT에 딱 맞춰서 timeout 시간을 정하면 오버헤드가 없을 것이다.
하지만 RTT의 값은 매번 달라지기 때문에 RTT가 딱 맞아 떨어지는 이상적인 상황은 없다 (라우터 상황, queueing delay등)
그렇기 때문에 TCP는 EstimtedRTT라는 값을 사용한다. EstimtedRTT는 그동안의 RTT 값의 평균 값인데 이 값을 그대로 사용하면 너무 타이트하기 때문에 보통 EstimtedRTT에 4*devRTT를 더해서 사용한다. 대충 넉넉하게 사용한다고 이해하자.
또 TCP는 sender buffer에 타이머를 하나만 사용한다.
TCP 시나리오
Fast Retransmit
- TCP에서 패킷 로스 상황을 Timer가 터지기 전에 알 수 있는 방법이 있다.
- 만약 0 ~ 100번 패킷을 파이프 라인으로 보냈고 중간에 10번만 로스되었다고 가정하자
- 그때 receiver는 ACK0 …. 9번까지는 정상적으로 보낸다.
- 그때 10번 패킷은 로스되었기 때문에 ACK 10을 보낸다.
- 그 다음 11번 패킷이 와도 10번이 필요하므로 ACK 10을 다시 보낸다. 이걸 반복한다.
- 이런 식으로 ACK 10이 로스 되었다는 것을 ACK를 통해 짐작할 수 있다.
- 그렇기 때문에 TCP에서는 중복 ACK가 3번오면 로스되었다고 판단해 재전송한다. (첫번째는 제외하고 중복 3번이라 총 중복 ACK는 4번 발생)
TCP Flow Control
- TCP는 receiver가 받을 수 있을만큼만 sender가 보내도록 flow control를 해준다.
- flow control은 receiver가 받을 수 있는 양에 따라 정해지기 때문에 receiver에 의해 좌우된다고 볼 수 있다.
- receiver는 TCP 헤더에 있는 recv buf size라는 필드에 자신이 받을 수 있는 양을 담아서 sender에게 보낸다.
- 문제 상황
- 만약 recv buf가 꽉 차 있고 receiver는 데이터를 받기만 하는 프로세스라고 가정해보자
- recv buf가 가득 찼다는 것을 헤더의 buf size를 통해 확인한 sender는 멈추게 된다.
- 그때 receiver의 buffer에 빈 공간이 생겼을 때 이를 sender가 어떻게 확인 할 수 있을까?
- 이런 문제를 해결하기 위해 TCP는 recv buf가 가득차 있는 경우 data를 가지지 않은 segment를 reciver에게 주기적으로 보내 recv buf size를 확인한다.
TCP 3-way handshake
- TCP에서 두 프로세스 간 통신을 위한 각각 send, recv buffer를 만들고, sequence number를 확인하는 등 통신 전 두 프로세스 간의 연결이 필요하다. 이때 TCP는 3-way handshake를 통해 연결을 수립한다.
- 먼저 클라이언트가 서버에게 SYN 패킷을 날림 -> TCP 헤더 중 SYN 비트를 1로 만들고, 첫 seq 넘버를 보냄
- SYN 패킷을 받은 서버는 클라이언트와 똑같이 SYN 패킷을 날리고 SYN 패킷을 받았다는 ACK를 날림 -> 패킷 헤더는 SYN = 1, ACK=1인 상태, ACK의 번호는 넘오온 seq 넘버 + 1
- 마지막으로 클라이언트는 SYNACK를 받았다는 의미의 ACK를 서버에게 보냄 -> ACK=1, ACK 넘버는 서버가 보낸 seq + 1
- 서버가 ACK를 받으면 연결
- 마지막 클라이언트가 보내는 ACK에는 데이터 추가해서 보내기 가능(나머지는 불가능)
TCP 4-way handshake
- 클라이언트가 자신은 더 이상 보낼 데이터가 없다는 패킷인 FIN을 보냄
- 서버는 FIN을 잘 받았다는 ACK를 보냄
- 서버가 끝까지 동작하다(데이터를 보내는 등) 더 이상 보낼 데이터가 없다면 FIN을 보냄. 서버는 이때 할당된 리소스를 해제
- 클라이언트가 FIN을 잘 받았다는 ACK를 보냄
- 만약 클라이언트가 보낸 마지막 ACK가 유실될 가능성이 있기 때문에 클라이언트는 timed wait라는 시간만큼 기다림 다음 리소스를 해제함
- 만약 이 기다리는 시간이 없다면 ACK가 유실될 경우 서버는 계속해서 FIN을 보내게 됨
- 연결 끝
TCP Congestion Control 개요
- Congestion control은 네트워크 상황에 맞게 데이터를 보내는 방식이다.
- sender가 데이터를 보낼 때 flow control를 통해 receiver가 받을 수 있을만큼의 데이터만 보낸다고 했다.
- 그럼 네트워크 상황을 체킹하는 Congestion control과 recv buf를 체킹하는 flow control 중 어느 것을 우선으로 생각해야할까?
- 정답은 더 작은 쪽이다.
- 하지만 flow control의 경우 명확한 수치로 알 수 있지만 네트워크 상황의 경우 명확한 수치로 알아내기 힘들다(변동성이 큼, 네트워크는 public)
- 또한 TCP의 특성 상 네트워크가 막힐 경우 더 상황을 악화시킨다.
- 네트워크에 패킷들이 많아지는 경우 로스가 발생한다.
- 그럼 TCP는 로스가 발생한 패킷을 다시 재전송한다. 이 때문에 점점 패킷이 줄어들지는 않고 더 늘어나게 된다.
- 이런 문제를 해결하기 위해 TCP는 네트워크 상황에 따라 보내는 데이터 양을 조절하는 식으로 동작한다.
- 그럼 이 네트워크 상황을 어떤 식으로 알 수 있을까?
TCP Congestion Control
TCP는 상대방 프로세스이 보내는 피드백(ACK)을 통해 네트워크 상황을 유추한다.
그렇기 때문에 TCP는 처음에는 아주 조금의 패킷만 전송하고 ACK들이 잘 온다면 점차 늘려가는 식으로 동작한다.
Congestion Control은 이런 메커니즘을 구현하기 위해 3단계로 이루어져있다.
- Slow Start : 전송 단위를 1부터 시작해 Threshold에 도달하기 전까지 2배씩 증가시킴(1-2-4-8…), 1씩 증가시키면 실제 네트워크 전송량 한계까지 가는데 너무 오래걸림
- Additive increase : Threshold에 도달한 뒤부터는 1씩 증가시킴
- Multiplicative decrease : 패킷 로스가 발생하면 전송단위를 절반으로 줄임 -> 다시 Additive increase 과정 시작
- 전송 단위 = MSS(Maximum segment size) : sender가 ACK와 상관없이 보내는 양(= window size) / 보통 1MSS는 500byte이다
TCP Tahoe VS TCP Reno
- Congestion Control의 1번째 버전인 Tahoe는 진행하다 로스가 발생하면 Threshold 사이지를 발생한 지점의 window size의 절반으로 설정하고 slow start부터 다시 시작한다.
- Tahoe는 최근에는 사용하지 않는다. 그 이유는 뭘까?
- 우리는 패킷이 로스된 상황에 대해 다시 생각해볼 필요가 있다.
- 로스된 상황은 두가지 경우가 존재한다. 1) Timeout, 2) 3 dup ACK
- 이런 2가지 경우는 로스가 발생한 사실은 같지만 네트워크 상황을 살펴보면 다르다.
- Timeout의 경우 로스가 발생한 시점 이후로 모든 패킷이 로스가 된 상황이다.
- 3 dup ACK는 특정 패킷만 로스가 된 상황이다.
- 이렇듯 3 dup ACK보다 Timeout이 더 심각한 문제가 있다는 것을 알 수 있다.
- 그렇기 때문에 2가지 경우를 각각 다르게 행동해야된다.
- 그래서 최근에 나온 Reno는 3 dup ACK의 경우 Threshold와 window size를 절반으로 줄인 뒤 그 size부터 다시 1씩 증가시킨다.
- Timeout이 발생한 경우는 Tahoe와 마찬가지로 slow start부터 시작한다.
TCP fair?
TCP에서 Congestion Control를 통해 네트워크 전송량을 조절한다고 했다.
근데 실제 네트워크에서는 하나의 큰 네트워크를 여러 사람들이 공유하게 되는데 이때 이 네트워크를 모두가 fair하게 사용하게 되는걸까? Congestion Control은 각각 자신만의 사용량을 조절하는건데 과연 fair할까? 에 대한 의문이 있을 수 있다.
결론은 fair하다.
위 그림과 같이 각자 Congestion Control를 하다보면 가운데 fair한 지점에 수렴하게 된다.
하지만 주의할 점은 이 Congestion Control은 TCP Connection끼리 네트워크 사용량을 조절하는거다.
결국 한 사용자가 2개의 TCP를 열면 이 사용자가 네트워크 사용을 많이 하게 된다. 주의하자
출처)
[컴퓨터네트워크 - 한양대학교 | KOCW 공개 강의](http://www.kocw.net/home/search/kemView.do?kemId=1169634) |