본문 바로가기

Dev.Basic/네트워크

[1장] 4. 프로토콜 스택에 메시지 송신을 의뢰한다.

이 포스팅은 '성공과 실패를 결정하는 1%의 네트워크 원리' 책을 기반으로 작성되었습니다.

1장 네번째. 프로토콜 스택에 메시지 송신을 의뢰한다.

데이터 송 수신 동작의 개요
이제 드디어 IP 주소를 알아냈다. 이제 이 IP 주소와 HTTP Request 메시지를 프로토콜 스택에 전달하여 메시지 송신을 의뢰해야 한다.
이 때도 Socket 라이브러리를 이용한다. 단, 의뢰를 할 때는 Socket 라이브러리 프로그램 부품을 결정된 순번대로 호출해야 한다.
데이터를 송수신은 기본적으로 '데이터 통로'를 통해 수행된다. 클라이언트와 서버 사이에 파이프 같은 것이 존재하여 파이프를 통해 데이터를 주고 받는 것이다. 송수신 동작을 하기 전에 송수신하는 양자 사이를 파이프로 연결해줘야 한다. 연결해줄 때의 요점은 소켓이라 불리는 파이프의 양 끝에 있는 데이터의 출입구다. 우선 이 소켓을 만들어주고, 파이프를 연결해줘야 하는 것이다. 이 역할은 프로토콜 스택이 수행한다.

실제 파이프 연결 순서이다.
1. 먼저 서버 측에 소켓을 만든다.(보통 실행된 직후에 소켓을 만든다.) — 소켓을 만드는 단계
2. 서버에서는 이 소켓에 클라이언트가 파이프가 연결되길 기다린다.
3. 클라이언트 측에도 소켓을 만든다.
4. 클라이언트 측 소켓에서 파이프를 늘려 서버 측 소켓과 연결한다. — 소켓 접속 단계
5. 소켓이 연결되면 데이터 송수신 동작을 실행한다. — 메시지 송 수신 단계

데이터 송수신 동작이 끝나면 연결했던 파이프가 분리된다. 분리할 때는 어느 쪽에서 먼저 분리하든 상관없다. 한쪽에서 분리하면 나머지 한 쪽도 분리되기 때문이다.

자, 아까 프로토콜 스택에 메시지 송신을 의뢰하기 위해 Socket 라이브러리를 이용한다고 했다. 하지만 이 프로그램 부품들은 의뢰받은 메시지 내용을 그대로 프로토콜 스택에 전달하는 중개역을 수행할 뿐이고, 데이터 송수신에 대한 실질적인 작업은 하지 않는다. DNS 서버에 조회를 보낼 때와 비교해보자. 이 때는 리졸버를 호출하면 됬지만, 이번에는 프로그램 부품들을 순번대로 호출한다.


(1) 소켓을 만드는 단계
이 단계에서는 socket이라는 프로그램 부품만 호출하면 된다. socket을 호출하게 되면 제어권이 넘어가면서 소켓을 만들게 된다. 소켓이 다 만들어지면 제어권이 다시 애플리케이션에게 제어가 돌아온다.(소켓 생성에 대한 자세한 이야기는 뒤에서 진행된다.)
소켓이 생성되면 디스크립터라는 것이 생성되어 애플리케이션에게 전달된다. 애플리케이션은 이것을 메모리에 기록해둬야 한다. 이것은 소켓을 식별하는 식별자 역할을 수행한다.

소켓을 식별해야 한다고? 예를 들자면,  브라우저에서 2개의 창을 띄워 다른 페이지에 접속하게 되면 2개의 웹 서버에 동시에 액세스하게 되는 것이다. 이 때 각각의 웹 서버에 접속하기 위한 소켓이 필요할 것이고, 소켓을 식별해야 할 필요가 생긴다. 즉, 한 컴퓨터에 여러 개의 소켓이 존재할 수 있기 때문에 디스크립터가 필요한 것이다. 이 디스크립터를 프로토콜 스택에게 보여줘서 어느 소켓을 사용해야 하는지 알려주는 것이다. 즉, 디스크립터를 통해 프로토콜 스택이 알맞은 접속 동작이나, 데이터 송수신 동작을 실행하는 것이다.


(2) 소켓 접속 단계
만든 소켓을 서버측 소켓에 접속하도록 프로토콜 스택에 의뢰한다. 애플리케이션은 Socket 라이브러리의 connect 라는 프로그램 부품을 호출하여 이 의뢰 동작을 수행한다. connect를 호출할 때, 디스크립터, 서버의 IP 주소, 포트 번호 이 세 가지 값을 지정해준다. 디스크립터는 애플리케이션에 생성된 여러 소켓들 중 어떤 소켓으로 접속해야할 지(식별)를 정의하며, IP 주소는 네트워크 상에 접속해야할 서버의 주소를 정의하며, 포트 번호는 웹 서버에서 생성되어 있는 소켓 중 연결하고자 하는 소켓(식별)을 정의한다. 이 중 포트 번호는 전 세계적으로 통일되어 있는 값으로 애플리케이션의 종류에 따라 미리 결정된 값을 사용하면 된다.

클라이언트에서 웹 서버로 접속할 때는 포트 번호로 소켓을 찾아간다 쳐도 웹 서버는 어떻게 클라이언트 측의 소켓을 식별하는가?
이 역할을 프로토콜 스택이 수행하게 된다. 적당한 값을 골라서 할당을 한 뒤에, 접속 동작을 실행할 때 서버 측에 통지함으로써 식별 가능하게 하는 것이다.


(3) 메시지를 송 수신 단계
우선 애플리케이션은 송신 데이터(HTTP Request 메시지)를 메모리에 준비한다. 그리고 송신을 위해 Socket 라이브러리 중 write를 호출한다. 이 때 디스크립터와 송신 데이터를 지정한다. 이미 애플리케이션에는 상대 측과 연결되어 있고 관련 정보가 담긴 소켓들이 존재한다. 따라서 디스크립터를 통해 소켓을 지정해주면 그곳을 향해 데이터를 송신한다.

서버 측에는 수신 동작을 실행하여 적절한 처리를 한 후 응답 메시지를 반송한다.(후에 더 자세히 알아보자) 클라이언트 측에서는 서버로부터 온 응답 메시지를 수신하기 위해 Socket라이브러리의 read라는 프로그램 부품을 통해 프로토콜 스택에 수신 동작을 의뢰한다.  이 때 응답 메시지를 저장하기 위한 메모리 영역으로 수신 버퍼를 지정한다. read는 응답메시지를 받아서 이 수신 버퍼에 저장한다. 이 수신 버퍼는 애플리케이션 내부에 위치하고 있어서 버퍼에 메시지를 저장한 시점에서 메시지를 애플리케이션에게 전달해준다.


(4) 연결 끊기
데이터 송수신을 마치면 소켓 연결 끊기 단계로 들어간다. Socket 라이브러리의 close 라는 프로그램 부품을 호출하여 이를 실행한다. 애플리케이션 종류에 따라서 서버 측에서 먼저 close를 실행하는 경우도 있고, 클라이언트에서 먼저 close를 실행하는 경우가 있다. 만약 서버 측에서 먼저 close를 호출했다면, 연결을 끊었다는 것이 클라이언트 측에 전달되어 클라이언트의 소켓도 연결을 끊는다.


1장 네번째 End