옵션의 공식적인 의미를 이해하고 있다고 생각합니다. 지금 처리하고있는 일부 레거시 코드에서는 옵션이 사용됩니다. 고객이 측면에서 가까운 연결에서 FIN에 대한 응답으로 RST에 대해 불평합니다.
언제 사용해야하는지 모르겠 기 때문에 안전하게 제거 할 수 있을지 모르겠습니다.
언제 옵션이 필요한지 예를 들어 주시겠습니까?
답변:
SO_LINGER
시간 제한을 0 으로 설정하는 일반적인 이유 TIME_WAIT
는 서버에서 사용 가능한 모든 리소스를 묶어 상태 에있는 많은 수의 연결을 피하는 것 입니다.
TCP 연결이 완전히 닫히면 닫기를 시작한 끝 ( "활성 닫기")은 연결 TIME_WAIT
이 몇 분 동안 유지 된 상태로 끝납니다 . 따라서 프로토콜이 서버 가 연결 닫기를 시작하고 매우 많은 수의 단기 연결을 포함하는 경우이 문제에 취약 할 수 있습니다.
그러나 이것은 좋은 생각이 아닙니다. TIME_WAIT
(이전 연결에서 나온 패킷이 새로운 연결을 방해하지 않도록하기위한) 이유가 있습니다. 가능한 경우 클라이언트가 연결 닫기를 시작하는 프로토콜로 프로토콜을 다시 디자인하는 것이 좋습니다.
TIME_WAIT
의지는 고객에게 아무런 해를 끼치 지 않습니다. "UNIX 네트워크 프로그래밍"제 3 판 (Stevens et al) 203 페이지에 나와있는 것처럼 "TIME_WAIT 상태는 당신의 친구이며 우리를 도울 것입니다. 상태를 피하려고 시도하는 대신 우리는 그것을 이해해야합니다 (섹션 2.7). . "
제 제안에 대해서는 마지막 섹션 "시간 제한이 0 인 SO_LINGER를 사용하는 경우" 를 읽어보십시오 .
우리가 그것에 대해 조금 강의하기 전에 :
TIME_WAIT
FIN
, ACK
및RST
일반적인 TCP 종료 시퀀스는 다음과 같습니다 (간단).
A와 B의 두 동료가 있습니다.
close()
FIN
가 B 에게 전송FIN_WAIT_1
상태가 됨FIN
ACK
A 에게 보냅니다CLOSE_WAIT
상태가 됨ACK
FIN_WAIT_2
상태가 됨close()
FIN
A 에게 보냅니다LAST_ACK
상태가 됨FIN
ACK
가 B 에게 전송TIME_WAIT
상태가 됨ACK
CLOSED
상태가 됨 – 즉 소켓 테이블에서 제거됨따라서 종료를 시작하는 피어 (즉, close()
먼저 호출) 는 TIME_WAIT
상태가됩니다.
TIME_WAIT
국가가 우리의 친구 인 이유를 이해하려면 Stevens 등의 "UNIX 네트워크 프로그래밍"제 3 판 (43 페이지)의 2.7 절을 읽으십시오.
그러나 TIME_WAIT
서버의 상태에있는 소켓 이 많으면 결국 새 연결이 허용되지 않을 수 있으므로 문제가 될 수 있습니다 .
이 문제를 해결하기 위해을 (를) 호출하기 전에 시간 초과 0으로 SO_LINGER 소켓 옵션을 설정하라는 제안을 많이 보았습니다 close()
. 그러나 이는 TCP 연결이 오류와 함께 종료되는 원인이되므로 나쁜 해결책입니다.
대신 연결 종료가 항상 클라이언트 측에서 시작되도록 애플리케이션 프로토콜을 설계하십시오. 클라이언트가 남은 모든 데이터를 언제 읽었는지 항상 알고 있으면 종료 시퀀스를 시작할 수 있습니다. 예를 들어, 브라우저는 Content-Length
모든 데이터를 읽었을 때 HTTP 헤더에서이를 인식하고 닫기를 시작할 수 있습니다. (HTTP 1.1에서는 재사용이 가능하도록 잠시 열린 상태로 유지 한 다음 닫습니다.)
서버가 연결을 닫아야하는 경우 서버가 클라이언트에게을 호출하도록 요청하도록 응용 프로그램 프로토콜을 설계합니다 close()
.
다시 말하지만, "UNIX 네트워크 프로그래밍"3 판 페이지 202-203에 따르면 SO_LINGER
호출 전에 시간 초과 0으로 설정 close()
하면 정상적인 종료 시퀀스 가 시작 되지 않습니다 .
대신,이 옵션을 설정하고 호출하는 피어 는 오류 조건을 나타내는 (연결 재설정)을 close()
보내며 RST
이것이 다른 쪽에서 인식되는 방식입니다. 일반적으로 "피어에 의한 연결 재설정"과 같은 오류가 표시됩니다.
따라서, 정상적인 상황에서이 세트에 정말 나쁜 생각 SO_LINGER
하기 전에 호출하는 시간 종료 0 close()
지금부터라고에 - 유산 가까운 - 서버 응용 프로그램은.
그러나 특정 상황에서는 어쨌든 그렇게해야합니다.
CLOSE_WAIT
거나 종료 되지 않도록하는 것이 좋습니다 TIME_WAIT
.TIME_WAIT
( close()
서버 쪽에서 호출 할 때) 수천 개의 서버 소켓을 방지하기 위해이 소켓 옵션을 설정하는 것이 좋습니다. 이렇게하면 서버가 새 클라이언트 연결에 사용 가능한 포트를 가져 오지 못할 수 있습니다. 다시 시작한 후.CLOSE_WAIT
고정 된 터미널로 데이터를 전달하려고 할 때 영원히 멈출 수있는 RS-232 터미널 서버입니다." 하지만 RST
보류중인 데이터를 버릴 수있는 경우 고정 된 포트를 올바르게 재설정합니다 . "TIME_WAIT
: 그것은 원인 문제로 시작되지 않는 경우에만 친구입니다 stackoverflow.com/questions/1803566/...
linger가 켜져 있지만 시간 초과가 0이면 TCP 스택은 연결을 닫기 전에 보류중인 데이터가 전송 될 때까지 기다리지 않습니다. 이로 인해 데이터가 손실 될 수 있지만 이렇게 설정하면이를 수락하고 연결이 정상적으로 닫히지 않고 곧바로 재설정되도록 요청하는 것입니다. 이로 인해 일반적인 FIN이 아닌 RST가 전송됩니다.
EJP의 의견에 감사드립니다 . 자세한 내용 은 여기 를 참조하십시오.
코드에서 남아있는 것을 안전하게 제거 할 수 있는지 여부는 응용 프로그램의 유형에 따라 다릅니다. "클라이언트"(TCP 연결을 열고 적극적으로 먼저 닫음)인지 아니면 "서버"(TCP 열기 및 다른 쪽이 닫기를 시작한 후 닫기)?
응용 프로그램이 "클라이언트"(먼저 닫힘)의 특징을 갖고 있고 다른 서버에 대한 엄청난 수의 연결을 시작하고 닫는 경우 (예 : 앱이 수많은 다른 서버의 도달 가능성을 감독하는 모니터링 앱인 경우) 앱 모든 클라이언트 연결이 TIME_WAIT 상태에서 멈춘다는 문제가 있습니다. 그런 다음 정상적으로 종료되지만 클라이언트 연결 리소스를 더 일찍 확보하려면 시간 제한을 기본값보다 작은 값으로 줄이는 것이 좋습니다. 0은 FIN으로 정상적으로 종료되지 않고 RST로 중단되므로 시간 제한을 0으로 설정하지 않습니다.
응용 프로그램이 "클라이언트"의 특징을 가지고 있고 동일한 서버에서 많은 양의 작은 파일을 가져와야하는 경우 파일 당 새 TCP 연결을 시작하지 않고 TIME_WAIT에서 엄청난 양의 클라이언트 연결로 끝나야합니다. 연결을 열린 상태로 유지하고 동일한 연결을 통해 모든 데이터를 가져옵니다. Linger 옵션은 제거 할 수 있으며 제거해야합니다.
응용 프로그램이 "서버"(피어의 닫기에 대한 반응으로 두 번째 닫기) 인 경우 close ()에서 연결이 정상적으로 종료되고 TIME_WAIT 상태가되지 않으면 리소스가 해제됩니다. Linger는 사용해서는 안됩니다. 그러나 서버 앱에 오랜 시간 동안 유휴 상태 인 비활성 열린 연결을 감지하는 감시 프로세스가있는 경우 ( "long"을 정의해야 함)이 비활성 연결을 사용자 측에서 종료 할 수 있습니다. 일종의 오류 처리로 간주하여 중단을 종료합니다. 이것은 linger timeout을 0으로 설정함으로써 이루어집니다. close ()는 클라이언트에게 RST를 보내 당신이 화가났다는 것을 알려줍니다. :-)
서버에서는 오작동하는 클라이언트의 연결을 끊을 때 RST
대신 보낼 수 FIN
있습니다. 건너 뛰고 그 FIN-WAIT
다음에 TIME-WAIT
따라서, 서버 리소스를 고갈과의 방지는, 서비스 거부 공격의이 종류에서 보호하는 서버 소켓 상태.
나는 DOS 공격이 서버 자원을 고갈시킬 수 있다는 Maxim의 관찰을 좋아합니다. 실제로 악의적 인 적 없이도 발생합니다.
일부 서버는 클라이언트 앱에 연결 누출 버그가있을 때 발생하는 '의도하지 않은 DOS 공격'을 처리해야하며, 여기서 서버에 보내는 모든 새 명령에 대해 새 연결을 계속 생성합니다. 그리고 GC 압력에 도달하면 결국 연결이 닫히거나 연결이 결국 시간 초과됩니다.
또 다른 시나리오는 '모든 클라이언트가 동일한 TCP 주소를 갖는'시나리오입니다. 그러면 클라이언트 연결은 포트 번호로만 구별 할 수 있습니다 (단일 서버에 연결하는 경우). 그리고 클라이언트가 어떤 이유로 든 연결 열기 / 닫기를 빠르게 순환하기 시작하면 (클라이언트 주소 + 포트, 서버 IP + 포트) 튜플 공간이 소모 될 수 있습니다.
따라서 서버가 TIME_WAIT 상태에서 많은 수의 소켓을 볼 때 Linger-Zero 전략으로 전환하는 것이 가장 좋습니다. 클라이언트 동작을 수정하지는 않지만 영향을 줄일 수 있습니다.