상세 컨텐츠

본문 제목

네트워크 프로그래밍 - 패킷 전달[HLEN, Control Field(SYN, FIN, ACK), Checksum], handshake

Computer Science/Network

by 2021. 9. 22. 02:26

본문

반응형

Acknowledgment fild (ACK)

패킷을 받았을 때 어떻게 응답하는가?

패킷을 받으면 ack이라는 것을 응답의 신호로 보낸다. 이 ack은 acknowledgment field의 약자이다.

101번 패킷을 받으면 101번을 받았다는 ack을 보내고, 201번 패킷을 받으면 201번 ack을, 301번 패킷을 받으면 301번 ack을 받는 방식을 Selective ACK이라고 부른다. (TCP는 이 방식을 사용하지 않고 Cumulative ACK 방식을 사용한다.)

 

이번에는 Cumulative ack 방식에 대해서 보자

101번 패킷을 보내고 받으면 다음 번에 받고자 바는 byte번호를 응답으로 보낸다. 예를들어 101번 패킷을 통해 101~200까지의 byte를 받았다면, ack에 201번 byte를 보내달라는 의미로 전송한다. 이는 201번 byte 이전까지는 모두 정상적으로 전달받았음을 의미하는 것이기도 하다.
201~300까지의 byte를 받았다면, ack로 301번 byte를 보내달라는 신호를 응답으로 보낸다.

ack를 응답으로 받은 발신자가 301~400byte를 패킷으로 보내고, 패킷을 받은 쪽이 다시 ack를 보내는 방식이 cumulative ack 방식이다.

 

패킷의 헤더를 보도록 하자

헤더에는 Sequence number와 Acknowledgment number가 있다. 

 

송신자가 Data 앞에 Header를 붙여 보내면 받은쪽에서는 header만 보낸다. 이것이 일반적인 방식이다.

패킷을 보낼 때 Header의 Sequence number를 담아서 보내면 상대방이 응답헤더를 보낼 때 Header의 Acknowledgment number에 byte번호를 담아서 보낸다.

 

segment안에 acknowledgment field는 수신자가 다음번에 받고자 하는 next byte로 정의된다.

 

segment의 Data부분을 payload라고 부른다. 보통 패킷에서 header를 제거한 부분을 통칭하는 말이다.

TCP는 process to process 통신이므로 port 번호가 필요하다. 양방향이므로 Source port와 Destination port번호가 모두 필요하다.

 

HLEN

헤더의 길이는 20~60 byte까지 다양하다. 어디서부터 Data인지는 표시를 해주지 않으면 모르는데, 이에대한 정보로 header의 길이를 알려주는 것이 HLEN이다.

 HLEN은 4bit로 이루어져 있어 만약 header의 길이가 60byte일 경우 60을 저장하기 위해 6bit로 표현해야하는것이 일반적인 생각이지만, HLEN의 경우 4bit로 표현할 수 있는 최대값인 15에 4를 곱하면 60이라는 점을 이용하여 HLEN의 bit수에 4를 곱한만큼의 값으로 이용한다. 즉 4bit로 표현하는 HLEN의 값 * 4 만큼이 header의 실제 길이인 것이다. 

 header를 보낼 때는 (header의 byte수) / 4 를 HLEN에 저장하여 보내고, 받는 쪽에서는 (HLEN) * 4 만큼이 header의 크기임을 인지하는 것이다.

 

Control field

이번에는 header에서 위 그림과 같이 표시한 부분을 Control field라고 한다.

 

이와 같이 Control field의 각 자리는 1bit로 구성이 되어있다.

패킷을 전달하고 그 응답으로 ACK을 보낼 때 Acknowledgment number에 byte번호를 담아서 보내는데, 이 Acknowledgment number를 확인하라는 신호는 ACK에 담겨있다.

1bit인 ACK의 값이 1일 경우 Acknowledgment number를 확인하라는 의미이고, 0일 경우에는 Acknowledgment number의 값은 의미가 없게 된다.

즉 ACK의 값이 1인 경우에만 Acknowledgment number를 읽고 0일 경우에는 무시하는 것이다.

 

ACK, SYN, FIN 세 개의 Control field가 주로 쓰이는데, SYN은 연결 요청 패킷, FIN은 종료 요청 패킷이다.

 

Checksum

이번에는 Checksum에 대해서 알아보자. Checksum은 패킷 전송 중에 발생하는 오류를 체크하기 위해서 있다.

Header는 앞에 Pseudoheader를 붙이는데, 이 Pseudoheader에는 보내는 쪽의 IP주소와 받는 쪽의 IP주소가 담겨있고, 프로토콜과 TCP 전체 길이에 대한 정보가 담겨있다. 

 

 IP주소들은 모두 IP헤더에도 존재하나 더 확실하게 체크하기 위해 TCP헤더에도 붙여 checksum을 구하고, 보낼 때는 Pseudoheader없이 Header만 보낸다. 

 

먼저 IP 헤더에서 checksum을 구하는 방식을 먼저 보자

IP헤더에서 Checksum을 구하기 위해서는 먼저 header의 값들을 16bit씩 잘라서 2진수로 만든다.

그러고 나서 모두 더한 합의 보수값을 취한 것이 checksum이다. 이 값을 checksum field에 집어넣어 전달한다.

 

TCP헤더의 checksum도 IP헤더와 같은 방식이다. 16bit씩 자른 값을 모두 합한 값의 보수값을 담아서 보낸다. 

패킷 안에 담겨온 Checksum의 값과 받은 쪽에서 직접 구한 Checksum이 일치하는 경우 에러없이 정상적으로 전송완료된 것이다. 

Checksum으로는 어떤 비트에 오류가 있는 지 알 수는 없지만 오류발생의 여부만을 확인할 수 있다. 오류가 발생했다면 받은 패킷을 통째로 버린다. 

TCP에서의 checksum은 의무적이지만, UDP에서의 checksum은 선택적이다. 

Encapsulation

Encapsulation에 대해서 알아보자.

Application에서 만든 Data가 Transport layer의 segment, 즉 TCP의 data영역으로 들어간다. 

이것이 Network layer로 들어가서 datagram이 형성된다.

이것이 Data link layer로 들어가서 frame이 만들어지고, Network layer에서 datagram이었던 정보들이 frame의 data가 되는 것이다. 

각각의 data부분을 payload라고 하므로 Transport layer의 TCP payload, Network layer의 IP payload, Data-link layer의 payload는 위 그림과 같이 각 계층의 header를 제외한 영역이 해당된다. 

 

TCP Connection

TCP의 설립과정에 대해서 알아보자.

Establishment three-way handshake

연결 요청을 하는 패킷을 SYN 패킷이라고한다. 보통 서버는 연결을 대기하고 있고, 클라이언트가 서버에게 연결 요청을 하는 것이 일반적이다. 서버는 수동적이므로 클라이언트의 요청을 기다린다. 

 

Connection establishment using three-way handshake

 먼저 SYN 패킷을 통해 클라이언트가 연결 요청을 보낸다. 이 요청에 대해서 서버가 ACK으로 응답을 해주게 되는데, 연결을 요청할 때 제대로 받았는지 확인을 하기 위해 SYN 패킷에 sequence number를 붙여 보낸다. 

 그림을 보면 SYN 패킷에 sequence number로 8000을 보냈으니 ACK으로 그 다음 요청할 byte번호인 8001을 보내준다. 

SYN 이나 ACK을 패킷에 담아 전달할 때는 각각 Control field의 SYN filed / ACK field 값을 1로 설정하여 보내야한다. 그렇지 않으면 SYN / ACK에 담은 값이 무효하기 때문이다. 

 서버가 클라이언트에게서 8000seq의 SYN을 받고 ACK을 담은 응답패킷을 보낼 때, TCP는 양방향 통신이므로 서버에서도 클라이언트에 연결 요청을 해야한다. 따라서 연결요청 패킷 SYN을 담은 SYN + ACK 패킷을 보낸다. 

 서버가 응답 및 연결요청으로 클라이언트에 보낸 SYN + ACK 패킷에 대해서도 연결이 잘 되었는지 응답을 받아야하기에 SYN 패킷에 sequence number를 포함하여 보내야 하는데, sequence number는 시작 할 때 랜덤번호로 시작한다고 설명하였다. 따라서 그림에서 확인할 수 있는 클라이언트의 first sequence number 8000도, 서버의 fisrt sequence number 15000도 random number이다. 

 클라이언트가 서버에게서 seq : 8001의 ACK을 받으면, 요청이 정상적으로 전달되었음이 확인된다. 그리고 연결 요청인 seq : 15000 의 SYN이 왔기 때문에 응답으로 seq : 15001의 ACK을 보낸다. 이때 SYN Control field값이 0인 상태로 ACK만을 보냈으므로 패킷을 받은 서버는 sequence number를 보지 않는다.

 

SYN segment는 data를 가지지 않지만, sequence number 하나를 소비한다

 위 그림에서 sequence number로 8000을 보냈을 때 응답으로 8001이 왔다는 것은 SYN 패킷을 제외한 data의 크기가 0byte라는 것이다. 즉 클라이언트가 data없이 header에 SYN만을 담아 보냈으므로, sequence number는 SYN이 하나의 sequence number만을 사용하고 segment의 range가 0이기 때문에 그 다음값인 8001이 ACK으로 온 것이다. 

 만약 data를 패킷에 100byte 담았다고 가정한다면 SYN을 제외하고 8001~8100 까지의 정보를 읽었으므로, 응답으로 8101을 ACK에 담아 보냈을 것이다.

 SYN + ACK 또한 하나의 sequence number를 소비한다.

하지만 ACK만 header에 담아 보낼 때는 sequence number를 소비하지 않는다.

 

위 그림과 같은 상황을 three-way handshake라고 하는데, handshake는 통신을 주고 받는 상태를 말하며, three-way handshake는 보통 연결 시작상태로 들어갈 때 사용하는 방법이다.

 

Data Transfer

위의 three-way handshake로 연결 설정을 하였다고 가정하고 그 이후 데이터 전송상황을 보자.

sequence number로 8001을 보낸 데이터에 대해 데이터가 8001~9000 byte까지 이므로 응답 ACK은 9001이 와야한다.

sequence number로 9001을 보낸 데이터에 대해 데이터가 9001~10000 byte까지 이므로 응답 ACK은 10001이 와야한다.

 클라이언트 요청에 대한 서버의 응답으로 ACK만 보내도 되지만, 보낼 데이터가 있다면 data를 추가하여 보낸다. 이때 보내는 데이터의 시작 byte인 15001이 sequence number가 되고, 클라이언트에게서 10000까지의 byte를 받았으므로 다음으로 받고자 하는 10001을 ACK에 담아 보낸다.

 다시 서버에게서 패킷을 받은 클라이언트는 15001~17000 까지의 data를 받았으므로 ACK으로 다음으로 받을 17001을 담아 보낸다. 

 

위와 같은 통신과정은 프로세스 하나당 하나의 TCP 통신 시스템이 이루어지므로, 브라우저 하나에도 창이 여러개라면 각 창의 프로세스에 대해서 각각의 buffer를 가지고, 각 buffer마다 3-way handshake 과정으로 시작하는 Data tranfer 과정이 이루어지는 것이다.

 

Termination three-way handshake

연결을 끊을 때에 대해서 알아보자.

SYN와 마찬가지로 종료 요청 패킷을 보내는데 FIN이라고 한다. 마찬가지로 Control field의 FIN field 값을 1로 설정하여 FIN임을 알린다. 

 클라이언트가 sequence 로 x를 보냈으므로 서버가 보내는 ack은 x+1이다. FIN에 대한 응답으로 서버가 ACK을 보냈으므로 마찬가지로 서버도 클라이언트에게 FIN을 보내 종료 요청을 보낸다. 

 서버가 sequence로 y를 보냈으므로 클라이언트는 ACK에 y+1을 담아 보낸다. 

FIN 요청에 대한 ACK을 서버에서 받으면 buffer를 종료한다. 

 

위 그림과는 달리 연결을 끊는 방식으로 3-way handshake가 아닌 4-way handshake를 사용할 수도 있다.

 

FIN에서도 하나의 sequence number를 사용한다

 

전체적인 패킷 전송과정을 소켓 프로그래밍 함수를 통해서 알아보자

서버단을 보면, socket은 소켓을 생성하는 함수, bind는 소켓에 IP주소나 port 주소를 할당한다.

listen은 서버 소켓에서 호출하는 함수이고, accept통해 상대방의 연결요청을 받아들인다. 

클라이언트단을 보면, socket을 통해 소켓을 생성하고, connect를 통해 연결요청을 실행하고, 연결 이후 write나 read함수를 통해 데이터를 주고받는다.

 

connect를 통해 연결 요청을, close를 통해 연결 종료 요청을 한다. 

 

Half-Close

종료 요청은 클라이언트가 먼저 하는 것이 일반적이다. 클라이언트가 FIN을 보내고 서버가 ACK으로 응답만 하였을 경우, 서버는 남은 데이터를 마저 보내고 그 후에 FIN을 통해 종료 요청을 할 수 있다.

 클라이언트는 FIN을 보내고 난 후 서버가 보내는 data에 대해서 data를 보내지 못하고 ACK로 응답밖에 하지 못한다. 이러한 상태를 half-close라고 한다. 

서버가 data를 다 보내고 FIN을 보낸 후 클라이언트가 FIN에 대한 ACK를 보냈을 때 서버도 종료가 된고 buffer가 지워진다. 

반응형

관련글 더보기