사용자 도구

사이트 도구


kb:tcpglossary

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:tcpglossary [2014/11/08 13:02] (현재)
줄 1: 줄 1:
 +====== TCP Glossary ======
 +{{tcpglossary_transition.gif}}
 +
 +Tcp와 관련된 용어들 요약. TCP/IP Illustrated 요약? 튜토리얼이야 널렸으니,​ 비망록 정도로 기록해둬야겠다.
 +
 +
 +====== - Connection Establishment and Termination ======
 +===== - SYN, FIN, ACK =====
 +**연결 생성**
 +<​code>​
 +C => SYN          => S
 +C <= SYN with ACK <= S
 +C => ACK          => S
 +</​code>​
 +SYN 패킷(세그먼트)을 이용해 서로의 sequence number, window size 등을 알린다. 그 다음 서로 간에 SYN 패킷에 대한 ACK를 보내주면 땡이다. 클라이언트가 먼저 SYN을 보내는 것을 **active-open**을 수행한다고 하고, 서버가 이를 받아 다시 SYN을 보내는 것을 **passive-open**을 수행한다고 한다. Active-open과 passive-open이 동시에 이루어질 수도 있다. SYN이 서로 교차하면서 연결이 이루어진다는 말이다. 이를 **simultaneous-open**이라고 하는데, 딱히 어떤 상황에서 벌어지는 지는 의문이다.
 +
 +**연결 종료**
 +<​code>​
 +C => FIN => S
 +C <= ACK <= S
 +C <= FIN <= S
 +C => ACK => S
 +</​code>​
 +클라이언트에서 먼저 접속을 종료하는 경우다. FIN 패킷을 이용해 더 이상 해당하는 클라이언트에서는 송신할 데이터가 없음을 알린다. 그럼 서버에서는 이에 대한 ACK를 보내주고,​ 마찬가지로 서버에서도 더 이상 송신할 데이터가 없음을 알리기 위해 FIN을 날린다. 클라이언트가 이를 받아 ACK를 보내주면 접속 종료다.
 +
 +===== - Half Close =====
 +TCP는 full-duplex connection (송수신이 동시에 이루어지는 연결)을 가지기 때문에, 데이터 송수신시 다른 하나의 채널(?​)에는 영향을 받지 않는다. 그렇기 때문에 완벽한(?​) 연결 종료를 위해서는 2개의 채널 모두를 독립적으로 닫아야 한다.  ​
 +
 +송수신이 완전히 따로 이루어지기 때문에, FIN을 받은 쪽에서는 그 방향으로 더 이상 수신을 받지 못할 뿐, 이쪽에서 송신을 할 수는 있다. TCP의 이런 특성을 **half-close**라고 한다. 먼저 FIN을 보내는 쪽을 **active-close**를 수행한다고 하고, 나중에 FIN을 보내는 쪽을 **passive-close**를 수행한다고 한다.
 +
 +이러한 half-close 기능을 활용하는 프로그램으로서는 rsh(다른 컴퓨터에 접속해 특정 명령을 수행한 후, 그 결과를 클라이언트에 출력하는 프로그램)가 대표적이다.
 +
 +===== - Maximum Segment Size =====
 +MSS는 송수신할 수 있는 최대 세그먼트 크기를 말한다. 최초 연결시 SYN 패킷에다 이 크기를 서로에게 "​통보"​하게 된다. 이 크기는 일반적으로 해당 컴퓨터에 물려있는 네트워크 카드의 MTU(Maximum Transmission Unit) 크기와 비례한다. MSS는 일반적으로 세그먼트의 단편화가 일어나지 않는 한, 클수록 좋다고 볼 수 있으나, 반드시 그런 것만은 아니다. (Nagle Algorithm 부분 참고) 또한 클라이언트나 서버 중에서 어느 한쪽에서 큰 MSS를 통보하더라도,​ 다른 한쪽의 MSS가 작으면 어차피 그쪽의 MSS에 맞춰 통신이 일어나게 된다.
 +
 +클라이언트와 서버 모두가 같은 크기의 MSS를 가진다고 해도, 둘이 통신하기 위해 거치는 노드 중의 하나가 이보다 작은 MSS를 가진다면,​ 결국 세그먼트 단편화가 일어나고,​ 송수신 성능이 떨어지게 된다. 이를 방지하기 위해서는 중간에 거치는 모든 노드에 대한 MTU의 최소값이 필요하다. 이를 **path MTU discovery**라고 한다. 아직까지는 일반적인 기능은 아닌 모양이다.
 +
 +===== - 2MSL State (TIME_WAIT) =====
 +MSL(Maximum Segment Lifetime)이란 이름 그대로, 세그먼트가 네트웍 상에서 돌아다닐 수 있는 최대 시간이며,​ IP 데이터그램의 TTL과 비슷하다고 보면 된다. 다만 TTL은 시간이 아니라, 홉(노드)을 기준으로 하는데, 뭐 상관없다. 중요한 것은 구현마다 틀리긴 하지만 MSL 값은 상수라는 것이다.
 +
 +Active-close를 수행한 경우, 해당 소켓 페어는 MSL 시간의 2배만큼 TIME_WAIT 상태로 머물러야 한다. ACK가 하나 드랍되더라도 다시 받을 수 있는 시간이라고 하는데, 한번 더 드랍되면 어떻게 되는 거지? 어쨌든 TIME_WAIT 상태에 있는 소켓은 사용할 수 없다. TIME_WAIT 상태에 있는 포트로 날아오는 패킷은 모두 버려진다.
 +
 +TIME_WAIT 상태가 필요한 이유는, 임의의 소켓 페어 접속이 종료된 이후, 똑같은(양쪽의 포트 번호가 같은...) 소켓 페어가 다시 생성되어 패킷이 왔다갔다할 때, 이전 연결의 지연된 패킷을 새로운 연결의 패킷으로 오인하지 않도록 하기 위해서이다. 그러므로 TIME_WAIT 상태에 있는 소켓 페어는 2MSL만큼의 시간이 지나기 전에는 절대로 재사용할 수 없다.
 +
 +서버에서 SO_REUSEADDR 옵션을 이용해 포트를 재사용할 수 있는 것은 클라이언트가 사용하는 포트가 이전 연결과는 틀리기 때문이다. 소켓 페어의 유일성을 보장하는 것은 어느 한쪽의 주소:​포트가 아니라 양쪽 모두의 주소:​포트다.
 +
 +클라이언트와 서버의 역할을 서로 바꾸는 경우 TIME_WAIT 상태에 있는 소켓 페어도 사용할 수 있다. 클라이언트와 서버의 역할을 바꾼다는 말은, 클라이언트 컴퓨터에서 이전 연결에 썼던 자동으로 바인딩된(ephemeral,​ 한글로 뭐지...) 포트에다 서버를 띄우고, 서버 컴퓨터에서는 반대로 서버 바인딩할 때 썼던 포트를 클라이언트 프로그램 띄우는데 쓴다는 말이다. 클라이언트와 서버의 역할이 바뀌기는 했지만, 소켓 페어는 분명히 TIME_WAIT 상태에 있는데도 불구하고,​ 연결이 성립된다. Sequence number가 이전 연결에서 쓰던 것보다 새로운 것이라면 연결을 허락한다,​ 어쩌구저쩌구 있는데, 이렇게 쓸 일은 아마도 없을 것이므로 가볍게 생까주면 되겠다.
 +
 +Quite time? 아 적기 귀찮다.
 +
 +===== - FIN_WAIT_2 State =====
 +Active-close를 수행한 쪽에서 FIN을 보내고, 상대방이 FIN에 대한 ACK를 보내온 상태가 FIN_WAIT_2 상태다. 이 상태에서 상대방으로부터 FIN을 받고, ACK를 보내야 연결이 종료된다. 그렇다면 상대방이 FIN을 보내지 않으면 어떻게 되는가? 아무 것도 하지 않으면 영원이 이 상태에 머무르게 될 것이다. 이를 방지하기 위해서 구현마다 틀리기는 하지만, 일반적으로 타이머를 이용한다. 뭐 알아서 잘 하겠지.
 +
 +===== - Abortive Release =====
 +일반적으로 RST 패킷은, 수신한 세그먼트가 정상적인 세그먼트가 아닐 경우, 이를 상대방에게 알리기 위해 사용된다. SYN이 도착했는데,​ 해당 포트에 리스닝 중인 서버가 없는 것과 같은 경우가 대표적인 예라고 할 수 있다.
 +
 +위에서 언급한 4개의 패킷이(FIN,​ ACK, FIN, ACK) 왔다갔다하는 연결 종료를 **orderly release**(graceful close라고도 하지 않나?​)라고 한다. 이 경우 버퍼에 쌓여있는 모든 데이터를 송신한 다음, 비로소 연결 종료에 들어가게 된다. 이와는 달리 RST 패킷을 이용해 강제로 연결을 종료할 수 있다. 이를 **abortive release**라고 한다. 이 경우 버퍼에 쌓여있던 데이터는 모두 날아가게 된다.
 +
 +SO_LINGER 값을 0으로 주면 abortive release를 하게 된다. 즉 close() 함수 호출시 FIN 대신에 RST가 날아간다는 말이다.
 +
 +===== - Half Open =====
 +클라이언트나 서버 어느 한쪽에서 상대방에게 알리지 않고, 즉 FIN or RST를 보내지 않고, 연결을 종료한 경우를 **half-open** 상태라고 한다. FIN/RST를 보내지 않는다는 말은, 컴퓨터가 크래쉬되거나,​ 전원이 내려갔거나,​ 랜선을 뽑았다는 이야기다.
 +
 +TCP에는 하트비트식으로 일정 시간마다 왔다갔다하는 패킷이 없다. 한 쪽에서 데이터를 보내면, 다른 쪽에서 ACK를 보낼 뿐이다. 즉 어느 한쪽의 컴퓨터가 크래쉬되거나,​ 전원이 내려갔거나,​ 랜선이 뽑힌 시점에서,​ 서로에게 보낼 데이터가 없다면 연결이 종료된 것을 알 수 있는 방법이 없다는 말이다.
 +
 +결국 연결 종료를 알아내기 위해서는 애플리케이션 레벨에서 일정 간격으로 패킷을 쏴봐야 한다. 단 TCP 내부에서도 이를 알아내기 위해 뭔가 있기는 있는 모양이다. (keepalive timer 참고)
 +
 +====== - Interactive Data Flow ======
 +===== - Delayed Acknowledgments =====
 +임의의 세그먼트에 대한 ACK를 보낼 때, ACK만 달랑 보내는 것보다는 데이터와 함께 보내는 것이 트래픽을 줄이는데 도움이 된다. 이를 위해 대부분의 TCP 구현에서는 세그먼트를 수신했을 때 바로 ACK를 보내는 것이 아니라, 일정 시간(대부분 200ms) 후에 보내도록 구현되어 있다. 이 시간 후에 ACK를 보낼 때, 같이 보낼 데이터가 있기를 기대하는 것이다. 이를 **delayed acknowledgement** 또는 **piggyback**이라고 한다.
 +
 +===== - Nagle Algorithm =====
 +Nagle 알고리즘을 요약하자면 다음과 같다. ​
 +
 +* ACK를 받지 못한 데이터가 있는 상황에서는,​ 조그만 세그먼트들은 ACK를 받을 때까지 보낼 수 없다. ​
 +* 대신 이 조그만 세그먼트들은 하나로 합쳐져서 ACK가 도착했을 때 하나로 날아가게 된다.
 +
 +ACK가 빨리 올수록 데이터도 빨리 날아간다. 하지만 ACK가 바로바로 날아오지 않는 상황(즉 느린 WAN)에서는 알고리즘을 적용하지 않았을 때보다, 더 적은 수의 세그먼트들이 날아가게 된다. 즉 병목 현상을 줄이는 데 도움이 된다. 하지만 결국 데이터를 모아서 보내는 것이기 때문에 원래보다 반응이 느려진다. 빠른 반응이 필요한 애플리케이션의 경우, 이 알고리즘을 사용하지 않는 것이 도움이 될 수 있다. TCP_NODELAY 소켓 옵션을 이용해 알고리즘을 끌 수 있다.
 +
 +
 +====== - Bulk Data Flow ======
 +===== - Sliding Windows =====
 +<​code>​
 +                 ​|<​----------- offered window ---------->​|
 +                 ​| ​     (advertised by receiver) ​        |
 +                 ​| ​                                      |
 +                 ​| ​                          ​usable ​     |
 +                 ​| ​                  ​|<​----- window ---->|
 +                 ​| ​                  ​| ​                  |
 +                 ​+-------------------+-------------------+ ​                                
 + ​1 ​    ​2 ​    ​3 ​  ​| ​  ​4 ​    ​5 ​    ​6 ​  ​| ​  ​7 ​    ​8 ​    ​9 ​  ​| ​  ​10 ​    11
 +                 ​+-------------------+-------------------+ ​                                
 +  sent & ACKed      sent, not ACKed      can send ASAP
 +</​code>​
 +
 +===== - Slow Start =====
 +병목 현상을 줄이기 위해서 도입된 알고리즘으로서,​ sliding window와 비슷한 **congestion window**라는 것을 사용한다. ​
 +  ​
 +최초에 congestion window의 크기는 1이다. 이 윈도우의 크기는, 세그먼트를 송신할 때마다 1씩 줄어들고,​ 보낸 데이터에 대한 ACK를 받을 때마다 1씩 늘어난다. 즉 최초의 세그먼트를 송신한 다음, 그에 대한 ACK를 받으면 2가 된다. 그러면 2개의 세그먼트를 한꺼번에 보낼 수 있다. 다시 이들에 대한 ACK를 모두 받으면 4가 된다. 즉 지수승으로 늘어나게 된다.
 +  ​
 +이런 식으로 계속 윈도우의 크기를 늘려가다보면,​ 어느 한계점에 이르러 라우터가 세그먼트를 드랍하기 시작할 것이다. 이는 결국 타임아웃이나 중복된 ACK를 받는 현상으로 나타난다. 이런 현상이 나타나면 congestion window의 크기를 줄인다. (얼마나 줄이는가는 아래에서...)
 +  ​
 +
 +====== - Timeout and Retransmission ======
 +===== - Congestion Avoidance Algorithm =====
 +Congestion avoidance 알고리즘은 간단히 말해서, ACK를 받을 때마다 1/cwnd 크기만큼씩 늘리는 알고리즘이다. ​
 +
 +Slow start의 경우 ACK를 받을 때마다 cwnd(congestion window)의 크기를 1씩 늘리게 된다. 이렇게 하다보면,​ 위에서도 언급했듯이,​ 어느 시점에서 패킷 로스가 생기게 된다. 패킷 로스가 생기는 것에 대한 증거는 2가지가 있다. 첫번째는 타임아웃이 일어나는 것이고, 두번째는 수신 측에서 같은 ACK를 두번 받는 것이다. ​
 +
 +Congestion avoidance 알고리즘과 slow start 알고리즘은 별개의 것이지만,​ 보통 같이 구현된다. 이들을 구현하는 데에는 2가지 변수가 필요하다. 첫번째는 cwnd(congestion window), 그리고 두번째는 ssthresh(slow start threshold)다.
 +
 +   - 연결이 성립되면 cwnd의 크기는 1로, ssthresh의 크기는 65535 바이트로 설정한다.
 +   - TCP 송신 함수에서는 MIN(cwnd, 수신 측의 윈도우)보다 작은 크기의 데이터만 보낸다.
 +   - 병목 현상이 일어난 경우, 현재 윈도우 크기의 절반을 ssthresh 변수에다 대입한다. 또한 병목 현상이 타임아웃으로 나타난 경우, cwnd의 크기를 1로 줄인다.
 +   - ACK가 도착한 경우, cwnd의 크기를 다시 늘리기 시작한다. 여기서 cwnd, ssthresh의 두 변수의 값에 따라, slow start를 적용할 것인가, congestion avoidance를 적용할 것인가가 나뉘게 된다.
 +
 +현재 cwnd의 크기가 ssthresh보다 작거나 같은 경우, slow start 알고리즘을 적용한다. 반대의 경우 congestion avoidance 알고리즘을 적용한다. Slow start 알고리즘을 적용하는 경우, cwnd의 크기가 ssthresh 값보다 커지게 되면, congestion avoidance 알고리즘을 적용하게 된다. Congestion avoidance 알고리즘을 적용하게 되면, cwnd의 크기는 ACK를 받을 때마다 1씩 늘어나는 것이 아니라, 1/cwnd 크기만큼씩 늘어나게 된다.
 +
 +===== - Fast Retransmit and Fast Recovery Algorithms =====
 +===== - Repacketization =====
 +
 +
 +====== - Persist Timer ======
 +===== - Silly Window Syndrome =====
 +이 현상은 송/​수신측 모두에서 발생할 수 있다.
 +
 +  * 수신측 : 데이터를 수신하는 데 사용할 수 있는 버퍼(윈도우)가 생길 때마다, 이 버퍼의 크기가 좀 더 커지기를 기다리지 않고, 상대방에게 윈도우 크기를 통보한다.
 +  * 송신측 : 보낼 데이터가 모이기를 기다리지 않고, 조그만 데이터를 계속 보낸다.
 +
 +둘 중에 어느 쪽이 문제이던 간에 결과적으로 송수신자 모두, 버퍼는 충분함에도 불구하고,​ 조그만 크기의 세그먼트만을 서로 교환하게 된다. 이를 **silly window syndrom**이라고 한다. 이 현상을 피하기 위한 일반적인 구현 방법은 다음과 같다.
 +
 +  * 수신측 : 이용 가능한 버퍼의 크기가 전체 버퍼의 크기의 50% 이상이거나 MSS와 동일해질 때까지 윈도우 크기를 통보하지 않는다.
 +  * 송신측 : 다음 조건 중의 어느 하나라도 참이 되어야만 세그먼트를 전송한다.
 +    * 최대 크기(full-sized)의 세그먼트를 보낼 수 있다.
 +    * 수신측에서 통보했던 윈도우의 크기 중에 최대값이 있을 텐데, 이 최대값의 절반 이상의 크기를 가진 세그먼트를 바로 보낼 수 있다. ​
 +    * 현재 가지고 있는 데이터를 모두 보낼 수 있고, ACK를 기다리고 있지 않다. 또는 nagle 알고리즘이 꺼져 있다.
 +
 +
 +====== - Keepalive Timer ======
 +HalfOpen Half open 항에서 언급했듯이,​ 송수신할 데이터가 없는 상황에서 어느 한쪽이 비정상적으로 연결을 종료한 경우 TCP 레벨에서는 이를 알아낼 방법이 없다. 애플리케이션 레벨에서 일정 간격으로 패킷을 쏴봐야한다고 했는데, 이를 TCP 레벨에서 처리하기 위해 TCP에 도입된 것이 **keepalive timer**이다.
 +
 +RFC에는 없는 사항이며,​ 아래와 같은 문제점 때문에 RFC에 들어가지 않았다.
 +
 +  * 정상적인 연결이 몇번의 패킷 로스 때문에 끊어져버릴 수 있다.
 +  * 필요없는 대역폭을 잡아먹는다.
 +  * 패킷당 요금을 부과하는 인터넷 연결의 경우, 추가적으로 요금이 들어간다.
 +
 +하지만 대부분의 TCP 구현에는 들어가 있으며, SO_KEEPALIVE 옵션을 통해 조정할 수 있다.
 +
 +
 +====== - Futures and Performance ======
 +===== - Path MTU Discovery =====
 +송신 측 노드와 수신 측 노드 사이에 있는 모든 노드들의 MTU 값 중에 제일 작은 값을 찾아내어 이를 송신시 사용하는 기능이다. 가장 작는 MTU 값에 세그먼트 크기를 맞춤으로서 fragmentation을 최소화하는데 그 목적이 있다.
 +
 +윈도우즈에서 이를 활성화시키기 위해서는 레지스트리를 수정해야 하는 모양이다.
 +
 +{{tcpglossary_pathmtu.gif}}
 +
 +<​code>​
 +System Key: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
 +Value Name: EnablePMTUDiscovery
 +Data Type: REG_DWORD (DWORD Value)
 +Value Data: (0 = false, 1 = true)
 +</​code>​
 +
 +===== - Window Scale Option =====
 +TCP 윈도우 크기는 기본적으로 16비트 변수를 이용하기 때문에 65535바이트 이상으로 커질 수 없다. 이는 요즘 같은 시대에는 상당히 작은 값이다. 그래서 헤더에 있는 16비트 윈도우 크기는 무시하고,​ 따로 32비트의 윈도우 크기를 사용하는 기능을 window scale option이라고 한다. 32비트 변수를 어디다 두는지는 잘 모르겠지만,​ 아마 데이터 부분에다 두겠지. 어쨌든 이 옵션을 켜고, 송수신 측이 서로 SYN을 주고받으면,​ 좀 더 큰 크기의 윈도우를 사용할 수 있게 된다.
 +
 +===== - Timestamp =====
 +송신측에서 세그먼트마다 시간 값을 기록하고,​ 수신측에서는 송신측에서 보낸 시간 값을 ACK에 실어보내는 기능을 말한다. 이 시간 값을 이용해 송신 측에서 좀 더 정밀한 RTT 값을 측정할 수 있다.
 +
 +===== - Protection Against Wrapped Sequence Numbers =====
 +
 +
 +====== - 링크 ======
 +  * [[http://​www.microsoft.com/​mind/​1098/​tcpip/​tcpip.asp | The ABCs of TCP/IP]]
 +  * [[http://​www.ssfnet.org/​Exchange/​tcp/​tcpTutorialNotes.html | A TCP Tutorial]]
 +  * [[http://​www.faqs.org/​rfcs/​rfc1323.html | RFC1323 - TCP Extensions for High Performance]]
 +  * [[http://​www.faqs.org/​rfcs/​rfc2001.html | RFC2001 - TCP Slow Start, Congestion Avoidance, Fast Retransmit, and Fast Recovery Algorithms]]
 +  * [[http://​kb.pert.switch.ch/​cgi-bin/​twiki/​view/​PERTKB/​LargeTcpWindows | Large TCP Windows]]
 +
 +  * [[http://​www.microsoft.com/​korea/​technet/​network/​tcpip2k.asp | Microsoft Windows 2000 TCP/IP 구현 정보]]
 +  * [[http://​www.microsoft.com/​technet/​network/​evaluate/​technol/​tcpipfund/​tcpipfund.mspx | TCP/IP Fundamentals for Microsoft Windows]]
 +  * [[http://​www.microsoft.com/​technet/​prodtechnol/​windowsserver2003/​technologies/​networking/​tcpip03.mspx | Microsoft Windows Server 2003 TCP/IP Implementation Details]]
 +  * [[http://​support.microsoft.com/​kb/​224829/​en-us | Description of Windows 2000 and Windows Server 2003 TCP Features]]
 +  * [[http://​www.microsoft.com/​resources/​documentation/​Windows/​2000/​server/​reskit/​en-us/​regentry/​58800.asp | Windows 2000 TCP 1323 Options]]
  
kb/tcpglossary.txt · 마지막으로 수정됨: 2014/11/08 13:02 (바깥 편집)