답변:
서버 소켓은 단일 포트에서 수신 대기합니다. 해당 서버에 설정된 모든 클라이언트 연결은 연결 의 서버 측에있는 동일한 수신 포트와 연관됩니다 . 설정된 연결은 클라이언트 측 및 서버 측 IP / 포트 쌍의 조합으로 고유하게 식별됩니다. 동일한 서버의 여러 연결은 서로 다른 클라이언트 측 IP / 포트 쌍과 연결되어있는 한 동일한 서버 측 IP / 포트 쌍을 공유 할 수 있으며, 서버 는 사용 가능한 시스템 리소스가 허용하는 한 많은 클라이언트를 처리 할 수 있습니다. 에.
온 클라이언트 측 , 그것은 새로운 아웃 바운드 연결이 임의 사용에 대한 일반적이다 클라이언트 측 당신이 짧은 시간에 많은 연결을 할 경우 사용 가능한 포트 밖으로 실행하는 것이 가능하다이 경우 포트.
그렇다면 서버가 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 주소간에 혼동이 없습니다.
연결된 소켓이 새 (전용) 포트에 할당됩니다.
그것은 일반적인 직관이지만 틀 렸습니다. 연결된 소켓이 새 / 전용 포트에 할당되지 않았습니다. 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 에서 "소켓 쌍"단락을 참조하십시오.
bind()
는 connect()
작업이 암시 적으로도 작업보다 우선 하기 때문에 나가는 클라이언트 측 포트가 실제로 고유해야 함을 의미합니다 .
bind()
서버 측에서만 사용 한다고 생각했습니다. accept()?
그래서 클라이언트 측도 특정 포트를 바인딩합니까?
bind()
이전에 클라이언트 측에서 사용할 수 있습니다 connect()
.
이론적으로는 그렇습니다. 연습이 아니라. 대부분의 커널 (리눅스 포함)은 1 초를 허용하지 않습니다.bind()
이미 할당 된 포트에 . 이것을 허용하는 것은 정말로 큰 패치가 아니 었습니다.
개념적으로 소켓 과 포트를 구별해야합니다. . 소켓은 양방향 통신 끝점입니다. 즉, 바이트를 보내고받을 수있는 "사물"입니다. 개념적인 것입니다. "socket"이라는 이름의 패킷 헤더에는 이러한 필드가 없습니다.
포트는 소켓을 식별 할 수있는 식별자입니다. TCP의 경우 포트는 16 비트 정수이지만 다른 프로토콜도 있습니다 (예 : 유닉스 소켓에서 "포트"는 본질적으로 문자열입니다).
주요 문제는 다음과 같습니다. 들어오는 패킷이 도착하면 커널은 대상 포트 번호로 소켓을 식별 할 수 있습니다. 가장 일반적인 방법이지만 유일한 가능성은 아닙니다.
응용 프로그램 서버에서 작업하고 있기 때문에 그렇게 할 수 있습니다.
bind()
.
bind()
있습니까? 나는 그것을 상상할 수 있습니다. 예, 그것은 가능합니다. 그러나 사실은 WinSock과 Posix API가 그 bind()
호출을 사용한다는 것입니다. 심지어 그들의 매개 변수화조차도 사실상 동일합니다. API에이 호출이 없더라도 들어오는 바이트를 어디에서 읽고 싶은지 어떻게 든 말해야합니다 .
listen()
/ accept()
API 호출은 커널이 들어오는 포트로 구분하는 방식으로 소켓을 생성 할 수 있습니다. OP의 질문은 그가 본질적으로 요구하는 방식으로 해석 될 수 있습니다. 제 생각에 그것은 매우 현실적이라고 생각하지만 이것이 그의 질문이 말 그대로 의미하는 것은 아닙니다.