TCP 옵션 SO_LINGER (0)는 언제 필요합니까?


96

옵션의 공식적인 의미를 이해하고 있다고 생각합니다. 지금 처리하고있는 일부 레거시 코드에서는 옵션이 사용됩니다. 고객이 측면에서 가까운 연결에서 FIN에 대한 응답으로 RST에 대해 불평합니다.

언제 사용해야하는지 모르겠 기 때문에 안전하게 제거 할 수 있을지 모르겠습니다.

언제 옵션이 필요한지 예를 들어 주시겠습니까?


1
제거해야합니다. 프로덕션 코드에서 사용해서는 안됩니다. 내가 사용한 유일한 시간은 잘못된 벤치 마크의 결과였습니다.
Marquis of Lorne

답변:


83

SO_LINGER시간 제한을 0 으로 설정하는 일반적인 이유 TIME_WAIT는 서버에서 사용 가능한 모든 리소스를 묶어 상태 에있는 많은 수의 연결을 피하는 것 입니다.

TCP 연결이 완전히 닫히면 닫기를 시작한 끝 ( "활성 닫기")은 연결 TIME_WAIT이 몇 분 동안 유지 된 상태로 끝납니다 . 따라서 프로토콜이 서버 가 연결 닫기를 시작하고 매우 많은 수의 단기 연결을 포함하는 경우이 문제에 취약 할 수 있습니다.

그러나 이것은 좋은 생각이 아닙니다. TIME_WAIT(이전 연결에서 나온 패킷이 새로운 연결을 방해하지 않도록하기위한) 이유가 있습니다. 가능한 경우 클라이언트가 연결 닫기를 시작하는 프로토콜로 프로토콜을 다시 디자인하는 것이 좋습니다.


4
전적으로 동의합니다. 나는 많은 (X 초마다 수천 개의 짧은 연결)을 시작하는 모니터링 애플리케이션을 보았으며 더 크게 확장 할 수있는 프로브 (1000 개 이상)를 가졌습니다. 이유는 모르겠지만 응용 프로그램이 응답하지 않았습니다. 누군가가 SO_LINGER = true, TIME_WAIT = 0을 제안하여 OS 리소스를 빠르게 해제했으며 짧은 조사 끝에이 솔루션을 매우 좋은 결과로 시도했습니다. TIME_WAIT는 더 이상이 앱에서 문제가되지 않습니다.
bartosz.r

24
동의하지 않습니다. TCP 위에있는 응용 프로그램 수준 프로토콜은 클라이언트가 항상 연결 닫기를 시작하도록 설계되어야합니다. 그렇게하면 TIME_WAIT의지는 고객에게 아무런 해를 끼치 지 않습니다. "UNIX 네트워크 프로그래밍"제 3 판 (Stevens et al) 203 페이지에 나와있는 것처럼 "TIME_WAIT 상태는 당신의 친구이며 우리를 도울 것입니다. 상태를 피하려고 시도하는 대신 우리는 그것을 이해해야합니다 (섹션 2.7). . "
mgd

8
클라이언트가 30 초마다 4000 개의 연결을 열려고하면 (이 모니터링 응용 프로그램은 연결을 시작하기 때문에 클라이언트입니다!) 예, 애플리케이션을 다시 작성하고 인프라에 로컬 에이전트를 추가하고 모델을 푸시로 변경할 수 있습니다. 그러나 우리가 이미 그러한 애플리케이션을 가지고 있고 그것이 성장한다면, 우리는 조정을 통해 그것을 작동시킬 수 있습니다. 하나의 매개 변수를 변경하면 새로운 아키텍처를 구현하기위한 예산을 투자하지 않고 갑자기 작동하는 애플리케이션이 생깁니다.
bartosz.r

4
@ bartosz.r : 시간 초과 0과 함께 SO_LINGER를 사용하는 것이 실제로 최후의 수단이되어야한다고 만 말합니다. 다시 말하지만, "UNIX 네트워크 프로그래밍"3 판 (Stevens et al) 203 페이지에서도 데이터 손상 위험이 있다고 말합니다. TIME_WAIT가 친구 인 이유를 알 수있는 RFC 1337을 읽어보십시오.
mgd

7
@caf 아니요, 고전적인 솔루션은 HTTP 1.1과 같은 모든 대용량 TCP API에서 볼 수 있듯이 연결 풀입니다.
Marquis of Lorne

191

제 제안에 대해서는 마지막 섹션 "시간 제한이 0 인 SO_LINGER를 사용하는 경우" 를 읽어보십시오 .

우리가 그것에 대해 조금 강의하기 전에 :

  • 정상적인 TCP 종료
  • TIME_WAIT
  • FIN, ACKRST

정상적인 TCP 종료

일반적인 TCP 종료 시퀀스는 다음과 같습니다 (간단).

A와 B의 두 동료가 있습니다.

  1. 전화 close()
    • A FIN가 B 에게 전송
    • A FIN_WAIT_1상태가 됨
  2. B 수신 FIN
    • B는 ACKA 에게 보냅니다
    • B가 CLOSE_WAIT상태가 됨
  3. A 수신 ACK
    • A FIN_WAIT_2상태가 됨
  4. B 호출 close()
    • B는 FINA 에게 보냅니다
    • B가 LAST_ACK상태가 됨
  5. A 수신 FIN
    • A ACK가 B 에게 전송
    • A TIME_WAIT상태가 됨
  6. B 수신 ACK
    • B CLOSED상태가 됨 – 즉 소켓 테이블에서 제거됨

TIME_WAIT

따라서 종료를 시작하는 피어 (즉, 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().

시간 초과 0과 함께 SO_LINGER를 사용하는 경우

다시 말하지만, "UNIX 네트워크 프로그래밍"3 판 페이지 202-203에 따르면 SO_LINGER호출 전에 시간 초과 0으로 설정 close()하면 정상적인 종료 시퀀스 가 시작 되지 않습니다 .

대신,이 옵션을 설정하고 호출하는 피어 는 오류 조건을 나타내는 (연결 재설정)을 close()보내며 RST이것이 다른 쪽에서 인식되는 방식입니다. 일반적으로 "피어에 의한 연결 재설정"과 같은 오류가 표시됩니다.

따라서, 정상적인 상황에서이 세트에 정말 나쁜 생각 SO_LINGER하기 전에 호출하는 시간 종료 0 close()지금부터라고에 - 유산 가까운 - 서버 응용 프로그램은.

그러나 특정 상황에서는 어쨌든 그렇게해야합니다.

  • 서버 애플리케이션의 클라이언트가 오작동하는 경우 (시간 초과, 유효하지 않은 데이터 반환 등) 중단 된 닫기는 상태에 갇히 CLOSE_WAIT거나 종료 되지 않도록하는 것이 좋습니다 TIME_WAIT.
  • 현재 수천 개의 클라이언트 연결이있는 서버 응용 프로그램을 다시 시작해야하는 경우 TIME_WAIT( close()서버 쪽에서 호출 할 때) 수천 개의 서버 소켓을 방지하기 위해이 소켓 옵션을 설정하는 것이 좋습니다. 이렇게하면 서버가 새 클라이언트 연결에 사용 가능한 포트를 가져 오지 못할 수 있습니다. 다시 시작한 후.
  • 앞서 언급 한 책의 202 페이지에서 "이 기능을 사용하여 중단 된 닫기를 보내야하는 특정 상황이 있습니다. 한 가지 예는 CLOSE_WAIT고정 된 터미널로 데이터를 전달하려고 할 때 영원히 멈출 수있는 RS-232 터미널 서버입니다." 하지만 RST보류중인 데이터를 버릴 수있는 경우 고정 된 포트를 올바르게 재설정합니다 . "

나는 귀하의 질문에 대한 매우 좋은 답변을 제공한다고 믿는 긴 기사를 추천 합니다 .


6
TIME_WAIT: 그것은 원인 문제로 시작되지 않는 경우에만 친구입니다 stackoverflow.com/questions/1803566/...
Pacerier

2
웹 서버를 작성한다면 어떨까요? "고객에게 종가를 시작하라고"어떻게 말합니까?
Shaun Neal

2
@ShaunNeal 당신은 분명히하지 않습니다. 그러나 잘 작성된 클라이언트 / 브라우저는 닫기를 시작합니다. 클라이언트가 제대로 작동하지 않으면 다행스럽게도 소켓 설명자와 임시 포트가 부족하지 않도록 TIME_WAIT 암살이 발생합니다.
mgd mar

이것은 훌륭한 대답입니다. 감사합니다!
Juraj Martinka

17

linger가 켜져 있지만 시간 초과가 0이면 TCP 스택은 연결을 닫기 전에 보류중인 데이터가 전송 될 때까지 기다리지 않습니다. 이로 인해 데이터가 손실 될 수 있지만 이렇게 설정하면이를 수락하고 연결이 정상적으로 닫히지 않고 곧바로 재설정되도록 요청하는 것입니다. 이로 인해 일반적인 FIN이 아닌 RST가 전송됩니다.

EJP의 의견에 감사드립니다 . 자세한 내용 은 여기 를 참조하십시오.


1
이해합니다. 내가 요청하는 것은 하드 리셋을 사용하고 싶을 때 "현실적인"예제를위한 것입니다.
dimba

5
연결을 중단 할 때마다 당신의 프로토콜 검증을 실패하고 당신에 모든 쓰레기를 말하는 클라이언트가 그렇다면 갑자기 당신은 등 RST를,와의 연결을 중단 것
렌 Holgate

5
제로 링거 ​​타임 아웃과 링거 오프를 혼동하고 있습니다. Linger off는 close ()가 차단되지 않음을 의미합니다. 시간 초과가 긍정적 인 경우에는 close ()가 시간 초과까지 차단된다는 의미입니다. 제한 시간이 0 인 상태에서 계속 기다리면 RST가 발생하며 이것이 질문에 관한 것입니다.
Marquis of Lorne

2
네, 맞습니다. 내 용어를 수정하기 위해 답변을 조정하겠습니다.
Len Holgate

6

코드에서 남아있는 것을 안전하게 제거 할 수 있는지 여부는 응용 프로그램의 유형에 따라 다릅니다. "클라이언트"(TCP 연결을 열고 적극적으로 먼저 닫음)인지 아니면 "서버"(TCP 열기 및 다른 쪽이 닫기를 시작한 후 닫기)?

응용 프로그램이 "클라이언트"(먼저 닫힘)의 특징을 갖고 있고 다른 서버에 대한 엄청난 수의 연결을 시작하고 닫는 경우 (예 : 앱이 수많은 다른 서버의 도달 가능성을 감독하는 모니터링 앱인 경우) 앱 모든 클라이언트 연결이 TIME_WAIT 상태에서 멈춘다는 문제가 있습니다. 그런 다음 정상적으로 종료되지만 클라이언트 연결 리소스를 더 일찍 확보하려면 시간 제한을 기본값보다 작은 값으로 줄이는 것이 좋습니다. 0은 FIN으로 정상적으로 종료되지 않고 RST로 중단되므로 시간 제한을 0으로 설정하지 않습니다.

응용 프로그램이 "클라이언트"의 특징을 가지고 있고 동일한 서버에서 많은 양의 작은 파일을 가져와야하는 경우 파일 당 새 TCP 연결을 시작하지 않고 TIME_WAIT에서 엄청난 양의 클라이언트 연결로 끝나야합니다. 연결을 열린 상태로 유지하고 동일한 연결을 통해 모든 데이터를 가져옵니다. Linger 옵션은 제거 할 수 있으며 제거해야합니다.

응용 프로그램이 "서버"(피어의 닫기에 대한 반응으로 두 번째 닫기) 인 경우 close ()에서 연결이 정상적으로 종료되고 TIME_WAIT 상태가되지 않으면 리소스가 해제됩니다. Linger는 사용해서는 안됩니다. 그러나 서버 앱에 오랜 시간 동안 유휴 상태 인 비활성 열린 연결을 감지하는 감시 프로세스가있는 경우 ( "long"을 정의해야 함)이 비활성 연결을 사용자 측에서 종료 할 수 있습니다. 일종의 오류 처리로 간주하여 중단을 종료합니다. 이것은 linger timeout을 0으로 설정함으로써 이루어집니다. close ()는 클라이언트에게 RST를 보내 당신이 화가났다는 것을 알려줍니다. :-)


1

서버에서는 오작동하는 클라이언트의 연결을 끊을 때 RST대신 보낼 수 FIN있습니다. 건너 뛰고 그 FIN-WAIT다음에 TIME-WAIT따라서, 서버 리소스를 고갈과의 방지는, 서비스 거부 공격의이 종류에서 보호하는 서버 소켓 상태.


1

나는 DOS 공격이 서버 자원을 고갈시킬 수 있다는 Maxim의 관찰을 좋아합니다. 실제로 악의적 인 적 없이도 발생합니다.

일부 서버는 클라이언트 앱에 연결 누출 버그가있을 때 발생하는 '의도하지 않은 DOS 공격'을 처리해야하며, 여기서 서버에 보내는 모든 새 명령에 대해 새 연결을 계속 생성합니다. 그리고 GC 압력에 도달하면 결국 연결이 닫히거나 연결이 결국 시간 초과됩니다.

또 다른 시나리오는 '모든 클라이언트가 동일한 TCP 주소를 갖는'시나리오입니다. 그러면 클라이언트 연결은 포트 번호로만 구별 할 수 있습니다 (단일 서버에 연결하는 경우). 그리고 클라이언트가 어떤 이유로 든 연결 열기 / 닫기를 빠르게 순환하기 시작하면 (클라이언트 주소 + 포트, 서버 IP + 포트) 튜플 공간이 소모 될 수 있습니다.

따라서 서버가 TIME_WAIT 상태에서 많은 수의 소켓을 볼 때 Linger-Zero 전략으로 전환하는 것이 가장 좋습니다. 클라이언트 동작을 수정하지는 않지만 영향을 줄일 수 있습니다.


0

서버의 청취 소켓은 시간이 0 인 linger를 사용하여 소켓에 대한 바인딩에 즉시 액세스하고 연결이 아직 완료되지 않은 모든 클라이언트를 재설정 할 수 있습니다. TIME_WAIT는 다중 경로 네트워크가 있고 순서가 잘못된 패킷으로 끝날 수 있거나 그렇지 않으면 이상한 네트워크 패킷 순서 / 도착 타이밍을 처리 할 때만 흥미로운 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.