리눅스 nc 명령어 사용법 - 포트 열림 확인과 임시 리스너 만들기
이 명령어를 언제 쓰는가
"방화벽 열어달라고 했는데 열린 건가?" 이 질문에 답하는 도구다. ping은 ICMP만 확인할 뿐 TCP 포트와는 무관하고, telnet은 종료가 번거롭다. nc는 지정한 포트로 TCP 연결이 실제로 맺어지는지를 한 줄로 확인하고 종료 코드까지 정직하게 돌려준다.
또 하나의 핵심 용도는 임시 리스너다. 애플리케이션을 배포하기 전에 "이 포트로 트래픽이 도달하긴 하나"를 미리 검증할 수 있다. 방화벽 담당자와 핑퐁하는 시간을 크게 줄여준다.
서버끼리 파일을 급히 옮기거나, HTTP·SMTP 같은 텍스트 프로토콜에 직접 말을 걸어보는 용도로도 쓴다. 여기서는 진단 용도만 다룬다.
기본 형식
nc [옵션] 호스트 포트 # 접속 모드
nc -l [옵션] 포트 # 리스닝 모드
- 접속 모드 — 지정한 호스트·포트로 TCP 연결을 시도하고, 표준 입력을 그대로 보낸다.
- 리스닝 모드 — 그 포트로 들어오는 연결을 받는다. 실제 서비스를 대신 세워보는 셈이다.
- 포트 인자는 하나뿐이다.
nc -zv host 20-25처럼 범위는 줄 수 있지만,nc -zv host 80 443처럼 떨어진 포트를 나열하면 netcat-openbsd는 두 번째 이후 인자를 조용히 무시한다. 여러 포트는 반복문으로 돌린다.
구현이 여러 개다
이름이 같아도 속은 다르다. 옵션이 안 먹으면 대부분 이 이유다.
| 구현 | 주로 쓰이는 곳 | 특징 |
|---|---|---|
| netcat-openbsd | Debian/Ubuntu 기본, macOS | -l 포트 형태. -z, -k, -N 지원 |
| nmap-ncat | RHEL/Rocky (nc가 이것) | ncat과 동일. SSL·프록시 기능이 있다 |
| netcat-traditional | 구형 Debian, 일부 임베디드 | 리스닝에 -l -p 포트가 필요. -e가 있어 보안상 기피된다 |
| busybox nc | Alpine 컨테이너 | 옵션이 매우 제한적. -z가 없을 수 있다 |
어느 것인지 모르겠으면 nc -h를 먼저 친다. 사용법 첫 줄만 봐도 구분된다.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
-z | 데이터를 주고받지 않고 연결만 시도(스캔 모드) | nc -z devin.kr 443 |
-v | 결과를 사람이 읽게 출력. -z와 항상 같이 쓴다 | nc -zv devin.kr 443 |
-w | 타임아웃(초). 안 붙이면 막힌 포트에서 오래 매달린다 | nc -zv -w 3 devin.kr 443 |
-l | 리스닝 모드 | nc -l 9000 |
-k | 연결이 끊겨도 리스너를 계속 유지 | nc -lk 9000 |
-u | TCP 대신 UDP | nc -zvu -w 2 10.0.0.5 53 |
-n | DNS 조회 생략 | nc -zvn 10.0.0.5 22 |
-4 / -6 | IPv4 / IPv6 강제 | nc -4zv devin.kr 443 |
-N | 입력이 끝나면 소켓을 닫는다(파일 전송 시 필요) | nc -N host 9000 < file |
실전 예제
1. 포트가 열려 있는지 확인한다. 방화벽 신청 후 검증에 그대로 쓰는 형태다.
nc -zv -w 3 devin.kr 443
Connection to devin.kr port 443 [tcp/https] succeeded!
닫혀 있거나 필터링되면 이렇게 나온다.
nc: connectx to devin.kr port 4567 (tcp) failed: Operation timed out
실패 메시지를 구분해서 읽는 것이 중요하다. Connection refused는 패킷이 도달했고 그 포트에 아무도 없다는 뜻이므로 경로는 정상이다. Operation timed out(리눅스에서는 보통 Connection timed out)은 응답 자체가 없다는 뜻으로 중간 방화벽이 조용히 버렸을 가능성이 크다. 문구는 구현마다 조금씩 다르지만 이 두 갈래의 의미는 같다.
2. 여러 포트를 한 번에 확인한다. 신규 서버 개통 점검에 쓴다.
for p in 22 80 443 3306; do
nc -zv -w 2 10.0.0.20 "$p"
done 2>&1 | grep -E 'succeeded|failed'
연속된 범위라면 반복문 없이 한 줄로 된다.
nc -zv -w 2 10.0.0.20 8000-8010
3. 스크립트에서 서비스 기동을 기다린다. 종료 코드가 정확해서 배포 스크립트에 그대로 넣을 수 있다.
for i in $(seq 1 30); do
if nc -z -w 1 127.0.0.1 8080; then
echo "app is up"
exit 0
fi
sleep 2
done
echo "timeout waiting for 8080" >&2
exit 1
4. 애플리케이션 없이 포트 도달 여부를 검증한다. 이것이 nc의 진짜 강점이다. 서버 A에서 리스너를 띄우고, 서버 B에서 접속해본다.
# 서버 A (수신 측)
nc -lk 9000
# 서버 B (송신 측)
echo "hello from B" | nc -w 3 서버A주소 9000
서버 A 화면에 hello from B가 찍히면 그 포트로의 경로와 방화벽이 모두 정상이다. 애플리케이션이 안 뜨는 것은 별개 문제로 분리된다. -k를 빼면 리스너가 첫 연결 후 종료되므로, 반복 시험에는 -k를 붙인다.
1024 미만 포트로 리스닝하려면 root 권한이 필요하다.
5. HTTP 응답을 직접 받아본다. 로드밸런서가 어느 백엔드로 보내는지, 리다이렉트가 어디로 걸리는지 볼 때.
printf 'GET / HTTP/1.1\r\nHost: devin.kr\r\nConnection: close\r\n\r\n' | nc -w 5 devin.kr 80
HTTP/1.1 301 Moved Permanently
content-length: 0
location: https://devin.kr/
connection: close
줄 끝이 \r\n이어야 하고 헤더 뒤에 빈 줄이 하나 더 있어야 한다. echo로는 이게 안 되므로 printf를 쓴다.
6. tcpdump와 짝지어 원인을 좁힌다. 서버 쪽에서 캡처를 띄워두고 클라이언트에서 nc로 두드린다.
# 서버 쪽
sudo tcpdump -i any -nn 'port 8080'
# 클라이언트 쪽
nc -zv -w 3 서버주소 8080
서버 캡처에 SYN이 아예 안 보이면 중간 구간에서 막힌 것이고, SYN은 보이는데 응답이 없으면 서버의 로컬 방화벽이 범인이다.
함정과 주의점
UDP 확인은 신뢰할 수 없다. nc -zvu는 UDP 패킷을 하나 보내고 ICMP Port Unreachable이 안 오면 "성공"이라고 판정한다. 그런데 방화벽이 그 ICMP를 막으면 닫힌 포트도 열렸다고 나온다. UDP는 실제 프로토콜 요청을 보내 응답을 받아봐야 확실하다. DNS라면 dig @대상 +time=2, NTP라면 ntpdate -q 같은 방식이다.
-w 없이 쓰면 스크립트가 멈춘다. 필터링된 포트는 OS 기본 TCP 재시도가 끝날 때까지, 리눅스에서 2분 이상 매달린다. cron이나 헬스체크에서는 반드시 -w를 붙인다.
리스너를 띄운 채 잊지 않는다. 검증용으로 nc -lk를 띄워두고 그대로 두면 그 포트를 계속 점유한다. 나중에 실제 애플리케이션이 뜨려다 Address already in use로 실패한다. 테스트가 끝나면 Ctrl+C로 확실히 내리고, 백그라운드로 돌렸다면 ss -tlnp | grep 포트로 확인한다.
구현에 따라 옵션이 다르다. netcat-traditional에서는 리스닝에 nc -l -p 9000처럼 -p가 필요하고, busybox nc에는 -z가 없을 수 있다. Alpine 컨테이너에서 -z가 안 먹는다면 이 경우다. RHEL의 nc는 nmap-ncat이라 성공 메시지 문구가 다르다.
-e 옵션은 쓰지 않는다. netcat-traditional의 -e는 접속 시 임의 명령을 실행하는 기능으로, 사실상 원격 셸을 여는 것과 같다. 많은 배포판이 이 이유로 해당 빌드를 기본 제외했다. 진단 목적에는 필요 없는 기능이며, 서버에 그런 것을 남겨서는 안 된다.
함께 보면 좋은 명령어
- ss — 서버 쪽에서 그 포트가 정말 LISTEN 중인지, 어느 주소에 묶였는지 확인한다.
- telnet — nc가 없는 서버에서 같은 포트 확인을 대신한다.
- tcpdump — nc로 두드린 패킷이 실제로 도달하는지 반대편에서 본다.
- curl — HTTP라면 nc보다 curl이 훨씬 정확하고 편하다.