전공 수업 내용 정리 (문제 발생시 비공개합니다.)
TCP segment
sending buffer
보내는 버퍼는 버퍼 안에 보내 복사본을 그대로 가지고 있음(재전송이 필요할까봐)
recieving buffer
패킷 번호 매기기
각 연결에서 전송되는 데이터의 바이트는 TCP에 의해 번호가 매겨짐.
번호 매기기는 임의로 생성된 번호로 시작
Cumulative ACK
현재 사용하는 TCP 방식
다음 번에 받고 싶은 번호를 보냄
TCP segment 형식
- 옵션마다 헤더 길이가 달라질 수 있음
- → 이를 표현하기 위해 HLEN 사용(보낼 때 %4, 받을 때 *4)
- IP(발신지 16 + 목적지 16) + seq(32) + ack(32) + 헤더(4) + 예약(6)
- 제어 필드(6) + 윈도우 크기(16) + 검사합(16) + 긴급 포인터(16)
- = 총 20Byte
ACK: 1로 세팅하여 유효함을 나타냄
SYN: 연결요청 패킷
FIN: 연결종료 요청 패킷
연결지향적 | 비연결지향 |
---|---|
중앙제어 | 분산제어 |
TCP | UDP |
논리적 | 물리적 |
실시간으로 서로 연결 가능 | 경로를 연결해놓고 출발 |
ex. 전화 | ex. 엽서 |
TCP cheeksum
- TCP 데이터그램에 추가되는 의사 헤더
- 아이피주소가 들어간 가상 헤더 만들어서 cheeksum에 실어보냄
캡슐화(Encapsulation)
- 애플리케이션 레이어 데이터 - TCP에 캡술화
- 네트워크 레이어 - IP 데이터그램에 캡슐화
- 데이터링크 레이어 - 프레임에 캡슐화 + CRC 꼬리(에러 탐지용)
TCP 연결
연결지향 프로토콜: 발신지와 목적지 간에 가상경로 설정
3단계 핸드셰이크
- 연결 설정
- SYN 세그먼트: 데이터 전송 안 함, 시퀀스 번호 1개 소비
- SYN + ACK 세그먼트: 전송 안 함, 시퀀스 번호 1개 소비
- ACK 세그먼트: 데이터 전달하지 않는 경우, 시퀀스 번호 소비 X
bind: 아이피 주소, 포트 넘버 부여
listen 일반소켓을 서버소켓으로 바꿔줌. syn → syn ack [파일디스크립터]
block: 어떤 일이 완료되기 전까지 코드가 멈춤. 마지막 ack이 올때 리턴 *ex. cin 입력*
connect: 내부적으로 syn 패킷 전송
accept: ack이 오면 리턴
- 데이터 전송
- 연결 종료
- FIN 세그먼트: 데이터를 전송하지 않는 경우, 시퀀스 번호 1개 소비
- FIN + ACK 세그먼트: 데이터를 전송하지 않는 경우, 시퀀스 번호 1개 소비메모리가 한정적이므로 관리해줘야함
하드웨어적으로 죽은 경우 제외하고, 종료시 FIN 전달
- 반대는 read
- Application에서 TCP = write 함수
- 서버가 종료되면 연결도 종료. FIN 패킷을 보내면 종료 요청
- 연결이 생길때마다 버퍼 생성해 따로 관리
- 반-닫기(Half-Close)
상태 천이 다이어그램
CLOSED |
미연결 |
---|---|
LISTEN |
SYN을 기다리는 상태 |
SYN-SENT |
SYN 보냄. ACK을 기다리는 상태 |
SYN-RCVD |
SYN+ACK 보냄. ACK을 기다리는 상태 |
ESTABLISHED |
연결 상태 (데이터 전송 진행 중) |
FIN-WAIT-1 |
첫번째 FIN 보냄 (ACK 기다리는 상태) |
FIN-WAIT-2 |
첫번째 FIN 받음. (두번째 FIN 기다리는 상태) |
CLOSE-WAIT |
첫번째 FIN 받고 ACK 보냄 (종료 기다리는 상태) |
TIME-WAIT |
두번째 FIN 받고 ACK 보냄 (타임아웃 기다리는 상태) |
LAST-ACK |
두번째 FIN 보냄 (ACK 기다리는 상태) |
CLOSING |
양 쪽 모두 동시에 종료 결정 |
for (i = 0; i < 5; i++) {
// 소켓 생성
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
if (clnt_sock == -1)
error_handling("accept() error");
else
printf("Connected client %d \n"
, i + 1);
while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
write(clnt_sock, message, str_len);
close(clnt_sock);
}
버퍼 안에 최소 1바이트 이상 존재해야 리턴 가능
소켓 리시빙 버퍼에 있는 값을 어레이로 → 리턴값이 0이 아니라면 write 함수를 통해 상대방에게 반사
리턴값이 0인 경우? 상대방이 FIN을 보낼 때 (연결 종료 시)
TCP 기반 서버, 클라이언트의 함수 호출 관계
문제가 발생하는 경우? 동일 포트가 동일 서버에 다시 재접속
해결방법: 같은 포트번호를 못 쓰게 해야 함
time-wait 상태: 1~2분 대기 동안 #3456 프로세스가 실행중임을 알림
→ 내부적으로 살아있으면 다음 포트번호인 #3457 사용하도록 보내줌
fin은 왔는데 ack이 안오면? 서버에서 재전송 필요 클라이언트: 정해진 시간 내에 못받을 경우, 안 왔다고 간주
=> 이를 방지하기 위해 time-wait 상태 필요.(1~2분 대기)
read() 호출 시 리시빙 버퍼가 비워지고, 상대방에게 정보가 전달 됨.
공간이 비었을때, ack이 와야만 write 함수 호출
버퍼가 비는 만큼 가져감
공간 부족하면 block 됨.
윈도우 사이즈(rwnd)
패킷 보내고, 이에 대한 응답을 받기 전까지 한꺼번에 보낼 수 있는 최대 크기
ex. 서울에서 부산까지 10시간 동안 얼마나 보낼지
최초: 빈 공간의 크기 → 가변
→ ack을 보낼 때마다 받을 수 있는 빈 공간이 얼마인지 알려줘야 함.
sending buffer
- Bytes that are acknowledged
- Receiver에게서 데이터를 받았다는 신호(ACK)를 받은 승인된 bytes
- buffer에서 제거 가능
- Outstanding bytes
- 상대방에게 전송했지만 아직 승인받지 않은(ACK이 도착하지 않은) bytes
- 오류 가능성이 존재하므로 ACK을 받을 때까지 보관하고 있어야 하는 데이터
- Bytes that can be sent
- 아직 보내지 않은 bytes
- 사용가능한 window
- Send window size
- Receiver가 알려준 size(rwnd)
- Bytes that cannot be sent until the right edge moves to the right
- 오른쪽 edge가 오른쪽으로 이동할 때까지 보낼 수 없는 bytes
- Outstanding bytes의 ACK이 도착해 버퍼에서 해당 데이터를 삭제하면, 데이터가 없는 빈 공간은 버리고 윈도우의 범위를 오른쪽으로 이동시킨다.
recieving buffer
- Bytes that have already pulled by the process
- 프로세스에서 이미 밀려난, 즉 read를 끝낸 데이터
- Bytes received, and acknowledged waiting to be consumed by process
- 프로세스에서 consume(소비) 대기 중인 수신, 승인된 바이트
- consume: process가 데이터를 호출해서 application으로 가지고 올라가는 것
- 데이터 받았지만 아직 프로세스가 read를 안 한 것
- Bytes that can be received from sender
- 받을 수 있는 비어있는 공간
- Receive window size(rwnd): 한번에 받을 수 있는 데이터의 양 = window size
흐름 제어(Flow control)
생산자가 데이터를 만드는 속도와 소비자가 데이터를 사용하는 속도의 균형을 맞추는 것.
남은 공간의 크기를 상대방에게 알려 줌.
기준: reciever appllication의 속도
슬라이딩 윈도우
시간에 관계없이 각 패킷에 번호를 붙여 연속적으로 전송.
항상 rwnd보다 작거나 같도록 설계 → 버퍼가 넘치지 않게.
흐름제어 이유?
stop & wait 방식의 비효율 (일정 시간 동안 한 개의 프레임만 전송 가능)
⇒ 효율성 및 신뢰성 보장
흐름 제어의 예시
ACK 세그먼트는 시퀀스 넘버를 소비하지 않으며, 확인 응답 되지도 않는다.
즉, ACK에 대한 ACK은 존재하지 않는다!
데이터는 순서에 어긋나게 도착할 수 있고, 수신 TCP에서 일시적으로 보관할 수 있다.
그러나 프로세스로는 순서에 어긋나게 전달되지 않도록 한다.
즉, 제대로 된 데이터만 올려 보낸다!
오류제어
- 양단간 쌍방향 소통(Normal operation)
Rule 1
데이터를 받았을 때 ACK을 보내야 하는데 ACK을 바로 보내는 게 아니라 Buffer에 보낼 데이터가 있는지 확인하고 보낼 데이터가 있으면 같이 보낸다.
Rule 2
Sender는 패킷을 송신하고 나면 일정 시간 동안 대기한다. 위의 그림에서는 지연 시간을 50ms로 설정하였다. 50ms까지 기다려도 Buffer에 보낼 데이터가 없을 시 데이터 없이 ACK만 전송한다.
Rule 3
timer를 켜놓고 기다리는 중에 패킷이 하나 더 도착하면 더 이상 안 기다리고 바로 ACK을 전송한다.
(=데이터가 없는 상황에 패킷 2개 들어오면 2개당 ACK 적어도 하나는 보냄.)
- Ack 전송 시 Ack Field를 1로 셋팅하면 상대측에서 Ack을 의미있는 정보로 받아들이고, 0으로 셋팅 시 상대는 쓰여진 Ack번호를 무시한다.
- 만약 그림의 맨 아래 Ack:7001 패킷이 Server에게 도착하기 이전 유실되면, Sever 입장에서는 위의 Seq: 5001-6000 / Ack: 1401패킷과 Seq: 6001-7000 / Ack: 1401패킷 두 개 모두 Client가 못 받았다고 생각.
손실 세그먼트(lost segment)
- Rule 4:타임 아웃: 정해진 시간 안에 응답이 오지 않은 경우, 재전송받은 데이터들을 저장해두지만, 빈 공간이 다 채워지기 전까지는 프로세스에게 전달하지 않는다.
- 수신자는 수신하고자 하는 다음 바이트를 나타내는 확인 응답을 송신자에게 즉시 전송한다.
- 예외 상황이 발생(out of drder)하면 딜레이 없이 바로 확인 응답을 보낸다.
- Rule 5: 재전송을 받은 후 적절히 확인 응답 처리 시킨다.
💡 tcp는 순서에 맞는 데이터만 프로세스에게 전송한다.
cf. 신뢰성을 보장하지 않아도 되는 애플리케이션의 예시: 라이브 스트리밍
빠른 재전송(fast retransmission)
전송 형태는 서버와 클라이언트가 얼마나 가까이 있는지에 따라 달라짐
ack이 오지 않더라도 윈도우 사이즈가 허락하는 한도 내에서는 계속 전송 가능.
3개의 중복된 ack이 도착하면 패킷이 없어졌다고 간주하고 타임아웃에 가기 전에, 빠르게 손실된 해당 패킷을 재전송함.
중복된 패킷이 있더라도 빠른 처리를 위해 일단 그전까지 보냈던 것들도 한꺼번에 전송함.
확인 응답의 손실(Lost acknowledgment)
확인 응답이 손실되는 경우, 적절히 처리되지 못하면 교착상태(dead lock)에 빠질 수 있다.
데드락: ack rwnd = 0 받으면 stop, 다시 보낸 ack rwnd값이 사라지면 서로 기다리는 시간
→ 해결 방법: 영속 타이머(Persistence Timer) 사용
타이머가 만료되면 데이터 재전송. 1 바이트의 작은 probe 패킷 보냄.
ack 보내면 rwnd가 같이 오므로 아까 보낸 ack이 없어졌더라도 내가 보낸 probe 세그먼트에 의해 상대방의 상황을 알 수 있음.
혼잡 제어(congestion control)
혼잡(Congestion)
인풋 > 아웃풋인 경우, 라우터에서 발생
순간적인 리시빙 속도가 100보다 커도 흡수하지만,
라우터는 버퍼가 꽉 차면 패킷을 버릴 수 밖에 없음.
중앙 제어가 없으므로 내가 보내는 데이터의 양을 모름 → 혼잡이 발생할 수밖에 없는 구조
best - effort: 최선은 다했으나 보장하지 않음. like 분산제어 (vs 중앙제어는 보장)
딜레이(delay)
딜레이란? 데이터 속도의 지연
capacity에 가깝게 커지면 딜레이 급격히 증가.
처리량(throughtput)
처리량이란? 파일을 하나 받는데 드는 시간 당 처리량. ( ≠ recieving rate)
혼잡 발생 시 패킷이 손실되므로 재전송하면서 시간이 늘어남 → 처리량 감소
윈도우 사이즈
rwnd: Byte 단위, 리시빙 버퍼가 받을 수 있는 크기
cwnd: 패킷 단위 (구현은 바이트로), 최솟값 1, 0으로 떨어지면 시작 불가하므로 sender가 stop 됨.
window size: 위 둘의 minimum, 서로 독립적임
혼잡 회피 구간(addictive increase)
4개 오면 4개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
5개 오면 5개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
6개 오면 6개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
**RTT(왕복 시간)**마다 1개 씩 증가
Slow start (exponential increase = SS)
1개가 오면 2개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
2개가 오면 4개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
4개가 오면 8개 보냄 → 손실 없음 → 혼잡 없다고 간주하고 하나 더 보냄
2배씩 증가하지만 처음에는 여러개 보낼 수 없어서 느리다. threshold까지만 증가.
cwnd
threshold 전까지는 AI, 만난 후에는 SS방식 사용.
보내는 양은 RTT 마다 증가함.
- 패킷 손실,즉 혼잡이 발생하면?ex. 보내는 속도가 512Mbps였다면 256Mbps으로 줄임.
- 보내는 양의 크기(cwnd)를 반으로 줄인다!
- 타임아웃이 발생하면?이때 threshold는 타임 아웃이 발생했을 때의 절반으로 줄여야 함.
- 심각한 상황이므로 다시 최솟값인 1로 초기화 → 무조건 SS로 감.
- cf. 만약 흐름제어라면?= 리시빙버퍼가 항상 비어있다면 512Mbps=> RTT 1초가 1초 일때 512MBits만큼 한번에 보낼 수 있음
TCP 혼잡 제어 정책 요약
'CS > Network' 카테고리의 다른 글
[네트워크] 소켓 프로그래밍: 프로토콜, 클라이언트와 서버의 함수호출 (0) | 2025.01.29 |
---|---|
[네트워크] 소켓 프로그래밍 개요 (0) | 2025.01.29 |
[네트워크] TCP 타이머, 옵션 (0) | 2025.01.29 |
[컴퓨터 네트워크] 3가지 주소 지정; 물리, 논리, 포트 (0) | 2023.07.01 |
[컴퓨터 네트워크] The OSI Model & Layer 7 기초 (0) | 2023.06.08 |