Devin.KR

TCP·UDP와 소켓

100분 안팎

학습 목표

학교 웹 TCP 연결과 DNS UDP 질의를 관찰하고 TCP 연결 상태를 확인합니다.

개념

연결 성공과 웹 성공을 분리합니다

학교 서버까지 ping이 되지만 웹 화면이 열리지 않는 상황을 맡았습니다. 앞 모듈의 왕복 IP 경로가 정상이어도 서버 프로그램이 포트를 열지 않았거나 HTTP 처리가 실패할 수 있습니다. 이번 레슨은 주소와 경로 위에서 프로그램의 끝점을 찾아가는 작업입니다. 웹 요청의 TCP 연결과 DNS 질의의 UDP 소켓을 구분하고 ss로 실제 연결 상태를 확인합니다. 통신 도구가 성공했다고 업무가 성공한 것으로 보고하지 않습니다.

이번 모듈의 공통 실습 환경

각 starter.zip은 m04 미션 solution 전체를 출발점으로 포함합니다. Linux VM에서만 sudo bash check.sh를 실행합니다. 호스트 인터페이스를 브리지에 연결하지 않습니다. 맥에서는 압축을 푼 루트에서 python3 test_services.py로 설정 검사를 실행합니다. 실제 패킷 검사는 external 확인 대기이며 아래 Linux 단계의 output은 비어 있습니다. 준비 도구와 소유 네임스페이스 정리 규칙은 README.md를 따릅니다.

두 주소와 두 포트가 연결을 구분합니다

TCP의 같은 프로토콜 안에서는 출발지 IP·출발지 포트·목적지 IP·목적지 포트가 연결의 양 끝을 나타냅니다. 학생 두 명이 학교 서버의 8080 포트를 공유해도 출발지 끝점이 달라 연결을 구분합니다. 서버의 수신 포트 하나가 동시에 연결 하나만 처리한다는 뜻은 아닙니다. 클라이언트 출발지 포트는 운영체제가 선택할 수 있으므로 실측 자료에는 숫자를 그대로 남기되 특정 값으로 채점하지 않습니다.

TCP는 순서 있는 바이트 흐름입니다

TCP는 연결을 수립하고 바이트의 순서를 맞추며 손실에 대한 복구를 시도합니다. 프로그램이 한 번 보낸 문자열을 상대가 한 번의 읽기로 얻는다는 보장은 없습니다. HTTP도 헤더와 본문 경계를 자체 규칙으로 해석합니다. 이번 과제는 서버 구현이 아니라 제공 앱의 연결 관측입니다. send 호출이 성공했다는 로그를 페이지 생성이나 데이터 저장 완료의 근거로 삼으면 안 됩니다. 업무 성공에는 해당 응답을 추가로 확인합니다.

UDP의 단위는 데이터그램입니다

UDP는 메시지의 경계를 유지하지만 기본 프로토콜 자체가 도착과 순서를 보장하지 않습니다. DNS 질의 프로그램은 응답을 기다리고 필요하면 다시 물어볼 책임이 있습니다. UDP 소켓을 열었다고 상대 프로그램이 준비됐다는 확인 절차를 수행한 것은 아닙니다. 따라서 ss의 UDP 소켓 표시와 DNS의 유효한 응답 수신을 별도 증거로 적습니다. UDP가 연결 수립을 생략한다는 특징만으로 모든 업무에서 더 빠르다고 평가하지 않습니다.

DNS가 항상 UDP만 쓰는 것은 아닙니다

이 실습의 작은 A 질의는 UDP 53을 사용합니다. 실제 DNS는 TCP를 쓰기도 하며 답변 크기와 서버 기능에 따라 전송 방식이 달라질 수 있습니다. 제공 교육 서버는 UDP A 조회만 구현하므로 dig +tcp의 성공을 기대하지 않습니다. TCP 8080의 웹과 UDP 53의 DNS가 같은 장비에 있어도 서로 다른 소켓입니다. 정책을 작성할 때 포트 숫자뿐 아니라 전송 프로토콜까지 기록하는 이유가 여기에 있습니다.

LISTEN과 ESTABLISHED를 나눠 읽습니다

LISTEN은 서버가 연결 요청을 받을 준비를 한 소켓입니다. ESTABLISHED는 연결된 흐름이 존재한다는 상태입니다. ss -ltn은 수신 포트를, ss -tn state established는 현재 연결을 보여줍니다. 상태는 명령을 실행한 시점의 관측입니다. 짧은 curl 요청이 끝난 뒤 조회하면 연결이 이미 사라질 수 있으므로 제공 transport_probe.py는 연결을 연 상태에서 ss를 실행하고 확인을 마친 뒤 스스로 닫습니다.

끝점을 표로 정리합니다

학교 웹은 server1의 10.20.30.11:8080 TCP, HTTPS는 같은 주소의 8443 TCP, DNS는 r1의 10.20.30.1:53 UDP입니다. DHCP는 학생 VLAN의 r1 인터페이스에서 UDP 67을 받고 단말의 UDP 68로 응답합니다. 포트는 이 가상 학교의 실습 계약이며 모든 웹 서버가 이 숫자를 쓴다는 뜻은 아닙니다. 서비스 이름·주소·포트·전송 방식·검사 명령을 한 행씩 연결하면 다른 담당자가 동일 조건으로 확인할 수 있습니다.

설정 실패를 먼저 해결합니다

starter의 services.json에는 웹 전송 방식이 udp로 적혀 있습니다. 제공 웹 서버는 TCP 수신자이므로 web_protocol을 tcp로 고치고 DNS의 dns_protocol은 udp로 유지합니다. python3 test_services.py의 FAIL: transport가 사라지는지 확인합니다. 이 검사는 문서 계약을 비교하며 실제 연결은 만들지 않습니다. PASS를 보았다는 이유로 운영체제 소켓까지 확인했다고 쓰지 않고 Linux 검사의 ESTABLISHED 증거를 별도로 요구합니다.

연결 거부 메시지를 해석합니다

Connection refused는 연결 시도가 거절됐다는 단서입니다. 이번 검사에서는 수신자가 없는 TCP 8099를 사용해 재현합니다. 서버 프로그램 미기동이나 거절 정책 같은 원인 후보를 조사할 수 있지만 문자열 하나만으로 원인을 확정하지 않습니다. 먼저 어느 주소와 포트에 시도했는지 적고 해당 네임스페이스에서 LISTEN을 확인합니다. DNS 이름이 잘못된 주소를 반환했다면 정상 서버의 상태를 보아도 문제가 풀리지 않습니다.

시간 초과는 침묵의 위치를 알려주지 않습니다

제한 시간 안에 결과를 받지 못한 경우에는 경로·필터·서버 지연을 단계별로 나눕니다. 제공 8081 수신자는 TCP를 받아도 HTTP 바이트를 보내지 않습니다. 따라서 여기서 curl 28은 연결 불가가 아니라 응답 대기 중 시간 초과를 재현합니다. SYN 자체에 답이 없는 시간 초과와 같은 원인이라고 쓰지 않습니다. 연결 시간과 전체 요청 시간을 구분하는 옵션을 사용하고 실제 실패 시점은 상세 로그와 함께 판단합니다.

종료 상태를 성급히 고치지 않습니다

TIME_WAIT는 종료 후 프로토콜 상태가 일정 시간 남는 경우입니다. CLOSE_WAIT는 상대 종료를 받은 뒤 로컬 종료가 남아 있다는 단서입니다. 한 시점의 개수만으로 누수라고 진단하지 않습니다. 이번 실습에서는 상태를 강제로 지우거나 프로세스를 kill하지 않습니다. 제공 서비스는 35초 뒤 스스로 종료하고 검사기가 wait로 기다립니다. 실행 중인 서비스가 남으면 앞 모듈 cleanup.sh가 정리를 거절하므로 자연 종료 후 다시 확인합니다.

관측 범위를 보고합니다

services.csv의 TCP 행에는 ESTABLISHED 확인과 수신자 없는 포트의 거부를 따로 기록합니다. DNS 응답과 HTTP 200은 각각 자기 단계의 행입니다. TCP 성공만 적은 보고서에는 TLS 인증과 페이지 응답이 확인되지 않았다고 덧붙입니다. 동료에게 학생 주소에서 어느 서버 끝점으로 연결했는지 설명하고 주소·포트·전송 방식 중 하나가 바뀌면 같은 시험이 아니라고 말할 수 있어야 합니다. 이 기록이 다음 레슨의 주소 배포 검사 기준이 됩니다.

실습 결과를 재현 가능한 자료로 남깁니다

검사 실행 전에 services.json을 보존하고 실행 후 services.csv와 환경 정보를 함께 제출합니다. 예측한 값과 실측한 값을 같은 열에 혼합하지 않습니다. Linux를 실행하지 못했다면 설정 검사 통과와 패킷 확인 대기를 적습니다. Permission denied는 소켓 정책 문제일 수도 있으므로 관리자 권한과 지정 네임스페이스를 먼저 확인합니다. FileNotFoundError: services.json은 압축을 푼 루트가 아닌 폴더에서 실행했을 때도 발생하므로 작업 위치부터 점검합니다.

기술 확인: TCP의 바이트 흐름과 연결 상태의 공식 명세를 참고했습니다. 설명과 학교망 예제는 이 레슨의 과제에 맞춰 작성했습니다.

따라하기

소켓 종류를 구분합니다

패킷 관측이 아닌 결정적인 계산 예제입니다. 이 출력의 범위만 판단합니다.

import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
u=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
print('web',s.type==socket.SOCK_STREAM)
print('dns',u.type==socket.SOCK_DGRAM)
s.close();u.close()

실행 결과

web True
dns True

수정한 설정을 검사합니다

압축을 푼 루트에서 starter의 잘못된 필드를 수정합니다. 다음 출력은 solution 설정을 실제 실행한 결과입니다. 서비스 패킷 전송의 증거는 아닙니다.

python3 test_services.py

실행 결과

PASS: transport
PASS: web-name
PASS: pool
PASS: reservation
PASS: gateway
PASS: dns
PASS: record
PASS: timers

Linux에서 실제 서비스를 검증합니다

다음 명령은 Linux 외부 검증 대기입니다. check.sh가 실제 TCP 연결 상태와 UDP DNS 응답을 확인합니다. 상세 수치와 메시지는 실행 후 생성된 services.csv로 확인합니다. 서비스 관측 명령은 35초 실행 중에 사용하며 종료 뒤에는 망이 정리됩니다.

sudo bash check.sh
# check.sh의 서비스가 실행 중인 동안 별도 터미널에서 관측합니다.
sudo ip netns exec bcnet-server1 ss -ltn
sudo ip netns exec bcnet-s1 python3 transport_probe.py

판단 근거를 제출합니다

services.csv의 ESTABLISHED와 REFUSED 행을 비교합니다. 학생·서버 끝점과 연결 상태 관측 시각을 적습니다. 서버 포트만으로 연결을 세지 않습니다.

확인 문제

실습

starter.zip을 푼 루트에서 services.json의 web_protocol을 본문의 학교망 계약에 맞춰 수정합니다. python3 test_services.py로 일부 실패를 확인한 뒤 수정 결과를 검사합니다. Linux VM에서 sudo bash check.sh로 실제 단계 증거를 services.csv에 생성합니다. README의 검사 범위와 앱 제약을 보고서에 적고 미확인은 대기로 표시합니다.

시작 코드·테스트 내려받기

실행 명령

sudo bash check.sh

기대 결과

설정 검사 8건과 Linux의 DHCP·DNS·TCP·TLS·HTTP 검사가 통과하고 소유한 망을 정리합니다.

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • DNS 오류와 서버 연결 오류를 구분하는 방법을 설명합니다.