소켓으로 에코 서버 만들기
HTTP는 소켓 위에 세워진 집입니다. 지금까지 그 집의 구조를 살펴봤다면, 이번 장에서는 집을 받치고 있는 기초, 즉 소켓을 직접 손으로 다뤄봅니다. Python socket 모듈은 운영체제의 소켓 시스템 콜을 그대로 노출합니다. PART 05에서 배운 TCP, 포트, 3-way 핸드셰이크가 코드 한 줄 한 줄과 직결됩니다.
에코 서버란
에코(echo) 서버는 클라이언트가 보낸 데이터를 그대로 돌려주는 서버입니다. 기능은 단순하지만, 소켓의 생애 주기 전체를 담고 있습니다. 생성, 바인딩, 리스닝, 수락, 수신, 송신, 종료. 이 흐름을 한 번 만들어 보면 이후 어떤 네트워크 프로그램도 같은 틀 위에서 이해할 수 있습니다.
에코 서버 코드
# 새 파일: echo_server.pyimport socketHOST = "127.0.0.1"PORT = 50007with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: server.bind((HOST, PORT)) server.listen() print(f"서버가 {HOST}:{PORT}에서 대기 중입니다.") conn, addr = server.accept() with conn: print(f"{addr}에서 연결되었습니다.") while True: data = conn.recv(1024) if not data: break conn.sendall(data)
코드 해설
socket.socket(socket.AF_INET, socket.SOCK_STREAM)이 소켓을 만드는 첫 단계입니다. AF_INET은 IPv4 주소 체계를 사용한다는 뜻입니다. SOCK_STREAM은 TCP 스트림 소켓을 선택합니다. PART 05의 소켓 설명에서 스트림 소켓은 TCP와 연결된다고 배웠습니다.
server.bind((HOST, PORT))는 이 소켓을 특정 주소와 포트에 묶습니다. 127.0.0.1은 루프백 주소로, 같은 컴퓨터에서만 접속 가능합니다. PORT 50007은 IANA 등록 범위(0~1023) 밖의 자유 포트입니다.
server.listen()은 운영체제에게 "이 소켓으로 연결 요청을 받을 준비가 됐다"고 선언합니다. 이 순간부터 TCP 3-way 핸드셰이크 요청이 오면 운영체제가 대기열에 쌓아 둡니다.
conn, addr = server.accept()는 대기열에서 연결 하나를 꺼내 수락합니다. conn은 이 클라이언트와의 전용 통신용 소켓이고, addr는 클라이언트의 IP 주소와 포트 번호입니다. accept는 연결이 들어올 때까지 블로킹됩니다. 클라이언트가 올 때까지 이 줄에서 멈춥니다.
conn.recv(1024)는 최대 1024바이트를 수신합니다. 클라이언트가 연결을 끊으면 recv가 빈 바이트열(b"")을 반환합니다. 그것을 if not data: break로 감지해 루프를 빠져나옵니다.
conn.sendall(data)는 받은 데이터를 그대로 돌려보냅니다. send는 일부만 전송하고 반환할 수 있지만, sendall은 데이터 전체를 보장 전송합니다.
with 블록은 파일 with open()처럼 블록이 끝나면 소켓을 자동으로 닫습니다. 닫는 순간 TCP 4-way 종료 과정이 시작됩니다.
서버 실행
# 파일: 터미널python echo_server.py
서버가 127.0.0.1:50007에서 대기 중입니다.
이 상태로 클라이언트를 기다립니다. 다음 절에서 클라이언트를 만들어 연결해 봅니다.