TCP 연결 단계 (RFC 793)
TCP A TCP B
1. CLOSED LISTEN
2. SYN-SENT –> < SEQ=100>< CTL=SYN> –> SYN-RECEIVED
3. ESTABLISHED <– < SEQ=300>< ACK=101>< CTL=SYN,ACK> <– SYN-RECEIVED
4. ESTABLISHED –> < SEQ=101>< ACK=301>< CTL=ACK> –> ESTABLISHED
5. ESTABLISHED –> < SEQ=101>< ACK=301>< CTL=ACK>< DATA> –> ESTABLISHED

LISTEN : 데몬이 요청을 발을 수 있도록 연결 요구를 기다리는 상태.
즉, http(80), mail(25), ftp(21), telnet(23) 등의 포트가 열려있음을 의미.
윈도우즈에서는 LISTENING으로 표시.

SYN_SENT : 로컬에서 원격으로 연결 요청(SYN 신호를 보냄)을 시도한 상태.

SYN_RECV : 원격으로 부터 연결 요청을 받은 상태.
요청을 받아 SYN+ACK 신호로 응답은 한 상태이지만 ACK는 받지 못했다.
netstat로 확인할 때 SYN_RECV가 아주 많다면 TCP SYN 플러딩(Flooding) 공격일 가능성이 있다.
윈도우즈와 솔라리스에서는 SYN_RECEIVED으로, FreeBSD는 SYN_RCVD으로 표시.

ESTABLISHED : 서로 연결이 되어 있는 상태
위에서 192.168.123.10의 포트 32794과 218.xxx.xx.xx의 포트 22(ssh)이 서로 연결되어 있는 상태.

TCP 종료 단계
* 정상적인 연결 종료 과정
TCP A TCP B
1. ESTABLISHED ESTABLISHED
2. (Close)
FIN-WAIT-1 –> <SEQ=100><ACK=300><CTL=FIN,ACK> –> CLOSE-WAIT
3. FIN-WAIT-2 <– <SEQ=300><ACK=101><CTL=ACK> <– CLOSE-WAIT
4. (Close)
TIME-WAIT <– <SEQ=300><ACK=101><CTL=FIN,ACK> <– LAST-ACK
5. IME-WAIT –> <SEQ=101><ACK=301><CTL=ACK> –> CLOSED
6. (2 MSL)
CLOSED

FIN_WAIT1 : 소켓이 닫히고 연결이 종료되고 있는 상태. 원격의 응답은 받을 수 있다.
솔라리스에서는 FIN_WAIT_1로 표시.

FIN_WAIT2 : 로컬이 원격으로 부터 연결 종료 요구를 기다리는 상태.
솔라리스에서는 FIN_WAIT_2로 표시.

CLOSE_WAIT : 원격의 연결 요청을 받고 연결이 종료되기를 기다리는 상태 .
원격으로 부터 FIN+ACK 신호를 받고 ACK 신호를 원격에 보냈다.

TIME_WAIT : 연결은 종료되었으나 원격의 수신 보장을 위해 기다리고 있는 상태.
이 상태를 특히 자주 보게되는 경우 => Apache에서 KeepAlive를 OFF로 해둔 경우, Tomcat 서버를 쓰는 경우 등.
LAST_ACK : 연결은 종료되었고 승인을 기다리는 상태.
CLOSED : 완전히 연결이 종료된 상태.
※ 위의 FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT 3개 상태는
연결 종료를 위해 서로간에 신호를 주고받는 과정에 나타나는 상태로 이해하면 된다.
종료 요청을 한 곳에서는 FIN_WAIT1, FIN_WAIT2, TIME_WAIT 상태가,
종료 요청을 받는 곳에서는 CLOSE_WAIT, LAST_ACK 상태가 표시된다.


링크 : http://dustin-hyun.blogspot.com/2008/06/tcp-status.html

TCP Status

TCP연결시에는 알다시피 3way handshaking을 한다. close시에는 client, server 가 각각 FIN을 보낸 후 ACK를 받는다(총 4번의 트랜젝션).

TCP연결에는 여러개의 상태가 있는데, netstat으로 확인할 수 있는 상태들에 대해 간단히 정리해보면,

  • 서버에서 listen()이 호출되면 상태는 LISTEN으로 바뀐다.
  • 이 때 client에서 SYN을 보낸 후 SYN_SENT로 상태가 바뀌면
  • 서버에서 이를 받고 SYN_RCVD로 바뀌고 SYN ACK를 보내준다.
  • 그리고 client에서 SYN을 다시 받게 되면 비로소 ESTABLISHED로 바뀐다.
  • 이 후 send(), recv()군의 함수들로 계속 통신을 하다가
  • client에서 close()호출로 인해 FIN 을 보내고 FIN_WAIT1상태가 되면
  • 서버에서 이를 받고 ACK를 보낸 후 CLOSE_WAIT으로 상태를 바꾸고
  • client에서는 이를 받으면 FIN_WAIT2로 바뀌게 된다.
  • client에서 close할 준비가 끝났으므로, 이제는 서버에서 close()가 호출되면 FIN이 전송되고, 상태는 LAST_ACK이 된다.
  • client에서는 ACK를 보낸 후 TIME_WAIT상태가 되고, Maximum Segment Lifetime x 2시간 정도 기다리다가 소켓을 닫는다.
  • 서버에서는 이 ACK를 받고 CLOSED로 상태가 바뀐다.

close시 일반적으로 client에서 먼저 FIN을 보내고(active close), 서버는 이를 받으면 recv()에 EOF가 전달된다. 서버는 자신이 보낸 FIN에 대한 ACK를 받아야 close된다(passive close).

TIME_WAIT이 필요한 이유 client에서 ACK를 보냈는데, 서버가 못받으면, 서버는 LAST_ACK상태에서 일정시간이 지나도 ACK가 안오므로 FIN을 다시 보낸다. 이때 client가 이미 close 한 상태이거나 같은 포트번호로 다른 접속을 시도하고 있다면 처리할 수 없으니까, TIME_WAIT을 두어 다시 들어온 FIN에 대해 ACK를 주는 것이다. (client에서는 그래서 재접속시 다른 포트 번호를 받게 된다).