Socket Identificaiton
0. 들어가면서
한 1년 전쯤 한번 같이 일하는 동료랑 웹 소켓에 대한 이야기를 나눴던 적이 있다. 웹 소켓을 연결할 때 사용하는 포트 번호에 관련된 이야기였다. 지금에 와서 정확히 기억이 나진 않지만, 클라이언트(브라우저)에서 서버의 80 포트로 웹 소켓을 연결할 때 연결 식별에 대한 이야기였던 것 같다. 당시에 이 주제를 메모장에 짧게 적어뒀다가 1년이 넘게 지난 지금에야 본인 블로그에 정리한다.
1. Socket
소켓(socket)은 대학 시절 네트워크 통신 시간에 처음 접했던 것 같다. 단순히 IP 주소와 포트 번호를 조합하면 소켓 주소가 된다는 교수님의 설명이 아직도 기억에 남아있다. 대학 시절의 나는 “말하는 감자” 수준이었기 때문에 이해하기 쉽게 설명하신 것 같다. 소켓의 정의를 찾아봤다.
네트워크 소켓이란 네트워크 노드(node) 내에서 네트워크를 거쳐 데이터를 전송하고 받기 위한 엔드포인트 역할을 하는 소프트웨어 구조다.
네트워크 노드란 문맥상 네트워크에 포함된 물리적 머신을 의미하는 것으로 보인다. 소켓은 프로세스가 노드에서 동작하는 동안 생성되고 사용된다. 위키피디아에 따르면 소켓의 구체적인 정의가 여러 레퍼런스마다 조금씩 다르다는 사실을 알게 되었다. 용어에 따라 미묘하게 의미가 다른 부분이 있는 것으로 보인다.
- 소켓 기술자(socket descriptor) 혹은 소켓 지시자
- 소켓 주소(socket address)
기왕 글을 쓰는 김에 각 용어에 대한 개념도 함께 정리헀다.
2. Socket Descriptor
소켓 기술자(socket descriptor)라는 개념은 애플리케이션이 운영체제의 네트워크 통신 기능을 사용하기 위해 소켓 API(application programming interface)를 호출할 때 필요하다. 소켓은 네트워크 아키텍처에선 API로 정의된다. API이니 구현체의 구체적인 구현 방식은 운영체제마다 다르다.
소켓 API는 애플리케이션과 네트워크 프로토콜 스택(TCP/IP) 연결 고리 역할을 한다. 개발자는 소켓 API를 사용해 운영체제가 제공하는 네트워크 기능을 호출하고 데이터를 송수신할 수 있다. 개발자는 네트워크의 복잡한 내부 동작을 알 필요가 없다.
애플리케이션 프로세스가 소켓을 열면 해당 소켓을 식별할 수 있는 소켓 기술자가 반환된다. 하나의 애플리케이션은 여러 개의 소켓을 열 수 있다. 단, 하나의 애플리케이션에서 연 소켓 기술자 값은 중복된 것이 없다. 애플리케이션은 멀티스레딩, 비동기 I/O 등을 통해 각 소켓을 독립적으로 처리한다.
아래 이미지를 통해 애플리케이션, 소켓, TCP/IP 연결의 관계를 알아보자.
- 네트워크 통신에서 노드 식별은 IP 주소를 사용한다.
- 하나의 노드에서 애플리케이션 식별은 포트 번호를 사용한다.
- 포트 번호는 상위 계층(애플리케이션 계층)의 프로세스를 구분하기 위한 번호이기 때문에 하나의 노드 내에서 동작하는 애플리케이션은 서로 다른 포트 번호를 사용해야 한다.
- 애플리케이션 내에서 네트워크 통신 연결 식별은 소켓 기술자(sd)를 사용한다.
- 2번, 3번, 4번 애플리케이션의 소켓 기술자가 3으로 동일하지만, 한 애플리케이션 내에서만 중복되지 않으면 된다.
소켓 기술자는 포인터(pointer)를 관리하는 테이블의 인덱스(index)이다. 애플리케이션은 특정 소켓을 사용할 때 소켓 정보가 담긴 구조체를 참조할 수 있는 포인터가 저장된 테이블을 소켓 기술자를 통해 접근할 수 있다.
3. Socket Address and connection identification
위에서 네트워크에서 소켓 구조체에 대한 개념과 애플리케이션이 소켓 기능을 사용하기 위해 API를 통해 접근한다는 컨셉을 확인했다. 이제 소켓 연결을 식별하는 방법에 대해 살펴본다.
본격적으로 소켓 연결 식별에 대한 이야기를 하기 전에 소켓 주소(socket address)에 대한 개념을 먼저 정리할 필요가 있다. 소켓의 고유한 식별성은 IP 주소, 포트 번호, 프로토콜의 조합으로 이뤄진다. 이 조합을 소켓 주소라고 한다.
- IP 주소
- 노드의 네트워크 상 위치를 나타낸다.
- IPv4(32 bit), IPv6(128 bit) 주소 체계를 사용한다.
- 포트 번호
- 노드 내에서 실행 중인 특정 프로세스를 식별한다.
- 각 프로토콜 마다 잘 알려진 포트 번호를 사용한다. 예를 들어, HTTP는 80, HTTPS는 443, SSH는 22 포트 번호를 사용한다.
- 프로토콜
- 데이터가 전송되는 방식과 규칙을 정의한다.
- 주요 프로토콜은 TCP(전송 제어 프로토콜), UDP(사용자 데이터그램 프로토콜)이 있다.
TCP 통신의 경우 서버, 클라이언트 모델에서 소켓 통신은 서버의 소켓과 클라이언트의 소켓이 서로 연결을 맺고 메시지를 주고 받는 과정이다. 예를 들어 보자. 서버 소켓이 192.168.1.10:8080
이라고 가정한다. 두 개의 클라이언트가 연결을 수행한다.
- 클라이언트1 - 192.168.1.20:5000
- 클라이언트2 - 192.168.1.30:5001
서버 소켓은 리스닝(listeening socket)이라고 하여 클라이언트 연결 요청을 기다린다. 클라이언트가 연결 요청을 보내면 서버는 클라이언트의 요청을 수락(accept)한다. 연결 요청을 수락한 해당 클라이언트와 통신을 담당할 새로운 소켓을 생성한다. 새롭게 생성한 소켓은 고유한 4-튜플(tuple) 기반으로 생성되며, 서버와 클라이언트 간의 데이터 송수신에 사용된다. 서버 측 새로 생성된 소켓은 별도 스레드에 의해 독립적으로 수행된다.
여기서 말하는 4-튜플이란 서버, 클라이언트 연결의 고유함을 식별하기 위해 클라이언트 IP, 포트, 서버 IP, 포트를 조합한 정보를 의미한다. 위 예시를 기반으로 4-튜플을 정의하면 다음과 같다.
- 연결 1 - (192.168.1.20:5000, 192.168.1.10:8080)
- 연결 2 - (192.168.1.30:5001, 192.168.1.10:8080)
서버는 4-튜플 정보를 바탕으로 두 연결이 서로 다르다는 것을 식별할 수 있다. 이는 하나의 클라이언트(하나의 물리적 머신, 하나의 프로세스)에서 동일한 서버로 여러 연결을 맺을 때도 동일하게 적용된다. 예를 들면 브라우저에서 여러 개의 탭을 통해 동시에 구글에 접속하는 것을 생각해보면 쉽다. 클라이언트 머신의 IP 주소가 192.168.1.20
라고 가정한다. 브라우저 각 탭에서 서버로 연결을 요청할 때 서로 다른 포트를 사용해 소켓 주소를 구분한다.
- 클라어은트 머신의 브라우저의 1번 탭 - 192.168.1.20:5000
- 클라어은트 머신의 브라우저의 2번 탭 - 192.168.1.20:5001
다음과 같은 4-튜플 정보가 생긴다.
- 연결 1 - (192.168.1.20:5000, 192.168.1.10:8080)
- 연결 2 - (192.168.1.20:5001, 192.168.1.10:8080)
소켓 연결 정보는 lsof 명령어를 통해 확인할 수 있다. lsof 명령어는 파일 시스템의 열린 파일을 나열하며, 네트워크 소켓도 열린 파일
로 간주하기 때문에 이를 확인할 수 있다. 브라우저에 두 개의 탭을 열어 NodeJS 개발 서버에 접속했을 때 lsof 명령어를 수행하면 다음과 같은 정보를 확인할 수 있다.
- firefox 프로세스
- 두 개의 소켓 연결 정보를 확인할 수 있다. 프로토콜은 TCP다. 서버 소켓 주소의 포트 번호는 동일하지만, 클라이언트(로컬) 소켓의 포트 번호가 다르다.
- node 프로세스
- 서버 소켓(localhost:5173)이 LISTEN 상태이다.
- 두 개의 소켓 연결 정보를 확인할 수 있다. 프로토콜은 TCP다. 서버(로컬) 소켓 주소의 포트 번호는 동일하지만, 클라이언트 소켓의 포트 번호가 다르다. 클라이언트 소켓의 포트 번호는 firefox 프로세스에서 사용 중인 포트 번호와 동일하다.
$ lsof -i :5173
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
firefox 3329 junhyunkang 136u IPv6 0xd115062857d781fb 0t0 TCP localhost:51750->localhost:5173 (ESTABLISHED)
firefox 3329 junhyunkang 165u IPv6 0xee22c9baba2f7cc7 0t0 TCP localhost:52149->localhost:5173 (ESTABLISHED)
node 34093 junhyunkang 37u IPv6 0x9c7c5878d9b723a6 0t0 TCP localhost:5173 (LISTEN)
node 34093 junhyunkang 43u IPv6 0xb28c06ebfe31dcb7 0t0 TCP localhost:5173->localhost:52149 (ESTABLISHED)
node 34093 junhyunkang 44u IPv6 0x75b7f2cdedb19f8a 0t0 TCP localhost:5173->localhost:51750 (ESTABLISHED)
REFERENCE
- https://en.wikipedia.org/wiki/Network_socket
- https://networkengineering.stackexchange.com/questions/54344/why-is-a-tcp-socket-identified-by-a-4-tuple
- https://www.quora.com/Why-is-a-TCP-socket-identified-by-a-4-tuple
- https://slidesplayer.org/slide/14140274/
- http://jkkang.net/unix/netprg/chap2/net2_1.html
- https://www.rfc-editor.org/rfc/rfc793
댓글남기기