Chapter 2. Story 2. 서버에 접속한다

Posted by yunki kim on July 31, 2022

접속의 의미

  소켓을 만들면 애플리케이션은 connect()을 호출하고 프로토콜 스택은 자기쪽의 소켓을 서버측 소켓에 접속한다. 이 때, 통신 회선 등의 물리적인 케이블은 항상 접속되 있다. 따라서 데이터를 신호로 변홚 송신하기만 하면 언제든지 통신할 수 있다. 하지만 이제 소켓을 막 만든 시점에서 애플리케이션에서 데이터 송신 의뢰가 오면 프로토콜 스택은 어떻게 동작할까?

  소켓이 갓 생성된 직후에는 소켓에는 아무런 정보도 없다. 따라서 데이터를 보낼 수 없다. 애플리케이션이 URL을 통해 IP를 파악하고, 포트를 알고 있지만 프로토콜 스택에는 아무런 정보도 전달되지 않은 상태다. 따라서 서버의 IP, 포토를 프로토콜 스택에 알릴 동작이 필요하며 이것이 접속 동작의 한 역할이다.

  서버측 역시 소켓이 만들어진 직후에는 클라이언트의 정보를 알 수 없다. 또 한, 애플리케이션을 통해 클라이언트의 정보를 알 수 없다. 이 문제를 해결하기 위해 클라이언트는 자신의 IP와 Port를 담은 연결 요청을 전달한다. 그러면 서버는 클라이언트 정보를 알 수 있다. 이처럼 클라이언트에서 서버측에 통신 동작의 개시를 전달하는 것도 접속의 한 역할이다.

  접속 동작의 첫 번쨰 동작은 통신 상대와의 사이에 제어 정보를 주고받아 소켓에 필요한 정보를 기록하고 데이터 송/수신이 가능한 상태로 만드는 것이다. 여기서 말하는 제어 정보는 IP 같은 데이터 송/수신을 제어하기 위한 정보다. 접속 동작에서 주고받는 제어 정보는 통신의 규칙으로 정해져 있기때문에 규칙을 따르면 데이터를 송/수신할 수 있는 상태가 된다.

  데이터를 송/수신할 때는 데이터를 일시적으로 저장하는 버퍼 메모리가 필요하다. 버퍼 메모리 확보는 접속 동작을 실행할 때 실행되며 이 행위를 '접속한다'라고 한다.

맨 앞부분에 제어 정보를 기록한 헤더를 배치한다

  위에서 언급한 제어 정보는 크게 두 종류로 나뉘며 그 중 하나가 클라이언트와 서버가 연략을 절충하기 위해 주고받는 정보다. 제어 정보의 항목은 고정되 있고 접속, 송/수신, 연결 끊기 각 단계에서 클라이언트와 서버 대화시 제어 정보를 패킷 맨 앞에 추가한다. 이렇게 앞에 추가된 제어 정보들을 헤더라 부른다. 제어정보는 다음과 같은 필드들을 포함한다.

필드 명칭 길이(비트) 설명
TCP 헤더(20 바이트 ~) 송신처 포트 번호 16 이 패킷을 송신한 측의 프로그램의 포트 번호
수신처 포트 번호 16 이 패킷을 받는 상대 프로그램의 포트 번호
시퀀스 번호(송신 데이터의 일련 번호) 32 이 패킷의 맨 앞 위치의 데이터가 송신 데이터의 몇 번째 바이트에 해당하는지를 송신측에서 수신측에 전달하기 위한것
ACK 번호(수신 데이터의 일련번호) 32 데이터가 볓 바이트까지 수신측에 도착했는지를 수신측에서 송신측에 전달하기 위한 것. ACK는 ackowledge의 약자
데이터 오프셋 4 데이터 부분이 어디부터 시작하는지를 나타낸다. 헤더의 길이를 나타낸다.
사용하지 않음 6 이 필드는 현재 사용하지 않는다.
컨트롤 비트 6 이 필드의 각 비트가 각각 통신 제어상의 의미를 가진다.
- URG: 긴급 포인터의 필드가 유효하다는 것을 나타낸다
- ACK: 수신 데이터의 일련 번호 필드가 유효하다는 것을 나타낸다. 보통 데이터가 올바르게 수신측에 도착한 것을 의미한다.
- PSH: flush 동작에 의해 송신된 데이터임을 나타낸다.
- RST: 접속을 강제로 종료하고 이상 종료시에 사용한다.
- SYN: 송신측과 수신측에서 일련번호를 서로 확힌한다. 이것으로 접속 동작을 나타낸다.
- FIN: 연결 끊기를 나타낸다.
윈도우 16 수신측에서 송신측에 윈도우 사이즈(수신 확인을 기다라지 않고 묶어서 송신할 수 있는 데이터 양)를 통지하기 위해 사용된다.
체크섬 16 오류 유무를 검사하기 위한 것
긴급 포인터 16 긴급하게 처리해야 할 데이터의 위치를 나타낸다.
옵션 가변 길이 위의 헤더 필등 이외의 제어 정보를 기록하기 위해 헤더의 옵션 필드를 추가할 수 있다. 그러나 접속 동작을 제외하면 옵션 필드를 사용하는 경우는 적다.

  애플리케이션의 데이터를 송/수신할 경우의 패킷은 다음과 같은 모습을 가진다.

  접속 동작, 연결 끊기 등 데이터가 없는 경우의 패킷은 다음과 같은 모습을 하고 있다.

  제어 정보의 또 다른 한 종류는 소켓에 기록해 프로토콜 스택의 동작을 제어하는 것이다. 이 정보는 프로토콜 스택 내의 소켓의 메모리 영역에 기록된다. 이 메모리에는 애플리케이션에서 통지된 정보, 통신 상대로부터 받은 정보가 있다. 

  클라이언트와 서버는 규칙에 따라 헤더에 정보를 기록하고 대화하기 때문에 상대방의 소켓에 기록된 정보를 볼 수 없다.

  소켓에 기록하는 제어 정보는 프로토콜 스택을 만드는 사람에 따라 달라진다. 하지만 다음과 같은 일부 정보의 경우는 모든 OS 공통이다. 

  위 사진에 나열된 정보에 대해 알고 싶다면 이 링크를 참고하자.

접속 동작의 실체

  접속 동작은 Socket 라이브러리의 connect을 호출하는 것부터 시작한다.

1
connect(<디스크립터><서버측의 IP 주소와 포트 번호>, ...)
cs

  이 함수로 전달된 IP, port는 프로토콜 스택의 TCP 담당 부분에 전달된다. 그러면 데이터 송/수신 동작의 개시를 나타내는 제어 정보를 기록한 헤더를 만들고 서버의 TCP 담당 부분과의 사이에 제어 정보를 주고 받는다. 

  헤더의 여러 항목 중 이 과정에서 중요한 것은 송신처와 수신처의 포트번호다. 포트번호가 있어야 클라이언트와 서버의 소켓을 지정할 수 있다. 즉, 접속해야 하는 소켓을 특정짓고 컨트롤 비트인 SYN을 1로 만든다.

  위와 같은 과정으로 통해 TCP 헤더를 만들면 이를 IP를 담당하는 레이어에 의뢰한다. 그러면 IP 담당 부분은 패킷을 송신한다.

  네트워크를 통해 패킷이 서버에 도착하면 IP 담당 부분이 이를 TCP 담당 부분으로 전달한다. 이후 서버측 TCP 담당 부분은 TCP 헤더를 조사해 기록되 있는 포트에 해당하는 소켓을 찾는다. 해당 소켓이 발견되면 필요한 정보를 기록하고 접속 동작이 진행중이라는 상태가 된다. 그 후 서버의 TCP 담당 부분은 응답을 돌려보낸다.

  응답을 받은 서버는 클라이언트와 마찬가지로 TCP 헤더를 만든다. 이 때, SYN 대신 ACK라는 컨트롤 비트를 1로 만든다. 이는 패킷을 정상적으로 받았음을 알리기 위함이다. 그 후 IP를 담당하는 부분이 클라이언트에게 응답한다.

  그러면 해당 패킷은 클라이언트의 IP 담당 부분을 거쳐서 TCP 담당 부분에 도착하게 된다. 이 때, TCP 헤더에서 SYN이 1이면 접속 성공이므로 소켓에 서버의 IP, port와 함께 접속 완료를 나타내는 제어 정보를 기록한다. 그 후, 서버가 보낸 패킷이 클라이언트에 도착했다는 것을 알리기 위해, ACK 비트를 1로 만든 TCP 헤더를 반송한다.

  이제 소켓은 데이터를 송/수신할 수 있는 상태가 되었고 파이프로 연결되 있다. 여기서 이 파이프를 커넥션이라 한다. 이 커넥션은 close()를 호출해 연결을 끊을 때 까지 계속 존재한다.

 

출처 - 성공과 실패를 결정하는 1%의 네트워크 원리