업데이트됨 1개월 전
지금도 당신의 컴퓨터에서는 수십 개의 프로그램이 네트워크를 사용하고 있습니다. 브라우저에는 여러 탭이 열려 있고, 이메일은 백그라운드에서 동기화되고, 동영상이 스트리밍되고, 운영체제는 업데이트를 확인합니다.
그런데 컴퓨터에는 네트워크 연결이 하나뿐입니다.
패킷이 도착했을 때, 어떤 프로그램으로 가야 하는지 어떻게 알 수 있을까요? YouTube를 보여주는 브라우저 탭인지, 아니면 은행 사이트를 불러오는 탭인지? 이메일 클라이언트인지, 시스템 업데이터인지?
소켓은 하나의 엔드포인트입니다. 한 프로그램의 네트워크 대화가 이루어지는, 컴퓨터 내부의 특정 목적지입니다. 네트워크에 연결된 모든 프로그램은 최소한 하나의 소켓을 생성하며, 그 소켓은 인터넷 전체에서 고유한 주소를 가집니다.
소켓 주소
소켓은 세 가지로 식별됩니다:
- IP 주소 — 네트워크에서 어떤 컴퓨터인지
- 포트 번호 — 그 컴퓨터에서 어떤 프로그램인지 (0~65535)
- 프로토콜 — TCP 또는 UDP
이 세 가지가 합쳐져 소켓 주소를 구성합니다. 브라우저는 192.168.1.50:52431/TCP에 소켓을 만들 수 있고, 웹 서버는 93.184.216.34:443/TCP에 소켓을 가집니다. 두 소켓 사이를 오가는 패킷에는 양쪽 주소가 모두 담겨 있습니다.
IP 주소는 건물이고, 포트는 호수입니다. 그게 전부입니다.
포트 번호가 존재하는 이유
컴퓨터가 수천 개의 연결을 동시에 유지할 수 있는 것은, 각 연결이 IP 주소와 포트 번호의 고유한 조합을 가지기 때문입니다.
브라우저가 웹 서버에 연결할 때, 자신의 쪽 대화를 위해 사용되지 않는 포트(예: 52431)를 선택합니다. 서버는 포트 443(표준 HTTPS 포트)에서 수신 대기합니다. 모든 패킷에는 출발지와 목적지 소켓 주소가 모두 담겨 있어, 항상 올바른 곳에 도달합니다.
운영체제는 들어오는 각 패킷의 목적지 포트를 읽어 해당 포트를 소유한 소켓에 전달합니다. 서로 다른 대화는 서로 다른 포트를 사용하므로 충돌이 없습니다.
스트림 소켓과 데이터그램 소켓
소켓은 두 가지 전송 프로토콜에 대응하는 두 가지 유형이 있습니다.
스트림 소켓은 TCP를 사용합니다. 연결을 맺어 데이터가 순서대로, 손상 없이, 정확히 한 번 도착함을 보장하는 전용 채널을 만듭니다. 연결하고, 데이터를 교환하고, 연결을 끊습니다. 전화 통화와 같습니다.
웹 브라우징, 이메일, 파일 전송은 스트림 소켓을 사용합니다. 다만 오버헤드가 따릅니다. TCP는 핸드셰이크를 수행하고, 확인 응답을 추적하며, 손실된 패킷을 재전송해야 합니다.
데이터그램 소켓은 UDP를 사용합니다. 연결도 없고, 보장도 없습니다. 패킷을 보내면 도착할 수도, 안 할 수도 있습니다. 또 다른 패킷을 보내면 먼저 도착할 수도 있습니다. 엽서를 우편으로 보내는 것과 같습니다.
동영상 스트리밍, 온라인 게임, DNS 조회는 데이터그램 소켓을 사용합니다. 동영상 프레임 하나가 사라지면 계속 재생하면 됩니다. 재전송을 요청하느라 일시 중지하는 것이 오히려 더 나쁩니다. 완벽함보다 속도가 더 중요합니다.
소켓의 생명 주기
스트림 소켓에는 생명 주기가 있습니다. TCP 연결이 그러하기 때문입니다.
서버 소켓은 listen()을 호출해 연결 수락을 시작합니다. 클라이언트 소켓은 connect()를 호출해 연결을 시도합니다. TCP의 3방향 핸드셰이크가 완료되면, 두 소켓은 established 상태에 진입합니다. 이 상태에서 데이터가 흐릅니다.
종료는 더 복잡합니다. TCP는 양쪽 모두 대화가 끝났다는 데 동의해야 합니다. 종료를 시작한 쪽은 FIN-WAIT-1, FIN-WAIT-2, TIME-WAIT 단계를 거칩니다. 상대방은 CLOSE-WAIT와 LAST-ACK 단계를 거칩니다.
TIME-WAIT는 처음에 이상하게 보입니다. 연결이 닫힌 후에도 소켓은 완전히 사라지기 전까지 60초 동안(Linux 기준; 다른 시스템은 다를 수 있음) 남아 있습니다. 왜 닫힌 소켓을 격리해두는 걸까요?
패킷은 인터넷을 통해 예상치 못한 경로로 이동하기 때문입니다. 이전 대화에서 온 오래된 패킷이 유령처럼 나타날 수 있습니다. 시스템이 해당 소켓 주소를 즉시 새 연결에 재사용한다면, 이 유령 패킷이 새 연결을 망칠 수 있습니다. TIME-WAIT는 남아있는 패킷들이 모두 사라진 후에야 주소가 재사용되도록 보장합니다.
데이터그램 소켓은 이 모든 과정을 건너뜁니다. 연결이 없으니 연결 상태도 없습니다.
프로그래밍 인터페이스
소켓 API는 1980년대 BSD Unix에서 시작되어 보편적인 표준이 되었습니다. C, Python, Go, JavaScript — 모두 같은 모델을 사용합니다.
소켓 생성: socket()은 새 소켓의 핸들을 반환합니다.
서버의 경우: bind()로 포트를 점유하고, listen()으로 연결 수락을 시작하며, accept()로 클라이언트를 기다립니다. 수락된 각 연결은 해당 대화를 위한 새 소켓을 생성합니다.
클라이언트의 경우: 서버 주소로 connect()를 호출합니다. 연결되면 소켓을 사용할 준비가 됩니다.
모든 경우: send()와 recv()로 데이터를 주고받습니다. 운영체제가 분할, 체크섬, 재전송, 재조립을 처리합니다. 바이트를 쓰고, 바이트를 읽으면 됩니다.
이 추상화 덕분에 네트워크 프로그래밍이 가능합니다. TCP 헤더를 직접 구성하거나 체크섬을 계산할 필요가 없습니다. 소켓에 무엇을 보낼지 알려주면, 소켓이 프로토콜을 처리합니다.
소켓 옵션
소켓은 세밀하게 조정할 수 있습니다.
SO_REUSEADDR는 아직 TIME-WAIT 상태인 포트에 바인딩할 수 있게 해줍니다. 이 옵션 없이는 서버를 재시작할 때 이전 연결이 정리될 때까지 기다려야 합니다. 개발 환경에서는 귀찮고, 운영 환경에서는 용납할 수 없는 일입니다.
SO_KEEPALIVE는 유휴 연결에 주기적인 프로브를 보내 상대방이 사라졌는지 감지합니다.
버퍼 크기는 운영체제가 대기시키는 데이터의 양을 제어합니다. 버퍼가 클수록 지연 시간이 긴 링크에서 처리량이 향상되고, 버퍼가 작을수록 메모리 사용량과 지연 시간이 줄어듭니다.
타임아웃은 네트워크가 오작동할 때 작업이 무한정 대기 상태에 빠지는 것을 방지합니다.
기반
모든 네트워크 애플리케이션은 소켓 위에 구축됩니다. 소켓은 프로그램이 끝나고 네트워크가 시작되는 경계입니다.
패킷, 경로, 재전송에 대해 생각할 필요가 없습니다. 소켓을 열고, 데이터를 보내고, 데이터를 받고, 소켓을 닫으면 됩니다. 인터넷 프로토콜의 모든 복잡성은 그 인터페이스 아래에서 처리됩니다.
소켓은 네트워크 통신의 근본적인 단위입니다. HTTP, WebSocket, 데이터베이스 연결, API — 이 모든 것이 이 기반 위에 놓여 있습니다.
소켓에 관한 자주 묻는 질문
컴퓨터가 한 번에 몇 개의 소켓을 열 수 있나요?
기본 설정으로 수만에서 수십만 개입니다. 조정하면 수백만 개까지 가능합니다. 각 소켓은 커널 메모리를 소모하며, 운영체제는 파일 디스크립터로 소켓을 추적합니다. 트래픽이 많은 서버는 이 한계를 높게 조정합니다.
소켓과 포트의 차이는 무엇인가요?
포트는 기기의 엔드포인트를 식별하는 번호(0~65535)입니다. 소켓은 실제 엔드포인트로, 포트뿐 아니라 IP 주소와 프로토콜도 가집니다. 여러 소켓이 포트를 공유할 수 있지만(원격 주소가 다른 경우), 각 소켓은 고유합니다.
일부 애플리케이션이 TCP와 UDP를 모두 사용하는 이유는 무엇인가요?
데이터마다 요구사항이 다르기 때문입니다. 화상 통화는 오디오와 동영상에는 UDP(실시간성이 완벽함보다 중요)를, 시그널링에는 TCP(신뢰성이 중요)를 사용할 수 있습니다. 게임은 위치 업데이트에는 UDP를, 채팅에는 TCP를 사용하는 경우가 많습니다.
두 프로그램이 같은 포트를 사용하려 하면 어떻게 되나요?
두 번째 프로그램은 "address already in use" 오류로 실패합니다. 포트는 독점적입니다. 하나의 소켓만 특정 포트에 바인딩할 수 있습니다. (수신 대기 중인 소켓은 많은 연결을 수락할 수 있으며, 각 연결은 자체 소켓을 가집니다.) SO_REUSEADDR는 빠르게 재시작해야 하는 서버에 유연성을 제공합니다.
이 페이지가 도움이 되었나요?