인터넷 프로토콜 스택
application | support network applications. IMAP, SMTP, HTTP |
transport | process-process data transfer. TCP, UDP |
network | route of datagrams from source to destination. IP, routing protocols |
link | data transfer between neighboring network elements. Ethernet. 802.11(WiFi), PPP |
physical | bits on the wire |
application, transport, network layer -> communication layers
transport layer의 역할 : 패킷 스위칭을 사용하는 network layer에서는 보존성을 장담해주지 않음. 패킷이 중간에 손실될 수도, 순서에 맞지 않게 도착할 수도 있다는 뜻임. 중간에 잃어버린 패킷의 재전송을 요청하고, 도착한 패킷을 순서에 맞게 재정렬하는게 transport layer의 TCP가 하는 주된 일임.
TCP (Transmission Conrtol Protocol)
- TCP는 거의 대부분의 어플리케이션에서 사용되는 transport layer protocol 이다.
- TCP는 어플리케이션 사이의 데이터 전송을 위해 신뢰할 수 있는 연결(reliable connection)을 제공한다.
- TCP 세그먼트는 기본 프로토콜 유닛이다.
특징
- 일대일 양방향 통신
- 신뢰할 수 있는 연결(보낸 것과 받은 것이 같음 = 패킷 손실 없이 순서에 맞게 전송)을 제공
- 혼잡 제어(congestion control), 흐름 제어(flow control) 제공
- cumulative ACKs (누적 확인 응답. n번째 데이터 잘 받았음을 알려줌)
- pipelining (연속으로 데이터 보냄 -> 흐름 제어 필요)
- 통신하고자 하는 네트워크끼리 연결(handshaking)이 필요함 (연결을 맺어야 버퍼를 생성할 수 있고 버퍼가 있어야 흐름 제어를 할 수 있음)
- 단, 보안과 지연 방지는 보장해주지 않음!
1. 연결
데이터를 교환하기 전에 연결하는 것에 대한 동의, 연결할 때 필요한 파라매터를 결정 짓는 (X번을 첫 시퀀스 번호로 잡고 전송하겠다) 과정..
하지만 2-way handshake는 다음과 같은 문제 상황이 발생할 수 있다.
- 클라이언트가 서버에 연결 확립을 위한 SYN 비트를 전송하고, 그 패킷을 받은 서버는 ACK 비트를 전송한다. 그러면 서버 입장에서는 연결이 완료되었기 때문에 서로 연결되었다고 생각한다.
- 그러나 서버의 ACK이 중간에 손실되거나 너무 지연되어 클라이언트 쪽에서 타임아웃이 일어날 경우, 클라이언트는 다시 연결을 위한 SYN 비트를 전송한다.
- 이때 지연되었던 서버의 ACK이 도착한다면 클라이언트는 아까 자신이 두번째로 보낸 연결 요청에 대한 응답이라 생각하고 연결이 확립되었다고 생각할 것이다.
- 그런데 서버는 다시 전달받은 2의 연결 요쳥에 의해 (클라이언트가 존재하지 않는) 반쪽짜리 연결을 만들어내게 된다.
그래서 TCP는 서로가 데이터를 보낼 때 연결된 것을 확실하게 보장하고 인지할 수 있도록 3-way handshake를 사용한다.
- 연결 요청 단계 : 클라이언트는 서버에게 특별한 TCP 세그먼트를 송신한다. (SYN bit를 켜고 x번을 첫번째 시퀀스 번호로 가지겠다는 내용을 담은 패킷을 보냄)
- 연결 승인 단계 : 1과 동일하게 서버(혹은 다른 클라이언트) 역시 SYN bit을 켜고 첫번째 시퀀스 번호 정보를 담은 패킷을 전송한다. 동시에 1의 클라이언트가 보낸 패킷을 잘 받았음을 ACK으로 알린다.
- ACK 단계 : 2에서 보낸 패킷을 잘 받았음을 ACK으로 알린다.
위의 세 과정이 끝나면 연결이 완료된 것이고 데이터를 전송할 수 있게 된다.
2. 통신
- 흐름 제어 (Flow Control)
TCP 통신하는 두 네트워크에 각각 receive buffer, send buffer가 있을 때, 송신측은 send buffer에 보낼 메세지를 계속 담고 있다가 한번에 보냄. 보낸 뒤에도 재전송할 수도 있으니 삭제는 하지 않음. 수신측에서는 receive buffer에 담긴 메세지를 읽어들이고 버퍼를 비움. 모두 제대로 왔다면 ACK을 보냄. 그 ACK이 송신측에 잘 전달되면 그제서야 send buffer를 비움.
근데 송신측에서 데이터를 보내는 속도가 수신측에서 receive buffer을 비우는 것보다 빠르다면? 수신측의 receive buffer에오버플로우가 발생해 패킷이 손실될 것이다. 이를 방지하기 위한 것이 흐름 제어 기법이다.
어떻게 흐름을 제어하느냐? 수신측이 ACK 신호를 보낼 때 TCP 헤더에 버퍼의 여유 공간 크기 정보(rwnd)를 담아서 보내는 것이다. 그러면 송신측에서 그 rwnd 크기를 보고 아직 ACK 응답을 받지 않은 데이터들을 어느정도 속도/양으로 보낼지 계산할 수 있게 된다.
3. 통신 종료
- 4-way handshake
연결을 종료하기 위해서는 서버와 클라이언트 모두 각자의 소켓 연결 부분을 닫아야 한다.
자신이 더이상 보낼 데이터가 없다면 close() 함수를 사용하여 나는 보낼 데이터가 없음을 먼저 알린다 (FIN=1인 TCP 세그먼트가 보내짐). 상대는 일단 FIN 세그먼트에 대해 ACK 신호를 보내고, 만약 더 보낼 데이터가 더 있다면 마저 전송한다. 상대도 보낼 데이터가 없을 때 close() 함수로 알린다. FIN 세그먼트를 서로 교환하였을 때 통신은 완전히 종료된다.
사실 close 함수는 소켓의 입력 버퍼, 출력 버퍼를 모두 닫아버린다. 이 때의 문제는 일단 FIN의 응답인 ACK을 받을 수 없다는 것이고 두번째로 아직 상대에게 전송해야할 데이터가 남아있을 수도 있다는 것이다. close 함수를 사용하면 이 모든것을 수신할 수 없고 이는 리소스 낭비로 이어질 수 있다. (상대는 데이터를 보내도 ACK이 날아오지 않으니 패킷이 손실된 줄 알고 계속 보내게 된다)
따라서 실제로는 close 대신 shutdown 함수를 사용한다. shutdown 함수는 입출력 버퍼 중 하나만 닫을 수 있다. 이를 half close라고 하며, 이를 적용하면 아래와 같은 과정이 일어난다.
첫번째 호스트에서 출력 버퍼의 데이터를 모두 전송하는 동시에 shutdown 함수로 출력 버퍼를 닫는다. 두번째 호스트는 입력 버퍼에 쌓인 데이터를 읽고 전송할 데이터가 남아있다면 출력 버퍼에 담고 전송한다. 두번째 호스트는 입출력 버퍼 모두 닫아도 되기 때문에 close 함수를 사용한다. 다시 첫번째 호스트는 입력 버퍼에서 데이터를 읽고 shutdown 함수로 입력 버퍼까지 닫고 연결을 끝낸다.
* 서버와 클라이언트 통신 과정
server | client |
socket() - serv | socket() |
bind() | |
listen() | |
connect() | |
accept() | |
socket() - clnt | |
close() - clnt | close() |
close() - serv |
클라이언트의 소켓과 서버의 clnt 소켓으로 통신한다.
혼잡 제어 (Congestion Control)
혼잡 : 너무 많은 리소스에서 너무 많은 데이터를 너무 빨리 보냄.
혼잡 제어를 위해서는 저 셋 중 하나는 고쳐야 한다. (흐름 제어와의 차이점: 흐름 제어는 하나의 송신측이 하나의 수신측에게 너무 빨리 데이터를 보내는 상황이고, 혼잡 제어는 여러 송신측에서 많은 데이터를 빨리 보내는 상황이다.)
혼잡 제어는 네트워크에서 명시적으로 피드백을 주지 않는다. 그래서 혼잡 제어의 첫번째 단계는 혼잡하다는 것을 알아채는 것이다. 만약 데이터를 보냈는데 ACK이 안온다거나, RTT가 계속 증가하는 상황이라면 현재 네트워크가 혼잡함을 유추할 수 있다. 두번째로 혼잡한 상황을 해결해야 하는데 그 방법은 다음과 같다.
1. AIMD (Additive Increase Multiplicative Decreas: 천천히 linear하게 증가, 과감하게 절반으로 감소)
송신자들은 패킷 손실(= 혼잡 상황)이 일어나기 전까지 한번에 보내는 패킷 양을 linear하게 증가한다. 그러다 혼잡 상황이 발생하면 보내던 양의 절반으로 확 낮춰서 다시 전송한다.
좀 더 세부적으로 나누면, 패킷 손실이 났을 때 timeout인 경우에는 네트워크가 정말 혼잡한 상황이란 뜻이므로 절반이 아니라 1개부터 다시 전송한다. 만약 duplicate fast retransmit이라면 중간에 손실이 났지만 다른 데이터가 도착하긴 했다는 뜻이므로 반으로 줄여 전송한다.
2. RTT 계속 증가 => TCP-Vegas
데이터를 전송할 때마다 RTT를 계산하면서, 만약 RTT 값이 계속 증가한다면 전송하는 데이터 양을 줄인다.
TCP는 공평(Fair)한가?
공평의 목표? 만약 라우터에서 R의 대역폭으로 데이터를 전송했다면 그 라우터에 연결된 커넥션 수 N에 대해 각 커넥션에서 R/N의 대역폭을 사용할 수 있어야 한다.
결론: 시간이 지날 수록 점점 공평해진다.