관리 메뉴

Value Creator의 IT(프로그래밍 / 전자제품)

#3 TCP/IP 송수신 과정 본문

1. 프로그래밍/4) Network

#3 TCP/IP 송수신 과정

valuecreatort 2019. 6. 17. 19:29
반응형

TCP/IP 송수신 과정

인터넷 웹 개발을 하기 위해서는 네트워크 통신은 필수입니다. 모든 사용자 프로그램들은 네트워크 통신을 하기 위해서 아래와 같은 과정을 거쳐 통신을 하게 됩니다.

네트워크 통신과정을 요약하면 다음과 같을 수 있습니다.

  1. 클라이언트 소켓 생성
  2. 클라이언트와 서버 소켓 연결
  3. 데이터 송수신
  4. 소켓 연결 종료

아래 내용에서는 클라이언트와 서버가 어떻게 실제로 연결이 맺어지고 데이터 송수신을 하고 연결이 끊어지는지 좀 더 세부적으로 알아보겠습니다.

소켓 생성

  1. 클라이언트가 특정 서버와 통신을 하기 위해서는 운영체제에 socket() 시스템콜을 통해 소켓을 생성해야 합니다.
  2. 운영체제 내부 프로토콜 스택은 소켓의 정보를 저장하기 위해 메모리 영역을 할당하고 그곳에 소켓 제어 정보(상대 IP, 소켓 상태 정보 등)를 저장합니다.
  3. 운영체제는 생성된 소켓의 정보의 파일 디스크립터를 사용자 프로그램에게 반환해줍니다.

File discripter : 프로토콜 스택의 내부에 이는 다수의 소켓 중 어느 것을 가리키는지를 나타내는 번호표와 같은 존재이다.

소켓 연결 설정

아직 소켓은 만들었지만 아직 그 소켓에 아무런 정보도 없기 때문에 통신 상대가 누구인지 누구한테 통신을 해야 하는지 모른다. 그렇기 때문에 서버쪽 소켓과 연결 신청을 해 파이프를 구축해야 한다.

그래서 다음과 같이 connect() 시스템콜을 호출하여 서버와 커넥션을 맺어야 한다. 운영체제는 데이터 송수신을 하기 위해서 사용자 프로그램에 도달하기 전에 일시적으로 데이터를 저장하기 위해서 버퍼 메모리 영역을 할당을 하고 데이터 송수신을 할 준비를 마치게 된다.

이렇게 연결을 맺는 단계를 Three way handshake라고 한다.

Three way handshake

다음은 Three way handshanking을 도식화한 것입니다.

  1. 클라이언트는 서버에게 연결 요청의 SYN이라는 비트를 1을 만들어 TCP 헤더 정보를 설정하고 패킷을 생성하여 보낸다.
  2. 서버는 받은 패킷을 기반으로 수신처 포트 번호에 해당하는 소켓을 찾고 거기에 해당 클라이언트에 대한 정보를 저장한다.
  3. 서버는 마찬가지로 SYN 비트를 만들고 정상적으로 처리했다는 걸 알리기 위해 ACK 비트를 1로 해서 클라이언트에게 패킷을 보냅니다.
  4. 클라이언트는 받은 패킷 정보를 기반으로 서버 측의 접속 동작이 성공했는지 확인합니다.
  5. 클라이언트는 서버가 정상적으로 처리됐다는 것을 인지하고 자신도 패킷을 제대로 받았다는 것을 알리기 위해 ACK 비트를 1로 만들고 패킷을 만들어 서버에게 전송을 합니다.
  6. 이로써 소켓은 데이터를 송수신할 수 있는 상태가 됩니다.

커넥션 : 위와 같이 서버와 클라이언트가 송수신할 수 있는 상태가 파이와 같은 것으로 소켓이 연결되었다고 할 수 있다. 이렇게 접속이 안료 된 상태를 '커넥션’을 맺었다고 한다. 혹은 '세션’이라고도 부른다.

윈도우 : 수신 측에서 송신 측에 윈도우 사이즈(수신 확인을 기다리지 않고 묶어서 송신할 수 있는 데이터양)를 즉 받을 수 있는 사이즈를 통지하기 위해 사용합니다.

데이터 송수신

데이터 송수신은 write(), read() 시스템 콜로 실행할 수 있습니다. 프로토콜 스택은 받은 데이터에 내용을 바로 송신하는 것이 아니라 일단 자체 송신용 버퍼 메모리 영역에 저장하고, 애플리케이션 다음 데이터를 건네주기를 기다립니다. 각 애플리케이션마다 송신 의뢰하는 데이터양 등이 적으면 너무 빈번하게 송신이 일어나면 네트워크의 이용 효율이 좋지 않기 때문에 한 번에 모아서 전송할 수도 있습니다.

또 반대로 데이터가 너무 클 때는 이것을 분할하여 보냅니다. 아래는 MTU와 MSS에 대한 그림입니다. 일반적으로 TCP 통신을 하기 위해서 이더넷에 송신할 수 있는 MTU는 1500 바이트이다. 그리고 MTU에서 TCP 헤더가 40이라고 하면 이것을 뺀 1460바이트가 MSS이다.

  • MTU : 패킷 한 개로 운반할 수 있는 디지털 데이터의 최대 길이. 이더넷에서는 보통 1,500 바이트
  • MSS : 헤더를 제외하고 한개의 패킷으로 운반할 수 있는 TCP의 최대 데이터 길이

만약 송신하고자 하는 데이터가 MSS(1460) 바이트를 넘는다고 하면 이것을 분할하여 데이터를 보내야 합니다. 그럼 지금부터 어떻게 데이터를 분할해서 보내고 또 받는지 다음 그림을 통해서 알아보겠습니다.

  1. 먼저 클라이언트가 데이터를 보내기 전에 자신이 보내려고 하는 데이터의 바이트를 세서 TCP 헤더에 기록하여 초기 SEQ를 생성해서 서버로 보낸다.
  2. 그리고 서버는 수신한 데이터 크기를 계산하여 그 값에 1을 더하여 ACK 번호넘긴다.
  3. 클라이언트는 나머지 분할 된 데이터 바이트를 읽어 SEQ를 생성해서 패킷을 보낸다.
  4. 만약 이 과정 중에 서버가 받았다는 ACK를 보내주지 않는다면 클라이언트는 다시 한번 요청하게 해준다.(ACK를 기다리는 시간이 초과한 것을 Time out이라 한다.)
  5. 그리고 서버는 다시 한번 패킷을 수신하면 SEQ번호 기반으로 패킷을 비교하여 기존에 도착한 패킷을 연결한다.(만약 이 과정 중에 SEQ 번호가 맞지 않다는 것은 데이터가 누락되었다는 것을 의미한다.)
  • 실제로는 시퀀스 번호가 1부터 시작하지 않고 난수를 바탕으로 산출한 초기값으로 시작한다. 시퀀스 번호를 항상 1부터 시작한다고 예측할 수 있으면 거기에 악의적인 공격을 할 우려가 있기 때문이다. 그러나 난수값으로하면 서버측에서 초기 SEQ을 알 수 없기 때문에 초기 Connect 단계에서 초기 SEQ 설정을 해줍니다.

소켓 연결 종료

TCP/IP 종료는 close() 시스템콜 함수로 종료할 수 있습니다. 일반적으로 HTTP 메시지를 송수신 한 뒤 다음과 같이 four way handshake 으로 연결을 종료합니다.(HTTP 1.1 이후부터는 바로 연결을 끊지는 않습니다.) 다음 그림은 서버의 요청으로 연결을 끊는 모습을 도식화 한 것입니다.

Four way handshake

다음은 Four way handshake을 도식화한 것입니다.

  1. 클라이언트는 서버는 FIN에 1을 설정한 TCP 헤더가 도착하면 자신의 소켓에 서버가 연결 종료에 들어갔다는 것을 표시합니다.
  2. 클라이언트는 자신이 FIN 플래그를 받았다는 것을 알리기 위해 서버에게 ACK를 서버 측으로 반송한다.
  3. 그와 동시에 Server에서는 해당 포트에 연결되어 있는 Application에게 Close()를 요청한다.
  4. 애플리케이션의 네트워크 연결 종료 준비가 끝나면 클라이언트도 마찬가지로 FIN 플래그를 서버에게 보냅니다.
  5. 서버는 FIN 플래그를 받게 되면 받았다는 것을 클라이언트에게 알리기 위해 ACK 플래그를 보냅니다.
  6. 클라이언트가 ACK를 받게 되면 네트워크 연결이 종료가 됩니다.

FIN_WAIT : FIN_WAIT 상태에서 클라이언트로부터 특정 시간 동안 응답이 오지 않는다면 특정 시간 이후에 서버는 자동으로 Closed가 된다.

TIME_WAIT: 서버에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop 되고 데이터는 유실될 것입니다.
이러한 현상에 대비하여 서버는 클라이언트로부터 FIN을 수신하더라도 일정 시간(디폴트 240초) 동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거치게 되는데 이 과정을 "TIME_WAIT"라고 합니다. 240초 후에는 CLOSED가 됩니다.

참고

반응형
Comments