TCP : 두 개의 다른 소켓이 포트를 공유 할 수 있습니까?


124

이것은 매우 기본적인 질문 일 수 있지만 저를 혼란스럽게합니다.

두 개의 서로 다른 연결된 소켓이 포트를 공유 할 수 있습니까? 저는 10 만 개 이상의 동시 연결을 처리 할 수있는 애플리케이션 서버를 작성 중이며 시스템에서 사용 가능한 포트 수가 약 60k (16 비트)라는 것을 알고 있습니다. 연결된 소켓은 새 (전용) 포트에 할당되므로 여러 소켓이 동일한 포트를 공유 할 수없는 경우 동시 연결 수가 포트 수에 의해 제한됩니다. 그래서 질문입니다.

미리 도와 주셔서 감사합니다!

답변:


175

서버 소켓은 단일 포트에서 수신 대기합니다. 해당 서버에 설정된 모든 클라이언트 연결은 연결 의 서버 측에있는 동일한 수신 포트와 연관됩니다 . 설정된 연결은 클라이언트 측 및 서버 측 IP / 포트 쌍의 조합으로 고유하게 식별됩니다. 동일한 서버의 여러 연결은 서로 다른 클라이언트 측 IP / 포트 쌍과 연결되어있는 한 동일한 서버 IP / 포트 쌍을 공유 할 수 있으며, 서버 는 사용 가능한 시스템 리소스가 허용하는 한 많은 클라이언트를 처리 할 수 ​​있습니다. 에.

클라이언트 측 , 그것은 새로운 아웃 바운드 연결이 임의 사용에 대한 일반적이다 클라이언트 측 당신이 짧은 시간에 많은 연결을 할 경우 사용 가능한 포트 밖으로 실행하는 것이 가능하다이 경우 포트.


2
대답 해 주셔서 감사합니다, 레미! 당신의 대답은 제가 궁금했던 모든 것입니다. ;)
KJ

2
@Remy Connections는 소스 / 목적지 포트 / IP뿐만 아니라 프로토콜 (TCP, UDP 등)에 의해서도 구분됩니다.
Ondrej Peterka 2014

1
@OndraPeterka : 예,하지만 모든 플랫폼이 제한하는 것은 아닙니다. 예를 들어, Windows는 기꺼이 별도의 IPv4 및 IPv6 서버 소켓이 같은 로컬 IP : Port에서 튀지 않고 수신하도록 허용하지만 * Nix 시스템 (Linux 및 Android 포함)은 그렇지 않습니다.
Remy Lebeau 2014

6
@ user2268997 : 단일 소켓을 사용하여 여러 서버에 연결할 수 없습니다. 각 연결에 대해 별도의 소켓을 만들어야합니다.
Remy Lebeau 2015 년

3
@FernandoGonzalezSanchez : 단일 클라이언트는 서로 다른 원격 IP / 포트 쌍에 연결되어있는 한 동일한 로컬 IP / 포트 쌍에 바인딩 된 여러 TCP 소켓을 가질 수 있습니다. 이는 Windows에만 국한된 것이 아니며 TCP가 일반적으로 작동하는 방식의 일부입니다.
Remy Lebeau

182

포트에서 TCP / HTTP 수신 : 여러 사용자가 동일한 포트를 공유하는 방법

그렇다면 서버가 TCP 포트에서 들어오는 연결을 수신하면 어떻게 될까요? 예를 들어 포트 80에 웹 서버가 있다고 가정합니다. 컴퓨터의 공용 IP 주소가 24.14.181.229이고 연결을 시도하는 사람의 IP 주소가 10.1.2.3이라고 가정 해 보겠습니다. 이 사람은 24.14.181.229:80에 대한 TCP 소켓을 열어 귀하에게 연결할 수 있습니다. 충분히 간단합니다.

직관적으로 (그리고 잘못) 대부분의 사람들은 다음과 같이 보인다고 가정합니다.

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

클라이언트의 관점에서 볼 때 그는 IP 주소가 있고 IP : PORT에서 서버에 연결하기 때문에 직관적입니다. 클라이언트가 포트 80에 연결되었으므로 그의 포트도 80이어야합니까? 이것은 생각하기에 현명한 일이지만 실제로 일어나는 일은 아닙니다. 이것이 맞다면 외부 IP 주소 당 한 명의 사용자 만 서비스 할 수 있습니다. 원격 컴퓨터가 연결되면 그는 포트 80에서 포트 80으로의 연결을 호그하고 다른 사람은 연결할 수 없습니다.

다음 세 가지를 이해해야합니다.

1.) 서버에서 프로세스가 수신 중입니다. 포트에서 합니다. 일단 연결되면 다른 스레드로 넘깁니다. 통신은 청취 포트를 절대로 잡아 먹지 않습니다.

2.) 연결은 다음 5 개의 튜플 (로컬 IP, 로컬 포트, 원격 IP, 원격 포트, 프로토콜)에 의해 OS에서 고유하게 식별됩니다. 튜플의 요소가 다른 경우 이것은 완전히 독립적 인 연결입니다.

3.) 클라이언트가 서버에 연결할 때 사용되지 않는 임의의 상위 소스 포트를 선택 합니다. 이러한 방식으로 단일 클라이언트는 동일한 대상 포트에 대해 최대 64k까지 서버에 연결할 수 있습니다.

따라서 이것은 클라이언트가 서버에 연결할 때 실제로 생성되는 것입니다.

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

실제로 일어나는 일보기

먼저 netstat를 사용하여이 컴퓨터에서 무슨 일이 일어나는지 살펴 보겠습니다. 80 대신 포트 500을 사용할 것입니다 (일반 포트이기 때문에 포트 80에서 많은 일이 발생하지만 기능적으로는 차이가 없기 때문입니다).

    netstat -atnp | grep -i ":500 "

예상대로 출력이 비어 있습니다. 이제 웹 서버를 시작하겠습니다.

    sudo python3 -m http.server 500

이제 다음은 netstat를 다시 실행 한 결과입니다.

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

이제 포트 500에서 능동적으로 수신하는 프로세스 (상태 : LISTEN)가 하나 있습니다. 로컬 주소는 "모든 IP 주소 수신"을위한 코드 인 0.0.0.0입니다. 쉬운 실수는 현재 컴퓨터의 연결 만 허용하는 127.0.0.1 포트에서만 수신하는 것입니다. 따라서 이것은 연결이 아닙니다. 이것은 프로세스가 포트 IP에 bind ()를 요청했고 해당 프로세스가 해당 포트에 대한 모든 연결을 처리 할 책임이 있음을 의미합니다. 이는 포트에서 수신 대기하는 컴퓨터 당 하나의 프로세스 만있을 수 있다는 제한을 암시합니다 (멀티플렉싱을 사용하여이를 해결할 수있는 방법이 있지만 이것은 훨씬 더 복잡한 주제입니다). 웹 서버가 포트 80에서 수신 대기하는 경우 해당 포트를 다른 웹 서버와 공유 할 수 없습니다.

이제 사용자를 컴퓨터에 연결해 보겠습니다.

    quicknet -m tcp -t localhost:500 -p Test payload.

이것은 TCP 소켓을 열고 페이로드 (이 경우 "테스트 페이로드")를 보내고 몇 초 동안 기다렸다가 연결을 끊는 간단한 스크립트 ( https://github.com/grokit/quickweb )입니다. 이 문제가 발생하는 동안 netstat를 다시 수행하면 다음이 표시됩니다.

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

다른 클라이언트에 연결하고 netstat를 다시 수행하면 다음이 표시됩니다.

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... 즉, 클라이언트가 연결에 다른 임의의 포트를 사용했습니다. 따라서 IP 주소간에 혼동이 없습니다.


11
이것은 내가 본 최고의 답변입니다.
작업

1
@ N0thing "이렇게하면 단일 클라이언트가 동일한 대상 포트에 대해 최대 64k까지 서버에 연결할 수 있습니다." 따라서 실제로 클라이언트가 동일한 서버 및 포트에 동시에 두 번 또는 여러 번 연결하지 않으면 클라이언트는 최대 64K 이상의 연결을 가질 수 있습니다. 사실입니다. 그렇다면 클라이언트 측의 단일 포트에서 여러 다른 서버 프로세스에 연결할 수 있음을 의미합니다 (예 : 소켓 연결이 다름). 따라서 전체적으로 여러 클라이언트 소켓이 클라이언트 시스템의 동일한 포트에 상주 할 수 있습니까? "Remey Lebeau"답변에 대한 내 의견을 읽으십시오. 감사합니다 : D
Prem KTiw

6
@premktiw : 예, 여러 클라이언트 소켓이 다른 서버 IP / 포트 쌍에 연결되어 로컬 + 원격 쌍의 튜플이 고유 한 경우 동일한 로컬 IP / 포트 쌍에 동시에 바인딩 될 수 있습니다. 그리고 예, 클라이언트가 총 64K 이상의 동시 연결을 가질 수 있습니다. 단일 포트에서 서버 IP / 포트 쌍이 고유 한 한 잠재적으로 무한한 수의 서버 (사용 가능한 OS 리소스, 사용 가능한 라우터 포트 등에 의해 제한됨)에 연결할 수 있습니다.
레미 Lebeau

1
@RemyLebeau 만족합니다. 아주 아주 많이 감사합니다 : D
프렘 KTiw

1
@bibstha 들어오는 모든 연결이 거부 될 때 방화벽은 임의의 포트를 어떻게 처리합니까?
PatrykG 2017

35

연결된 소켓이 새 (전용) 포트에 할당됩니다.

그것은 일반적인 직관이지만 틀 렸습니다. 연결된 소켓이 새 / 전용 포트에 할당되지 않았습니다. TCP 스택이 충족해야하는 유일한 실제 제약은 (local_address, local_port, remote_address, remote_port)의 튜플이 각 소켓 연결에 대해 고유해야한다는 것입니다. 따라서 서버는 포트의 각 소켓이 다른 원격 위치에 연결되어있는 한 동일한 로컬 포트를 사용하는 많은 TCP 소켓을 가질 수 있습니다.

http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false 에서 "소켓 쌍"단락을 참조하십시오.


1
완벽한 답을 주셔서 감사합니다, Jeremy!
KJ

6
당신이 말하는 것은 전적으로 서버 측에 해당됩니다. 그러나 BSD 소켓 API의 구조 bind()connect()작업이 암시 적으로도 작업보다 우선 하기 때문에 나가는 클라이언트 측 포트가 실제로 고유해야 함을 의미합니다 .
Marquis of Lorne

1
@EJP 안녕하세요, 저는 bind()서버 측에서만 사용 한다고 생각했습니다. accept()?그래서 클라이언트 측도 특정 포트를 바인딩합니까?
GMsoF 2013

5
@GMsoF : bind()이전에 클라이언트 측에서 사용할 수 있습니다 connect().
Remy Lebeau 2014

10

이론적으로는 그렇습니다. 연습이 아니라. 대부분의 커널 (리눅스 포함)은 1 초를 허용하지 않습니다.bind() 이미 할당 된 포트에 . 이것을 허용하는 것은 정말로 큰 패치가 아니 었습니다.

개념적으로 소켓포트를 구별해야합니다. . 소켓은 양방향 통신 끝점입니다. 즉, 바이트를 보내고받을 수있는 "사물"입니다. 개념적인 것입니다. "socket"이라는 이름의 패킷 헤더에는 이러한 필드가 없습니다.

포트는 소켓을 식별 할 수있는 식별자입니다. TCP의 경우 포트는 16 비트 정수이지만 다른 프로토콜도 있습니다 (예 : 유닉스 소켓에서 "포트"는 본질적으로 문자열입니다).

주요 문제는 다음과 같습니다. 들어오는 패킷이 도착하면 커널은 대상 포트 번호로 소켓을 식별 할 수 있습니다. 가장 일반적인 방법이지만 유일한 가능성은 아닙니다.

  • 소켓은 수신 패킷의 대상 IP로 식별 할 수 있습니다. 예를 들어 두 개의 IP를 동시에 사용하는 서버가있는 경우입니다. 그런 다음 예를 들어 동일한 포트에서 다른 IP에서 다른 웹 서버를 실행할 수 있습니다.
  • 소켓은 소스 포트와 IP로도 식별 할 수 있습니다 . 이는 많은로드 밸런싱 구성의 경우입니다.

응용 프로그램 서버에서 작업하고 있기 때문에 그렇게 할 수 있습니다.


2
그는 1 초를 만드는 것에 대해 묻지 않았습니다 bind().
론의 후작

1
@ user207421 리스닝 소켓이 설정되지 않은 OS를 본 적이 bind()있습니까? 나는 그것을 상상할 수 있습니다. 예, 그것은 가능합니다. 그러나 사실은 WinSock과 Posix API가 그 bind()호출을 사용한다는 것입니다. 심지어 그들의 매개 변수화조차도 사실상 동일합니다. API에이 호출이 없더라도 들어오는 바이트를 어디에서 읽고 싶은지 어떻게 든 말해야합니다 .
peterh-Monica 복원

1
@ user207421 물론 100k 이상의 TCP 연결은 동일한 포트로 처리 할 수 ​​있습니다. listen()/ accept()API 호출은 커널이 들어오는 포트로 구분하는 방식으로 소켓을 생성 할 수 있습니다. OP의 질문은 그가 본질적으로 요구하는 방식으로 해석 될 수 있습니다. 제 생각에 그것은 매우 현실적이라고 생각하지만 이것이 그의 질문이 말 그대로 의미하는 것은 아닙니다.
peterh-Monica 복원

1

아니요. 특정 순간에 동일한 포트를 공유 할 수 없습니다. 그러나 다른 순간에 포트에 액세스하도록 애플리케이션을 만들 수 있습니다.

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