Posts WEB - TCP vs UDP, 그리고 네트워크 진단 도구들
Post
Cancel

WEB - TCP vs UDP, 그리고 네트워크 진단 도구들

네트워크 팀과 소통하다 보면 “그 서버로 telnet 한번 때려보세요”, “traceroute 결과 좀 주세요” 같은 얘기를 자주 듣는다. 그런데 정작 그 도구가 뭘 하는 도구인지, 왜 그걸 쓰라는 건지 모르면 결과를 봐도 해석을 못 한다. 특히 네트워크 팀에서 “ACL 열어드렸어요”라고 했는데 telnet이 안 되면, 이게 네트워크가 안 열린 건지, 목적지 서버 문제인지 헷갈린다. 이 글에서는 TCP/UDP의 차이부터 시작해서, 상황별로 어떤 진단 도구를 왜 쓰는지 정리한다.

먼저 TCP vs UDP


진단 도구를 이해하려면 TCP와 UDP의 차이를 먼저 알아야 한다.

도구마다 “TCP만 되는 것”, “UDP도 되는 것”이 갈리고, 그 이유가 전부 이 차이에서 나오기 때문이다.

 TCPUDP
연결연결 지향 (3-way handshake로 연결을 맺고 시작)비연결 (그냥 보냄)
신뢰성상대가 받았는지 확인(ACK)하고 유실 시 재전송, 순서 보장전송 계층은 도착 보장·재전송을 안 함 (필요하면 앱이 직접 처리)
대표 사용처HTTP, DB, SSH 등 대부분DNS, 실시간 스트리밍, 일부 로그 전송

핵심은 TCP는 통신 시작 전에 “연결”이라는 단계를 거친다는 점이다.

1
2
3
4
5
[TCP 3-way handshake]

출발지 ---- SYN ----> 목적지     "연결하자"
출발지 <-- SYN/ACK -- 목적지     "그래 하자"
출발지 ---- ACK ----> 목적지     "좋아 시작"

이 handshake 덕분에 TCP는 “포트가 열려 있나?”를 연결을 시도해보는 것만으로 확인할 수 있다. 상대가 SYN에 응답하면 열린 것, 거부하면 닫힌 것이다. 뒤에 나올 telnet이 바로 이 성질을 이용한다.

반면 UDP는 handshake가 없다. 그냥 패킷을 던질 뿐이고, 상대가 응답할 의무도 없다. 그래서 “이 UDP 포트가 열려 있나?”를 확인하기가 근본적으로 어렵다. 응답이 없을 때 그게 ① 포트는 열렸지만 앱이 UDP라 굳이 답을 안 한 건지, ② 방화벽에서 패킷이 버려진 건지 구분할 수 없기 때문이다. (TCP라면 열린 포트는 앱이 뭘 하든 커널이 SYN/ACK로 반드시 응답하지만, UDP엔 그런 자동 응답이 없다. 그래서 telnet으로는 UDP를 진단할 수 없다 — telnet은 TCP handshake 전용이다.)

왜 HTTP는 TCP, DNS는 UDP를 쓸까

“UDP는 응답이 없다”가 아니라 “응답을 프로토콜 차원에서 보장하지 않는다”는 뜻이다. DNS도 당연히 도메인에 대한 IP 응답을 받아야 하고 실제로 받는다. 차이는 그 신뢰성을 누가 책임지느냐다. TCP는 전송 계층이 알아서 재전송·순서를 챙기지만, UDP는 “응답을 기다리고 안 오면 재시도하는” 로직을 애플리케이션이 직접 구현해야 한다.

  • HTTP가 TCP인 이유: HTTP는 HTML·이미지·JSON 같은 콘텐츠를 주고받는데, 이건 여러 패킷에 나뉘고 하나라도 빠지거나 순서가 틀리면 내용이 깨진다. 완전하고 순서 맞는 전달이 필수라서, 그걸 전송 계층이 보장해주는 TCP가 맞다. 연결 수립 오버헤드는 큰 전송 전체로 나눠지니 감수할 만하다.
  • DNS가 UDP인 이유: DNS는 보통 작은 질의 하나 보내고 응답 하나 받으면 끝이다. 이 짧은 1회성 통신에 매번 TCP 핸드셰이크(왕복 1.5번)를 치르는 건 낭비다. 그래서 가볍고 빠른 UDP를 쓰고, 응답을 못 받으면 리졸버가 스스로 재질의하거나(응답이 너무 크면) TCP로 폴백한다.
1
2
TCP:  SYN → SYN/ACK → ACK  (연결에만 왕복 1.5번)  → 요청 → 응답 ...
UDP:  질의 →  응답            (딱 왕복 1번)

정리하면, 주고받는 데이터가 크고 정확·순서가 중요하면 TCP(HTTP), 짧고 가볍고 약간의 유실을 앱이 감당할 수 있으면 UDP(DNS). 도구가 TCP/UDP로 갈리는 이유도 결국 여기서 나온다.

그 순서·재전송은 어디서 보장해줄까 — 라우터가 아니다

TCP의 순서 보장·재전송은 중간 네트워크 장비(라우터)가 아니라, 양 끝 호스트의 OS 커널 안에 있는 TCP 스택이 해준다.

  • 라우터는 L3(IP) 담당이라, 각 패킷을 독립적으로 포워딩할 뿐이다. 혼잡하면 그냥 버리기도 하고, 경로가 갈리면 순서가 뒤섞이기도 한다. 이 패킷들이 하나의 TCP 연결에 속하는지 알지도, 신경 쓰지도 않는다.
  • TCP는 L4(전송) 담당이고 양 끝 호스트에만 존재한다. TCP는 보내는 바이트마다 시퀀스 번호를 붙여서, 뒤섞여 도착해도 받는 쪽이 버퍼에서 순서를 맞추고, ACK가 안 오는 데이터는 보낸 쪽이 재전송한다.

즉 신뢰성은 네트워크 중간이 아니라 양 끝점에서 책임진다(end-to-end 원칙). 라우터가 패킷을 아무리 흘려도 양 끝 TCP가 복구해준다. UDP는 이 L4 로직이 없으니 라우터가 흘린 게 그대로 앱에 전달될 뿐이다.

telnet — 특정 포트로 TCP 연결이 되나?


telnet 목적지 포트 는 그 포트로 TCP 3-way handshake를 딱 한 번 시도하는 도구다.

원래는 원격 접속용 프로토콜이지만, 특정 포트로 TCP 연결이 되는지 확인하는 용도로도 쓴다.

1
telnet 10.0.1.20 8080

결과는 세 갈래로 갈리고, 각각이 서로 다른 원인을 가리킨다.

① 연결 성공Connected to 10.0.1.20 → 네트워크 경로도 뚫려 있고, 목적지에서 그 포트로 애플리케이션이 리슨 중이다. 정상.

② 즉시 거부Connection refused (바로 튕김) → 패킷이 목적지 서버까지 도달은 했다는 뜻이다. 서버가 “그 포트엔 아무도 없다”며 RST를 돌려보낸 것이다. → 즉 네트워크 ACL은 열려 있고, 목적지에 그 포트로 뜬 애플리케이션이 없다(앱이 안 떴거나 다른 포트에 떠 있다). → 서버/앱 쪽에서 볼 문제.

③ 타임아웃Connection timed out (한참 멈춰 있다가 실패) → SYN을 보냈는데 아무 응답도 안 온다. 중간의 방화벽/ACL이 패킷을 조용히 버린(drop) 것이다. → 네트워크(방화벽/ACL) 문제일 가능성이 높다. (거부는 “누가 응답이라도 해준” 것, 타임아웃은 “아무도 대답조차 안 한” 것)

Connection refused = 네트워크는 뚫렸다(→ 서버/앱 문제), Connection timed out = 네트워크에서 막혔을 가능성. 네트워크 팀이 “ACL 열었다”고 했는데 telnet이 안 될 때, 이 둘 중 어느 쪽인지부터 확인하면 누구에게 물어봐야 할지가 갈린다.

nc (netcat) — telnet으로 안 되는 것까지


nc(netcat)는 telnet의 상위호환이다.

TCP뿐 아니라 UDP도 되고, 연결 시도만 하는 게 아니라 직접 포트를 열어 리슨하거나 데이터를 전송할 수도 있다.

포트 확인 (telnet 대체)

1
nc -zv 10.0.1.20 8080
  • -z : zero-I/O 모드. 애플리케이션 데이터(페이로드)는 안 보내지만 TCP 연결 자체는 telnet처럼 3-way handshake로 끝까지 맺어본다. 연결이 맺어지면 열린 것으로 보고 바로 끊는다. (처음에 헷갈렸던 nz가 바로 이 nc -z다)
  • -v : 결과를 자세히 출력

UDP 확인

1
nc -zvu 10.0.1.20 514
  • -u : UDP로 시도한다. 단 앞서 말했듯 UDP는 응답이 없을 수 있어 결과가 애매하다(open인지 filtered인지 확신하기 어렵다). 확실히 하려면 목적지에서 패킷이 실제로 수신되는지 tcpdump로 같이 봐야 한다.

포트를 열어 리슨 — 네트워크 경로만 순수하게 테스트

telnet ②번(Connection refused) 상황에서, 목적지에 아직 앱이 배포되지 않았으면 네트워크가 열려 있어도 refused가 나서 ACL이 열렸는지 확인할 수 없다. 이때 목적지 서버에 nc로 임시 리슨을 띄우면 “앱 유무” 변수를 제거하고 네트워크 경로만 검증할 수 있다.

1
2
3
4
5
# 목적지 서버에서: 8080 포트를 임시로 열어둔다
nc -l 8080

# 출발지 서버에서: 그 포트로 연결이 되는지 확인
nc -zv 10.0.1.20 8080

→ 여기서 연결이 되면 ACL은 확실히 열린 것이다. 앱 배포 전에 네트워크만 먼저 검증할 때 쓴다.

ss / netstat — 내 서버에서 그 포트가 리슨 중인가


지금까지는 전부 “출발지에서 목적지로 쏴보는” 도구였다.

ss(와 옛날 도구 netstat)는 반대로, 지금 이 서버에서 어떤 포트가 열려(리슨) 있는지를 본다. telnet ②번(Connection refused)의 짝이다 — refused가 나왔다면 “목적지에 그 포트로 뜬 앱이 없다”는 뜻인데, 그걸 목적지 서버에 직접 들어가서 확인할 수 있다.

동작이 앞의 도구들과 근본적으로 다르다. ss네트워크로 패킷을 하나도 안 보낸다. 대신 커널이 들고 있는 소켓 테이블을 그대로 읽어 보여준다(ss는 netlink, netstat/proc/net/tcp 같은 파일). 그래서 “지금 이 서버가 실제로 리슨 중인 포트”를 네트워크 왕복 없이 즉시 알 수 있다.

1
2
3
ss -tlnp              # 리슨 중인 TCP 포트 전부
ss -tlnp | grep 8080  # 8080을 리슨 중인 프로세스가 있나
ss -ulnp              # 리슨 중인 UDP 포트
  • -t TCP / -u UDP, -l 리슨 중인 것만, -n 이름 해석 없이(숫자 그대로), -p 프로세스명까지

언제 쓰나

  • 출발지에서 telnet이 refused다 → 목적지 서버에 들어가 ss -tlnp | grep 8080 → 아무것도 안 나오면 앱이 그 포트로 안 떠 있는 것(확정). 다른 포트로 떠 있으면 포트 설정 실수.
  • 0.0.0.0:8080으로 떠 있으면 모든 IP에서 접근 가능, 127.0.0.1:8080(localhost)으로만 떠 있으면 외부에서는 접근 불가 — 이것도 refused의 흔한 원인이다.

netstat -tlnp도 같은 일을 하지만, 요즘 리눅스에선 더 빠른 ss가 표준이다. (netstat은 net-tools 패키지라 없는 서버도 많다)

traceroute — 목적지까지 경로, 어디서 끊기나


telnet/nc가 “목적지 포트가 열렸나”라는 최종 결과만 알려준다면, traceroute는 출발지에서 목적지까지 거쳐 가는 라우터(홉)들을 하나씩 보여준다.

“어디까지 갔다가 막히는가”를 볼 때 쓴다.

tracerouteTTL(Time To Live)을 이용한다. 패킷의 TTL은 홉을 하나 지날 때마다 1씩 줄고 0이 되면 버려지는데, 이걸 역이용한다.

1
2
3
TTL=1 로 전송 → 첫 번째 라우터에서 0이 되어 버려짐 → 그 라우터가 "시간 초과" 응답 → 1번 홉 IP 획득
TTL=2 로 전송 → 두 번째 라우터에서 버려짐 → 2번 홉 IP 획득
TTL=3 ... 목적지에 닿을 때까지 반복
1
traceroute 10.0.1.20

읽는 법

  • 홉이 목적지까지 쭉 이어지면 경로 정상.
  • 중간부터 * * *만 뜨고 멈추면, 그 지점 이후로 막힌 것이다(방화벽이 차단했거나 경로가 없음).
  • 특정 홉부터 응답 시간이 확 커지면 그 구간이 병목(지연)이다.

주의: 방화벽이 traceroute용 패킷(기본 UDP/ICMP)만 막고 실제 서비스 포트(TCP)는 열어둔 경우, * * *가 떠도 정작 서비스는 정상일 수 있다. 그래서 경로 확인은 traceroute, 포트 확인은 telnet/nc로 역할이 다르다. 특정 TCP 포트 기준으로 경로를 추적하려면 traceroute -T -p 8080 처럼 TCP 모드를 쓴다.

nmap — 열린 포트/서비스 전체를 훑는다


telnet/nc가 “이 포트 하나 열렸나”라면, nmap은 “이 호스트에 열린 포트가 뭐뭐 있나”를 한 번에 훑는 포트 스캐너다.

“훑는다”는 건, 포트 하나하나에 SYN 패킷을 보내고 응답을 보는 동작을 포트 범위만큼 반복하는 것이다. SYN/ACK가 오면 열림, RST가 오면 닫힘, 아무 응답도 없으면 필터링(방화벽)으로 판단한다. 기본 SYN 스캔은 열린 걸 확인해도 handshake를 끝까지 맺지 않고 바로 RST로 끊어버려서(half-open), telnet/nc가 매번 연결을 완성하는 것보다 빠르다.

1
2
3
4
nmap -p 1-1000 10.0.1.20     # 1~1000번 포트 스캔
nmap -p 8080 10.0.1.20       # 특정 포트만
nmap -sV 10.0.1.20           # 열린 포트의 서비스/버전까지 탐지
nmap -sU -p 514 10.0.1.20    # UDP 스캔

언제 쓰나

  • 여러 포트를 한꺼번에 확인하고 싶을 때.
  • 목적지에 어떤 서비스가 떠 있는지 모를 때 전체를 파악.
  • 방화벽 정책 검증 — 열려야 할 포트만 열리고 나머진 닫혔는지.

주의: 포트 스캔은 보안 장비가 침입 시도로 감지할 수 있다. 사내 서버라도 권한/합의 없이 대량 스캔하지 말 것. UDP 스캔(-sU)은 앞서 말한 UDP 특성 때문에 느리고 결과도 부정확하다.

tcpdump — 마지막 수단: 패킷을 직접 본다


앞의 도구들이 “연결이 되나 / 포트가 열렸나”를 판단해준다면, tcpdump는 실제로 오가는 패킷 자체를 캡처해서 보여준다.

가장 저수준이고, 위 도구들로 원인이 안 잡힐 때 쓴다.

ss처럼 이 도구도 트래픽을 만들어내지 않는다. 커널의 패킷 필터(BPF)에 필터 조건을 걸어두고, NIC를 드나드는 프레임 중 조건에 맞는 것의 복사본을 그대로 떠서 보여줄 뿐이다(수동 캡처). 그래서 telnet/nc가 만든 실제 패킷이 이 서버까지 왔는지를 있는 그대로 관찰할 수 있다.

1
2
3
tcpdump -i eth0 port 8080          # eth0에서 8080 포트 트래픽
tcpdump -i any host 10.0.1.20      # 특정 호스트와 오가는 트래픽
tcpdump -i any port 8080 -n        # 이름 해석 없이(-n) 빠르게

이게 telnet의 추측을 ‘사실’로 확정해준다. 앞에서 telnet 결과로 “네트워크 문제일 것 같다 / 서버 문제일 것 같다”를 추측했는데, tcpdump양쪽 서버에서 동시에 찍으면 확정할 수 있다.

  • telnet이 timeout인데 목적지에서 tcpdump를 떠보니 SYN도착조차 안 함 → 중간 네트워크에서 막힌 게 확실하다. (네트워크 팀에게)
  • SYN은 도착했는데 서버가 RST를 돌려보냄 → 네트워크는 뚫렸고 앱 문제 확정이다. (서버/앱 담당에게)

즉 “패킷이 실제로 여기까지 왔는가”를 눈으로 확인해서, telnet의 refused/timeout 추측을 근거 있는 사실로 바꿔준다.

정리 — 상황별 도구 선택


이럴 때이 도구
이 TCP 포트로 연결돼?telnet, nc -zv
(내 서버에서) 그 포트가 리슨 중인가?ss -tlnp
UDP 포트를 확인하고 싶어nc -u, nmap -sU (+ tcpdump로 실제 수신 확인)
앱 배포 전에 네트워크(ACL)만 검증목적지 nc -l + 출발지 nc -zv
목적지까지 경로가 어디서 끊겨?traceroute
열린 포트/서비스 전체를 파악nmap
위로도 원인이 안 잡혀, 패킷을 직접 봐야겠어tcpdump
This post is licensed under CC BY 4.0 by the author.