SO_REUSEADDR과 SO_REUSEPORT는 어떻게 다릅니 까?


663

man pages소켓 옵션에 대한 프로그래머 문서화 SO_REUSEADDR와는 SO_REUSEPORT다른 운영 체제에 대해 서로 다른 종종 매우 혼란이다. 일부 운영 체제에는 옵션이 없습니다 SO_REUSEPORT. WEB은이 주제와 관련하여 모순되는 정보로 가득 차 있으며 종종 특정 운영 체제의 단일 소켓 구현에만 적용되는 정보를 찾을 수 있습니다.이 정보는 텍스트에 명시 적으로 언급되지 않을 수도 있습니다.

그렇다면 정확히 어떻게 SO_REUSEADDR다른 SO_REUSEPORT가요?

시스템이 SO_REUSEPORT더 제한되지 않습니까?

다른 운영 체제에서 하나를 사용하면 정확히 예상되는 동작은 무엇입니까?

답변:


1615

휴대 성의 놀라운 세계에 오신 것을 환영합니다. 이 두 가지 옵션에 대해 자세히 분석하기 시작하고 다른 운영 체제에서 어떻게 처리하는지 자세히 살펴보기 전에 BSD 소켓 구현이 모든 소켓 구현의 모체라는 점에 유의해야합니다. 기본적으로 다른 모든 시스템은 특정 시점 (또는 적어도 인터페이스)에서 BSD 소켓 구현을 복사 한 다음 자체적으로 발전하기 시작했습니다. 물론 BSD 소켓 구현도 동시에 발전했기 때문에 나중에 그것을 복사 한 시스템은 이전에 그것을 복사 한 시스템에서 부족했던 기능을 갖게되었습니다. BSD 소켓 구현을 이해하는 것은 다른 모든 소켓 구현을 이해하는 열쇠이므로 BSD 시스템의 코드를 작성하는 데 신경 쓰지 않아도 읽어야합니다.

이 두 가지 옵션을보기 전에 알아야 할 몇 가지 기본 사항이 있습니다. TCP / UDP 연결은 5 개의 튜플로 식별됩니다.

{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

이러한 값의 고유 한 조합은 연결을 식별합니다. 결과적으로 두 개의 연결이 동일한 5 개의 값을 가질 수 없으며, 그렇지 않으면 시스템이 더 이상이 연결을 구별 할 수 없습니다.

소켓의 프로토콜은 socket()함수 로 소켓을 만들 때 설정됩니다 . 소스 주소와 포트는 bind()기능 으로 설정됩니다 . 목적지 주소와 포트는 connect()기능 으로 설정됩니다 . UDP는 비 연결 프로토콜이므로 UDP 소켓은 연결하지 않고 사용할 수 있습니다. 그러나 그것들을 연결할 수 있으며 경우에 따라 코드 및 일반적인 응용 프로그램 설계에 매우 유리합니다. 비 연결 모드에서는 언 바운드 UDP 소켓이 (응답) 데이터를 수신 할 수 없으므로 데이터가 처음으로 전송 될 때 명시 적으로 바인딩되지 않은 UDP 소켓은 일반적으로 시스템에 의해 자동으로 바인딩됩니다. 언 바운드 TCP 소켓도 마찬가지이며, 연결되기 전에 자동으로 바인딩됩니다.

소켓을 명시 적으로 바인드하면 0"모든 포트"를 의미 하는 port에 바인드 할 수 있습니다. 소켓은 실제로 모든 기존 포트에 바인딩 될 수 없으므로 시스템은이 경우 특정 포트 자체를 선택해야합니다 (일반적으로 사전 정의 된 OS 특정 소스 포트 범위에서). 소스 주소에 대해 유사한 와일드 카드가 존재하며 "모든 주소"일 수 있습니다 ( 0.0.0.0IPv4 및::IPv6의 경우). 포트의 경우와 달리 소켓은 실제로 "모든 로컬 인터페이스의 모든 소스 IP 주소"를 의미하는 "모든 주소"에 바인딩 될 수 있습니다. 소켓이 나중에 연결되면 소켓을 연결할 수없고 동시에 로컬 IP 주소에 바인드되므로 시스템은 특정 소스 IP 주소를 선택해야합니다. 대상 주소와 라우팅 테이블의 내용에 따라 시스템은 적절한 소스 주소를 선택하고 "any"바인딩을 선택한 소스 IP 주소에 대한 바인딩으로 바꿉니다.

기본적으로 두 소켓은 소스 주소와 소스 포트의 동일한 조합에 바인딩 될 수 없습니다. 소스 포트가 다르면 소스 주소는 실제로 관련이 없습니다. 바인딩 socketAA:XsocketBB:Y어디 AB주소입니다 및 XY포트입니다 긴만큼 항상 가능하다 X != Y마찬가지입니다. 그러나에도 불구 X == Y하고 바인딩이 가능한 한 바인딩은 여전히 ​​가능 A != B합니다. 예를 socketAFTP 서버 프로그램에 속하며에 바인딩 192.168.0.1:21socketB다른 FTP 서버 프로그램에 속하며에 바인딩 10.0.0.1:21, 두 바인딩이 성공합니다. 그러나 소켓은 "모든 주소"에 로컬로 바인딩 될 수 있습니다. 소켓이 바인드 된 경우0.0.0.0:21이는 모든 기존 로컬 주소에 동시에 21바인드되며,이 경우 바인드하려는 특정 IP 주소에 관계없이 0.0.0.0기존의 모든 로컬 IP 주소와 충돌하여 다른 소켓을 포트에 바인드 할 수 없습니다 .

지금까지 언급 한 내용은 모든 주요 운영 체제에서 거의 동일합니다. 주소 재사용이 시작되면 상황에 따라 OS가 달라지기 시작합니다. 위에서 언급했듯이 모든 소켓 구현의 어머니이기 때문에 BSD로 시작합니다.

BSD

SO_REUSEADDR

경우 SO_REUSEADDR를 바인딩하기 전에 소켓에서 사용되는 바인딩 다른 소켓과 충돌이없는 한, 소켓이 성공적으로 바인딩 할 수 있습니다 정확히 소스 주소와 포트의 같은 조합. 이제 이전과 어떻게 다른지 궁금 할 것입니다. 키워드는 "정확합니다". SO_REUSEADDR주로 충돌을 검색 할 때 와일드 카드 주소 ( "모든 IP 주소")가 처리되는 방식을 변경합니다.

없이 SO_REUSEADDR바인딩 socketA0.0.0.0:21바인딩 다음과 socketB하기 192.168.0.1:21(오류와 함께 실패합니다 EADDRINUSE0.0.0.0 수단 "로컬 IP 주소", 따라서 모든 로컬 IP 주소가이 소켓에 의해 사용 간주되며이 포함되어 있기 때문에) 192.168.0.1도. 로 SO_REUSEADDR가 있기 때문에, 성공 0.0.0.0하고 192.168.0.1있습니다 정확히되지 같은 주소, 하나는 모든 로컬 주소에 와일드 카드이고 다른 하나는 매우 특정 지역의 주소입니다. 문 위의 어떤 순서에 관계없이 사실이라고주의 socketAsocketB결합되어, 그렇지 않으면 SO_REUSEADDR항상 실패하고 SO_REUSEADDR항상 성공합니다.

더 나은 개요를 제공하기 위해 여기에 표를 만들고 가능한 모든 조합을 나열합시다.

SO_REUSEADDR socketA socketB 결과
-------------------------------------------------- -------------------
  ON / OFF 192.168.0.1:21 192.168.0.1:21 오류 (EADDRINUSE)
  ON / OFF 192.168.0.1:21 10.0.0.1:21 OK
  ON / OFF 10.0.0.1:21 192.168.0.1:21 OK
   OFF 0.0.0.0:21 192.168.1.0:21 오류 (EADDRINUSE)
   OFF 192.168.1.0:21 0.0.0.0:21 오류 (EADDRINUSE)
   ON 0.0.0.0:21 192.168.1.0:21 OK
   ON 192.168.1.0:21 0.0.0.0:21 OK
  ON / OFF 0.0.0.0:21 0.0.0.0:21 오류 (EADDRINUSE)

위의 표는에 socketA지정된 주소에 이미 바인드 된 socketAsocketB작성되고 SO_REUSEADDR설정되거나 설정되지 않은 것으로 가정 하고 마지막으로에 지정된 주소에 바인드됩니다 socketB. Result에 대한 바인드 작업의 결과입니다 socketB. 첫 번째 열에이라고 표시된 ON/OFF경우의 값은 SO_REUSEADDR결과와 관련이 없습니다.

좋아, SO_REUSEADDR와일드 카드 주소에 효과가 좋은 알고 있습니다. 그러나 그것이 효과가있는 것은 아닙니다. 대부분의 사람들 SO_REUSEADDR이 서버 프로그램에서 처음 사용하는 이유이기도 한 또 다른 잘 알려진 효과가 있습니다 . 이 옵션의 다른 중요한 사용을 위해서는 TCP 프로토콜의 작동 방식을 자세히 살펴 봐야합니다.

소켓에는 송신 버퍼가 있으며 send()함수 호출이 성공한 경우 요청 된 데이터가 실제로 전송되었다는 의미는 아니며 데이터가 송신 버퍼에 추가되었음을 의미합니다. UDP 소켓의 경우 데이터는 일반적으로 즉시 전송되지는 않지만 곧 전송되지만 TCP 소켓의 경우 전송 버퍼에 데이터를 추가하는 것과 TCP 구현이 실제로 해당 데이터를 전송하는 것 사이에는 비교적 긴 지연이있을 수 있습니다. 결과적으로 TCP 소켓을 닫을 때 아직 전송되지 않은 송신 버퍼에 보류중인 데이터가있을 수 있지만 코드는 송신 된 것으로 간주합니다.send()전화 성공 TCP 구현이 요청에 따라 즉시 소켓을 닫으면이 모든 데이터가 손실되고 코드는 알지 못합니다. TCP는 신뢰할 수있는 프로토콜이라고하며 이와 같이 데이터를 잃는 것은 그리 신뢰할 수 없습니다. 그렇기 때문에 여전히 전송할 데이터가있는 소켓은 TIME_WAIT닫을 때 호출 된 상태가 됩니다. 이 상태에서는 보류중인 모든 데이터가 성공적으로 전송되거나 시간 초과에 도달 할 때까지 기다립니다 (이 경우 소켓이 강제로 닫힙니다).

아직 비행중인 데이터가 있는지 여부에 관계없이 커널이 소켓을 닫기 전에 대기하는 시간을 Linger Time 이라고합니다 . 링거 시간 (2 분 당신이 많은 시스템에서 찾을 수 일반적인 값) 대부분의 시스템에서 오히려 긴 기본적으로 전 세계적으로 구성 할 수 있습니다. 또한 소켓 옵션 SO_LINGER을 사용하여 소켓별로 구성 할 수 있습니다.이 옵션 을 사용하면 시간 초과를 더 짧게 또는 더 길게 만들거나 완전히 비활성화 할 수 있습니다. TCP 소켓을 정상적으로 닫는 것은 약간 복잡한 프로세스이며 두 개의 패킷을주고 받거나 (패킷이 손실 된 경우 패킷을 다시 보내는 것)이 완전히 닫히는 과정을 포함하므로 완전히 비활성화하는 것은 매우 나쁜 생각입니다. 또한 Linger Time에 의해 제한됩니다. 느린 기능을 해제하면 소켓이 비행 중 데이터를 잃을뿐만 아니라 항상 정상적으로 닫히지 않고 강제로 닫힙니다. 일반적으로 권장하지 않습니다. TCP 연결이 정상적으로 닫히는 방법에 대한 자세한 내용은이 답변의 범위를 벗어납니다. 자세한 내용은 이 페이지를 참조하십시오 . 그리고을 사용 SO_LINGER하지 않고 비활성화하더라도 프로세스가 소켓을 명시 적으로 닫지 않고 죽으면 BSD (및 다른 시스템)는 구성 한 내용을 무시하고 남아 있습니다. 예를 들어 코드가 단지 호출하는 경우에 발생합니다.exit()(작고 간단한 서버 프로그램에서 일반적으로 사용됨) 또는 프로세스가 잘못된 메모리 액세스로 인해 단순히 충돌 할 가능성이있는 신호로 인해 프로세스가 종료되었습니다. 따라서 모든 상황에서 소켓이 멈추지 않도록하기 위해 할 수있는 일은 없습니다.

문제는 시스템이 소켓을 어떻게 처리 TIME_WAIT합니까? SO_REUSEADDR설정되지 않은 경우 소켓 상태 TIME_WAIT의 소스는 여전히 소스 주소 및 포트에 바인드 된 것으로 간주되며 소켓이 실제로 닫힐 때까지 새 소켓을 동일한 주소 및 포트에 바인드하려는 시도는 실패합니다. 구성된으로 링거 시간 . 따라서 소켓을 닫은 직후에 소켓의 소스 주소를 리 바인드 할 수 있다고 기대하지 마십시오. 대부분의 경우 실패합니다. 그러나 SO_REUSEADDR바인딩하려는 소켓에 대해 설정된 경우 다른 소켓은 동일한 주소와 포트에 바인딩 된 상태입니다.TIME_WAIT이미 "반감기"후에는 무시되고 소켓은 아무런 문제없이 정확히 동일한 주소에 바인딩 될 수 있습니다. 이 경우 다른 소켓이 정확히 동일한 주소와 포트를 가질 수 있습니다. 죽어가는 소켓 TIME_WAIT상태 와 소켓을 정확히 동일한 주소 및 포트에 바인딩 하면 다른 소켓이 여전히 "작동 중"인 경우 예상치 못한 부작용이 발생할 수 있지만 이는이 답변의 범위를 벗어납니다. 운 좋게도 이러한 부작용은 실제로는 드 rare니다.

마지막으로 알아야 할 것이 있습니다 SO_REUSEADDR. 바인딩하려는 소켓에 주소 재사용이 활성화되어 있으면 위에서 설명한 모든 것이 작동합니다. 이미 바인드되었거나 TIME_WAIT상태 인 다른 소켓 도 바인드 될 때이 플래그를 설정 하지 않아도 됩니다. 바인드의 성공 또는 실패 여부를 결정하는 코드 SO_REUSEADDRbind()호출에 공급 된 소켓 의 플래그 만 검사합니다. 검사 된 다른 모든 소켓의 경우이 플래그는 보이지 않습니다.

SO_REUSEPORT

SO_REUSEPORT대부분의 사람들이 기대 SO_REUSEADDR하는 것입니다. 기본적으로, 모든 이전 바운드 소켓 이 바인드되기 전에 설정되어 SO_REUSEPORT있는 한 임의의 수의 소켓을 정확히 동일한 소스 주소 및 포트 에 바인딩 할 수 있습니다. 주소 및 포트에 바인드 된 첫 번째 소켓이 설정 되지 않은 경우 , 다른 소켓의 설정 여부에 관계없이 첫 번째 소켓이 바인딩을 다시 해제 할 때까지 다른 소켓을 정확히 동일한 주소 및 포트에 바인드 할 수 없습니다. 코드 처리의 경우와 달리 현재 바인딩 된 소켓이 설정되었는지 확인할뿐만 아니라 주소와 포트가 충돌하는 소켓 이 바인딩되었을 때 설정 되었는지 확인합니다 .SO_REUSEPORTSO_REUSEPORTSO_REUSEPORTSO_REUESADDRSO_REUSEPORTSO_REUSEPORTSO_REUSEPORT

SO_REUSEPORT암시하지 않습니다 SO_REUSEADDR. 이것은 소켓 SO_REUSEPORT이 바인드 될 때 설정 되지 않았고 다른 소켓이 SO_REUSEPORT정확히 동일한 주소 및 포트에 바인드 될 때 설정되면 바인드가 실패하지만 다른 소켓이 이미 죽어가는 경우에도 실패 함을 의미합니다. 에있는 TIME_WAIT상태입니다. 소켓을 TIME_WAIT상태에있는 다른 소켓과 동일한 주소 및 포트에 소켓을 바인딩하려면 SO_REUSEADDR해당 소켓에 소켓 SO_REUSEPORT이 설정되어 있거나 소켓에 바인딩되기 전에 소켓 모두 에 설정 되어 있어야 합니다. 물론 모두를 설정할 수있다 SO_REUSEPORTSO_REUSEADDR소켓에.

SO_REUSEPORT이후에 추가되었다는 것 외에는 말할 것도 많지 SO_REUSEADDR않기 때문에이 옵션이 추가되기 전에 BSD 코드를 "포크 (fork)"한 다른 시스템의 많은 소켓 구현에서는 찾을 수 없습니다. 이 옵션을 사용하기 전에 두 소켓을 BSD에서 정확히 동일한 소켓 주소에 바인딩하는 방법.

Connect () EADDRINUSE를 반환 하시겠습니까?

대부분의 사람들 bind()은 오류로 인해 실패 할 수 있다는 것을 알고 EADDRINUSE있지만, 주소 재사용으로 게임을 시작 connect()하면 해당 오류로 실패 하는 이상한 상황 이 발생할 수 있습니다. 어떻게 이럴 수있어? connect가 소켓에 추가 한 원격 주소가 어떻게 이미 사용되고 있습니까? 정확히 동일한 원격 주소에 여러 소켓을 연결하는 것은 이전에는 문제가되지 않았으므로 여기서 무엇이 잘못 되었습니까?

내가 답장의 맨 위에 말했듯이 연결은 5 개의 값으로 구성된 튜플에 의해 정의됩니다. 기억하십니까? 또한이 5 개의 값은 고유해야하며 그렇지 않으면 시스템에서 더 이상 2 개의 연결을 구분할 수 없습니다. 주소 재사용을 통해 동일한 프로토콜의 두 소켓을 동일한 소스 주소 및 포트에 바인딩 할 수 있습니다. 이것은 5 개의 값 중 3 개가이 2 개의 소켓에 대해 이미 동일하다는 것을 의미합니다. 이 두 소켓을 모두 동일한 대상 주소 및 포트에 연결하려고하면 튜플이 완전히 동일한 두 개의 연결된 소켓을 만듭니다. 적어도 TCP 연결에서는 작동하지 않습니다 (UDP 연결은 실제 연결이 아닙니다). 두 연결 중 하나에 대한 데이터가 도착하면 시스템은 데이터가 속한 연결을 알 수 없습니다.

따라서 동일한 프로토콜의 두 소켓을 동일한 소스 주소 및 포트에 바인딩하고 동일한 대상 주소 및 포트에 모두 연결하려고 connect()하면 실제로 EADDRINUSE연결하려는 두 번째 소켓에 대한 오류와 함께 실패 합니다. 5 개의 동일한 튜플 소켓이 이미 연결되어 있습니다.

멀티 캐스트 주소

대부분의 사람들은 멀티 캐스트 주소가 존재하지만 존재한다는 사실을 무시합니다. 유니 캐스트 주소는 일대일 통신에 사용되지만 멀티 캐스트 주소는 일대 다 통신에 사용됩니다. 대부분의 사람들은 IPv6에 대해 배웠을 때 멀티 캐스트 주소를 알고 있었지만이 기능이 공용 인터넷에서 널리 사용되지는 않았지만 멀티 캐스트 주소도 IPv4에도 존재했습니다.

SO_REUSEADDR멀티 캐스트 주소가 소스 멀티 캐스트 주소와 포트의 정확히 동일한 조합에 바인딩 될 수 있도록하는 멀티 캐스트 주소 변경 의 의미 . 즉, 멀티 캐스트 주소의 경우 유니 캐스트 주소 SO_REUSEADDR와 동일하게 동작 SO_REUSEPORT합니다. 사실, 코드 치료 SO_REUSEADDRSO_REUSEPORT멀티 캐스트 주소에 대한 동일하게, 당신이 그렇게 말할 수있는 수단을 SO_REUSEADDR의미하는 SO_REUSEPORT모든 멀티 캐스트 주소와 다른 방식 라운드.


FreeBSD / OpenBSD / NetBSD

이것들은 모두 원래 BSD 코드의 늦게 나온 것이므로 세 가지 모두 BSD와 동일한 옵션을 제공하고 BSD와 동일한 방식으로 작동합니다.


macOS (MacOS X)

핵심적으로 macOS는 BSD 코드 (BSD 4.3)의 다소 늦은 포크를 기반으로하는 " 다윈 (Darwin) " 이라는 BSD 스타일의 UNIX 일 뿐이며 , 그 당시에는 현재 당시의 FreeBSD와 다시 동기화되었습니다. Apple은 완전한 POSIX 준수를 얻을 수 있도록 Mac OS 10.3 릴리스의 5 개 코드 기반입니다 (macOS는 POSIX 인증). 코어 ( " Mach ")에 마이크로 커널이 있음에도 불구하고 , 나머지 커널 ( " XNU ")은 기본적으로 BSD 커널이므로 macOS는 BSD와 동일한 옵션을 제공하며 BSD와 동일한 방식으로 동작합니다. .

iOS / watchOS / tvOS

iOS는 약간 수정되고 잘린 커널을 가진 macOS 포크 일뿐 아니라 사용자 공간 툴셋과 약간 다른 기본 프레임 워크 세트를 제거합니다. watchOS 및 tvOS는 iOS 포크이며, 특히 더 떨어져 있습니다 (특히 watchOS). 내가 아는 한, macOS와 동일하게 동작합니다.


리눅스

리눅스 <3.9

Linux 3.9 이전에는 옵션 만 SO_REUSEADDR있었습니다. 이 옵션은 일반적으로 두 가지 중요한 예외를 제외하고 BSD에서와 동일하게 작동합니다.

  1. 수신 (서버) TCP 소켓이 특정 포트에 바인딩되어 SO_REUSEADDR있으면 해당 포트를 대상으로하는 모든 소켓에 대해 옵션이 완전히 무시됩니다. 두 번째 소켓을 동일한 포트에 바인딩하는 것은 SO_REUSEADDR설정 하지 않고 BSD에서도 가능할 경우에만 가능합니다 . 예를 들어 와일드 카드 주소에 바인딩 한 다음 더 구체적인 주소 또는 다른 방법으로 바인딩 할 수 없으며 BSD에서 설정하면 둘 다 가능합니다 SO_REUSEADDR. 항상 허용되는 것처럼 동일한 포트와 두 개의 다른 비 와일드 카드 주소에 바인딩 할 수 있습니다. 이런 측면에서 리눅스는 BSD보다 더 제한적입니다.

  2. 두 번째 예외는 클라이언트 소켓의 경우이 옵션 SO_REUSEPORT이 바인딩되기 전에이 플래그가 설정되어있는 한 BSD에서와 동일하게 동작 한다는 것입니다. 이를 허용하는 이유는 여러 프로토콜에 대해 여러 소켓을 동일한 UDP 소켓 주소에 정확하게 바인딩하는 것이 중요 SO_REUSEPORT하고 3.9 이전 에는 없었던 것처럼 동작이 SO_REUSEADDR그 간격을 채우기 위해 변경되었습니다. . 이러한 측면에서 리눅스는 BSD보다 덜 제한적입니다.

리눅스> = 3.9

Linux 3.9는 SO_REUSEPORTLinux에도 옵션 을 추가했습니다 . 이 옵션은 BSD의 옵션과 똑같이 동작하며 모든 소켓에 바인딩 전에이 옵션이 설정되어있는 한 정확히 동일한 주소 및 포트 번호에 바인딩 할 수 있습니다.

그러나 SO_REUSEPORT다른 시스템 에서는 여전히 두 가지 차이점이 있습니다.

  1. "포트 하이재킹"을 방지하려면 한 가지 특별한 제한 이 있습니다. 동일한 주소 및 포트 조합을 공유하려는 모든 소켓은 동일한 유효 사용자 ID를 공유하는 프로세스에 속해야합니다! 따라서 한 사용자가 다른 사용자의 포트를 "훔칠"수 없습니다. 이것은 누락 SO_EXCLBIND/ SO_EXCLUSIVEADDRUSE플래그 를 다소 보상하는 특별한 마술 입니다.

  2. 또한 커널 수행에 대한 몇 가지 "특별한 마법" SO_REUSEPORT다른 운영 체제에서 발견되지 않는 소켓 : UDP 소켓의 경우, 그것은 TCP 소켓을 청취, 균등하게 데이터 그램을 배포하는 시도가 들어오는 연결 요청을 분산하려고합니다 (그 호출하여 사용 가능 accept()) 동일한 주소 및 포트 조합을 공유하는 모든 소켓에서 고르게 따라서 응용 프로그램은 여러 하위 프로세스에서 동일한 포트를 쉽게 열고 SO_REUSEPORT매우 저렴한로드 밸런싱을 얻는 데 사용할 수 있습니다.


기계적 인조 인간

전체 안드로이드 시스템은 대부분의 Linux 배포판과는 다소 다르지만 그 핵심은 약간 수정 된 Linux 커널에서 작동하므로 Linux에 적용되는 모든 것이 Android에도 적용되어야합니다.


윈도우

Windows는 SO_REUSEADDR옵션 만 알고 SO_REUSEPORT있습니다. 설정 SO_REUSEADDR설정 같은 윈도우 동작합니다의 소켓 SO_REUSEPORTSO_REUSEADDR한 가지를 제외하고, BSD의 소켓 :와 소켓 SO_REUSEADDR수 항상 바인드 이미 바운드 소켓과 정확히 동일한 소스 주소와 포트에 다른 소켓이이 옵션을 가지고 있지 않은 경우에도 바인딩되었을 때 설정합니다 . 이 동작은 응용 프로그램이 다른 응용 프로그램의 연결된 포트를 "훔칠"수 있기 때문에 다소 위험합니다. 말할 것도없이, 이것은 중대한 보안 영향을 미칠 수 있습니다. Microsoft는 이것이 문제 일 수 있다는 것을 깨달았으므로 다른 소켓 옵션을 추가했습니다 SO_EXCLUSIVEADDRUSE. 환경SO_EXCLUSIVEADDRUSE소켓에서 바인딩이 성공하면 소스 주소와 포트의 조합이이 소켓에 의해 독점적으로 소유되며 다른 소켓이 SO_REUSEADDR설정되어 있어도 바인딩 할 수 없습니다 .

Windows 에서 플래그 SO_REUSEADDRSO_EXCLUSIVEADDRUSE작동 방식, 바인딩 / 리 바인딩에 미치는 영향 에 대한 자세한 내용을 보려면 Microsoft는 해당 답변의 맨 위에 가까운 표와 비슷한 표를 친절하게 제공했습니다. 이 페이지를 방문 하여 조금 아래로 스크롤 하십시오 . 실제로 세 개의 테이블, 첫 번째 쇼 이전 동작 (2003 년 이전의 Windows), (최대 윈도우 2003) 두 번째 동작과 윈도우 2003에서 동작 변경이 나중에 어떻게 세 번째 쇼가 bind()호출에 의해 만들어진다는 다른 사용자.


솔라리스

Solaris는 SunOS의 후속 제품입니다. SunOS는 원래 BSD 포크를 기반으로했으며 SunOS 5 이상은 SVR4 포크를 기반으로했지만 SVR4는 BSD, System V 및 Xenix의 병합이므로 Solaris도 BSD 포크이며 오히려 초기 것. 결과적으로 솔라리스는 아는 것만 SO_REUSEADDR없다 SO_REUSEPORT. SO_REUSEADDR동작합니다 거의 같은이 BSD에서와 같이. 내가 아는 SO_REUSEPORT한 Solaris에서 와 동일한 동작을 수행 할 수있는 방법이 없기 때문에 두 개의 소켓을 정확히 동일한 주소와 포트에 바인딩 할 수 없습니다.

Windows와 마찬가지로 Solaris에는 소켓에 독점 바인딩을 제공하는 옵션이 있습니다. 이 옵션의 이름은 SO_EXCLBIND입니다. 이 옵션을 소켓에 바인딩하기 전에 설정 SO_REUSEADDR한 경우 두 소켓이 주소 충돌에 대해 테스트 된 경우 다른 소켓에 대한 설정 은 적용되지 않습니다. 경우 예 socketA와일드 카드 주소에 바인드되고 socketB있다 SO_REUSEADDR활성화 및 비 와일드 카드 주소와 같은 포트에 바인딩 socketA,하지 않는 한이 바인딩은 일반적으로 성공 socketA했다 SO_EXCLBIND사용할 수 있으며,이 경우에 관계없이 실패 SO_REUSEADDR의 플래그를 socketB.


다른 시스템

귀하의 시스템이 위에 나열되어 있지 않은 경우 시스템이 이러한 두 가지 옵션을 처리하는 방법을 찾는 데 사용할 수있는 작은 테스트 프로그램을 작성했습니다. 또한 내 결과가 잘못되었다고 생각되면 먼저 의견을 게시하고 허위 주장을 제기하기 전에 해당 프로그램을 실행하십시오.

코드를 빌드하는 데 필요한 모든 (네트워크 부분에 대한) 약간의 POSIX API와 C99 컴파일러입니다 (컴파일러뿐만 아니라 그들이 제공으로 작동합니다 실제로 대부분의 비 C99 inttypes.hstdbool.h, 예를 들어 gcc지원을 모두 긴 전체 C99의 지원을 제공하기 전에) .

프로그램을 실행하기 위해서는 시스템에서 하나 이상의 인터페이스 (로컬 인터페이스 제외)에 IP 주소가 할당되어 있고 해당 인터페이스를 사용하는 기본 경로가 설정되어 있어야합니다. 프로그램은 해당 IP 주소를 수집하여 두 번째 "특정 주소"로 사용합니다.

생각할 수있는 모든 가능한 조합을 테스트합니다.

  • TCP 및 UDP 프로토콜
  • 일반 소켓, 청취 (서버) 소켓, 멀티 캐스트 소켓
  • SO_REUSEADDR socket1, socket2 또는 두 소켓 모두에 설정
  • SO_REUSEPORT socket1, socket2 또는 두 소켓 모두에 설정
  • 모든 주소 조합 0.0.0.0(와일드 카드), 127.0.0.1(특정 주소) 및 기본 인터페이스에서 찾은 두 번째 특정 주소 (멀티 캐스트의 경우 224.1.2.3모든 테스트에 해당)

좋은 테이블에 결과를 인쇄합니다. 또한 모르는 시스템에서도 작동 SO_REUSEPORT하며이 경우이 옵션은 단순히 테스트되지 않습니다.

프로그램이 쉽게 테스트 할 수없는 것은 SO_REUSEADDR소켓 TIME_WAIT을 강제로 유지하고 상태를 유지하는 것이 매우 까다로워서 상태의 소켓에서 작동 하는 방법 입니다. 운 좋게도 대부분의 운영 체제는 여기서 BSD처럼 작동하는 것처럼 보이며 대부분의 경우 프로그래머는 단순히 해당 상태의 존재를 무시할 수 있습니다.

여기에 코드가 있습니다 (여기에 포함시킬 수 없습니다. 답변에는 크기 제한이 있으며 코드는이 회신을 한도 위로 푸시합니다).


9
예를 들어, "소스 주소"는 실제로 다음 세 필드 인 "로컬 주소"여야합니다. 로 바인딩하면 INADDR_ANY기존 로컬 주소가 아니라 미래의 모든 주소도 바인딩됩니다. listen불가능하다고 말 했음에도 불구하고 정확히 동일한 프로토콜, 로컬 주소 및 로컬 포트로 소켓을 만듭니다.
벤 Voigt

9
@Ben 소스 및 대상은 IP 주소 지정에 사용되는 공식 용어입니다 (1 차 참조). 원격 주소는 실제로 "로컬"주소가 될 수 있고 대상의 반대는 소스이며 로컬이 아니기 때문에 로컬 및 원격은 의미가 없습니다. 나는 당신의 문제와 무엇인지 모르는 INADDR_ANY나는, 결코 그것이 미래의 주소에 바인딩하지 않을 말했다. 그리고 listen소켓을 전혀 만들지 않으므로 문장 전체가 조금 이상합니다.
Mecki

7
@Ben 새로운 주소가 시스템에 추가되면 "기존 로컬 주소"이기도합니다. " 현재 존재 하는 모든 로컬 주소에" 라고 말하지 않았습니다 . 실제로 나는 심지어 소켓이 실제로 와일드 카드에 바인딩되어 있다고 말합니다. 즉, 소켓은 현재, 내일 그리고 수백 년 동안이 와일드 카드와 일치하는 모든 것에 바인딩됩니다. 소스 및 대상과 유사하게 여기서는 nitpicking입니다. 실질적인 기술 기여가 있습니까?
Mecki

8
@Mecki : 기존 단어에 현재 존재하지 않지만 앞으로는 존재하는 것들이 포함되어 있다고 생각 하십니까? 출발지와 목적지는 별난 것이 아닙니다. 들어오는 패킷이 소켓과 일치하면 패킷의 대상 주소가 소켓의 "소스"주소와 일치한다고 말하는 것입니까? 그것은 틀렸고 당신은 그것을 알고 있습니다. 당신은 이미 소스목적지 가 반대 라고 말했습니다 . 소켓 의 로컬 주소 는 수신 패킷 의 대상 주소 와 일치 하며 발신 패킷 의 소스 주소에 배치됩니다 .
벤 Voigt

10
@Mecki : "소켓의 로컬 주소는 나가는 패킷의 소스 주소와 들어오는 패킷의 목적지 주소"라고 말하면 훨씬 더 의미가 있습니다. 패킷에는 소스 및 대상 주소가 있습니다. 호스트와 호스트의 소켓은 그렇지 않습니다. 데이터 그램 소켓의 경우 두 피어가 동일합니다. TCP 소켓의 경우 3 방향 핸드 셰이크로 인해 발신자 (클라이언트)와 응답자 (서버)가 있지만 트래픽이 양방향으로 흐르기 때문에 연결이나 연결된 소켓에 소스대상 이 있다는 의미는 아닙니다 .
벤 Voigt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.