티스토리 뷰

데이터 송신 수신 동작의 개요

DNS 서버를 통해 IP 주소를 조사했으면 액세스 대상 웹 서버에 메시지를 송신하도록 OS 내부에 있는 프로토콜 스택에 의뢰한다. 디지털 데이터를 송, 수신 하는 동작은 브라우저뿐만 아니라 네트워크를 이용하는 애플리케이션 전체에 공통이므로 이 동작은 웹에 한정되지 않고 모든 네트워크 애플리케이션에 해당된다.

 

여기에서도 DNS 서버에 IP 주소를 조회할 때와 같이 Socket 라이브러리에 들어있는 메서드들을 이용하는데, IP 주소를 조회할 때 처럼 하나씩 호출하고 끝나지는 않는다. 많은 메서드들을 결정된 순번대로 호출해야 하므로 복잡한 과정을 거친다. 즉, OS 내부의 프로토콜 스택에 메시지 송신을 의뢰할 때는 Socket 라이브러리 프로그램 메서드를 결정된 순서대로 호출한다.

 

소켓 파이프를 통해 데이터가 흘러간다

Socket 라이브러리를 이용해 데이터를 송수신하는 경우, 송수신자 사이에 데이터의 통로가 있고 이를 통해 데이터가 흘러간다. 데이터는 양쪽 모두 보낼 수 있고 양방향으로 데이터가 흘러간다.

 

이 파이프는 처음부터 존재하는 것이 아니라 연결하기 위한 동작이 필요하다. 가장 먼저 하는 것은 소켓을 만드는 것이다. 서버측에서 소켓을 만들고, 소켓에 클라이언트가 파이프를 연결하기를 기다린다. 서버측이 기다리는 동안 클라이언트 측에서도 소켓을 만들고, 소켓에서 파이프를 늘려 서버측 소켓에 연결한다. 이로써 양쪽의 소켓이 연결되면 데이터를 송수신 하기 위한 준비가 완료되었다.

 

송수신이 끝나면 연결했던 파이프가 분리된다. 파이프를 연결할 때는 클라이언트 측에서 서버 측을 향해 연결했지만, 분리할 때는 어느 쪽에서 분리해도 상관없다.

 

이 과정을 순서대로 요약해보면 다음과 같다.

1. 소켓을 만든다. (소켓 작성 단계)

2. 서버측에 소켓에 파이프를 연결한다. (소켓 접속 단계)

3. 데이터를 송, 수신한다 (송,수신 단계)

4. 파이프를 분리하고 소켓을 삭제한다 (연결 끊기 단계)

 

각 과정을 하나씩 살펴보자.


소켓 작성 단계

가장 먼저 소켓 작성 단계이다. DNS 서버의 경우 gethostbyname 이라는 프로그램 리졸버를 호출했지만, 소켓 접속 단계에서는 몇 개의 프로그램(메서드)를 결정된 순번으로 호출한다. 소켓을 만드는 단계부터 알아보면, 클라이언트는 소켓 라이브러리의 socket이라는 메서드만 호출하면 된다. socket을 호출한 후의 동작은 리졸버를 호출했을 때와 같이 socket 내부에 제어가 넘어가서 소켓을 만드는 동작을 실행하고, 이것이 끝나면 애플리케이션에 제어가 돌아온다. 위 그림에서 1번 과정이다.

 

소켓이 생기면 디스크립터라는 것이 반환 값으로 넘어오며 애플리케이션은 디스크립터를 받아 메모리에 기록해 둔다. 디스크립터는 소켓을 식별하기 위해 사용하는 것이다. 리눅스에 소켓, 디렉토리, 애플리케이션등은 모두 파일이다. 리눅스에서는 파일을 실행하게 되면 파일 디스크립터(fd)가 생기는데, 소켓 디스크립터도 파일 디스크립터의 일종이다. 즉, 소켓의 식별자라고 생각하면 쉽게 이해할 수 있다.


소켓 접속 단계

소켓을 만든 이후에는 소켓에 접속해야 한다. 클라이언트 측의 소켓이 서버 측의 소켓에 접속하도록 프로토콜 스택에 의뢰한다. 애플리케이션은 Socket 라이브러리의 connect를 호출해 이 동작을 실행한다. Connect를 호출할 때 디스크립터, 서버 IP 주소, 포트 번호 세가지 값을 지정한다.

 

디스크립터는 connect가 프로토콜 스택에 전달하고, 프로토콜 스택이 해당 값을 보고 어느 소켓을 서버 측의 소켓에 접속할지 판단해 접속 동작을 실행한다.

 

IP 주소는 DNS 서버로 부터 받은 응답 값이다. 데이터 송수신 동작을 실행할 때는 상대의 IP 주소를 프로토콜 스택에 알린다. IP 주소는 상대방의 네트워크 주소이기 때문에, IP 주소로 지정할 수 있는 것은 네트워크의 어떤 컴퓨터인가 하는 것 까지다. 상대 측의 소켓에 접속하기 위해서는 상대 포트 번호가 필요하다. 쉽게 이야기하면 상대방 아파트까지는 갈 수 있는데 몇 호인지 알아야 한다는 것이다. 서버 측의 포트 번호는 애플리케이션의 종류에 따라 미리 결정된 값을 사용한다. 웹은 80번, 메일은 25번이다.

 

포트 번호가 접속 상대 입장에서 접속하기 위한 소켓을 지정하기 위해 필요하다면, 접속을 기다리는 입장에서도 접속하는 상대의 소켓 번호가 필요하다.  즉, 서버에서도 클라이언트의 소켓 번호가 필요하다는 것이다. 이 부분은 서버가 소켓을 만드는 작성 단계에서 프로토콜 스택이 임의로 값을 할당한 후에, 프로토콜 스택이 접속 동작을 실행할 때 서버 측에 통지한다.

 

여기까지 요약해보면 connect를 호출하면 프로토콜 스택이 접속 동작을 실행하고, 상대와 연결되면 프로토콜 스택은 연결된 상태의 IP 주소, 포트 번호, 디스크립터 등의 정보를 소켓에 기록한다. 이 과정을 마치면 데이터 송수신이 가능한 상태가 된다.


송수신 단계

소켓이 상대측과 연결되면 데이터를 전달하면 된다. 소켓에서 데이터를 쏟아 부으면 상대측의 소켓에 데이터가 도착한다. 그러면 애플리케이션은 소켓을 직접 다룰 수 있으므로 Socket 라이브러리를 통해 프로토콜 스택에 의뢰한다. 이 의뢰하는 메서드가 write이다. 애플리케이션은 송신 데이터를 메모리에 준비한다. 여기서 송신 데이터가 사용자가 입력한 URL을 바탕으로 만든 HTTP 리퀘스트 메시지이다. Write를 호출하면 디크스립터와 송신 데이터를 지정하고, 프로토콜 스택이 송신 데이터를 서버에게 송신한다.

 

소켓의 접속 단계를 거친 후에는 소켓에 상대측의 정보가 기록되어 있기 때문에 상대의 소켓으로 데이터를 전송한다. 이때 송신 데이터는 네트워크를 통해 액세스 대상의 서버에 도착하며 서버는 수신 동작을 실행하여 받은 데이터의 내용을 처리해서 응답 메시지를 반송한다.

 

이 응답 메세지가 액세스 대상에게 도착하면 액세스 대상은 read를 호출해 프로토콜 스택에 수신 동작을 의뢰한다. 이때 수신한 응답 메세지를 저장하기 위한 메모리 영역을 지정하는데, 이 영역을 수신 버퍼라고 한다. 그러면 응답 메시지가 돌아올 때 read가 받아서 수신 버퍼에 저장한다. 수신 버퍼는 애플리케이션 프로그램의 내부에 마련된 메모리 영역이므로 수신 버퍼에 메시지를 저장한 시점에서 메시지를 애플리케이션에 건내준다.

 


연결 끊기 단계

브라우저가 데이터 수신을 완료하면 송수신 동작은 끝이난다. 그 후 Socket 라이브러리는 close를 호출해 연결 끊기 단계로 들어가도록 의뢰한다. 그러면 소켓 사이를 연결한 파이프가 분리되고 소켓도 삭제된다. 이 동작은 다음과 같은 과정을 거친다.

 

HTTP 프로토콜에서는 응답 메세지의 송신을 완료했을 때, 웹 서버측에서 연결 끊기를 실행하므로 먼저 웹 서버에서 close를 호출한다. 그러면 클라이언트 측에 이 호출이 전달되어 클라이언트의 소켓은 연결 끊기 단계로 들어간다.

 

브라우저가 read로 수신 동작을 의뢰했을 때 read는 수신한 데이터를 건내주는 대신 송수신 동작이 완료되어 연결이 끊겼다는 사실을 브라우저에 통지한다. 이로써 송수신이 종료되었다는 것을 알 수 있으므로 브라우저에서도 close를 호출하여 연결 끊기 단계에 들어간다.

댓글