로컬 주소 127.0.0.1:45000의 연결이 ESTABLISHED 상태인 경우 로컬 프로세스가 동일한 포트 45000에서 바인딩하고 수신할 수 있습니까? 아니면 포트가 사용 중인 것으로 간주되어 바인딩 요청이 차단됩니까?
답변1
bind
로 설정하면 실패 합니다 . 정말로 이 작업을 수행하려면 다른 패킷 캡처 방법을 사용하여 무슨 일이 일어나고 있는지 확인해야 합니다. 진행 중인 정보 및 기타 커널 진단도 옵션입니다.errno
EINVAL
tcpdump
strace
답변2
전체 이야기는 "당신이 의미하는 바에 따라 다릅니다"입니다. 제어할 수 없는 기존 프로그램과 정확히 동일한 IP 및 포트에서 수신 대기하려는 경우 다른 사람들이 지적한 것처럼 운이 좋지 않을 수 있습니다. 그러나 다음과 같은 경우에는
- 다른 IP 주소를 수신할 수 있습니다.
- 원본 애플리케이션을 어느 정도 제어할 수 있습니다.
그렇다면 이 기사의 나머지 부분에도 관심이 있으실 것입니다.
동일한 포트, 다른 인터넷 프로토콜(IP) 주소
프로그램을 사용할 때바인딩(2)AF_INET 소켓의 경우 포트와 IP 주소를 지정하는 소켓에 주소를 할당하는 시스템 호출입니다. 따라서 포트는 동일하지만 IP 주소가 다른 두 주소는 서로 다르므로 충돌 없이 별도로 할당할 수 있습니다. 예를 들어소캇셸의 루프백 인터페이스 IP 주소에서 포트 9000에 바인딩할 수 있습니다.
socat TCP4-LISTEN:9000,bind=127.0.0.1 STDOUT
동일한 포트에 바인딩하지만 외부 IP 주소는 다른 포트에 있습니다.
socat TCP4-LISTEN:9000,bind=10.0.2.15 STDOUT
두 프로세스 모두 수신 대기 중인 IP 주소와 포트의 연결을 수락할 수 있습니다. 그러나 누군가가 와일드카드 주소 0.0.0.0을 수신하고 있는 경우 첫 번째 프로세스가 시스템의 모든 IP에 바인딩되므로 더 구체적인 주소에 바인딩할 수 없습니다.
동일한 포트, 동일한 IP
기본적으로 두 프로세스는 서로 다른 두 파일 설명자를 동일한 주소에 바인딩할 수 없습니다. Linux에서 이 작업을 시도하면 다음에서 EADDRINUSE가 반환됩니다 bind(2)
.
socat TCP4-LISTEN:9000,bind=127.0.0.1 STDOUT
2014/11/07 00:10:13 socat[21202] E bind(3, {AF=2 127.0.0.1:9000}, 16): Address already in use
귀하의 질문과 후속 조치를 보면 현재 필요한 포트를 사용하는 프로그램에 대해 많은 통제권을 갖고 있지 않은 것 같습니다. 그러나 이렇게 하면 한 프로세스는 포트 + IP 주소에 연결되고 다른 프로세스는 동일한 주소에서 수신 대기할 수 있습니다. 예를 들어, 많은 서버 애플리케이션은 다음을 수행합니다.
- 주요 프로세스에는 바인드(), 청취() 및 수락() 연결이 있습니다.
- 허용된 연결을 처리하기 위해 새 프로세스를 포크하면 기본 프로세스가 반환되어 새로 들어오는 연결을 accept()하려고 시도합니다.
이 경우 해당 포트에 ESTABLISHED 연결이 있는 하위 프로세스가 표시되는 반면 상위 프로세스에는 동일한 포트에 LISTENing 소켓이 있습니다.
최근 Linux 커널에서는 완전히 관련되지 않은 두 프로세스가 SO_REUSEPORT 소켓 옵션을 사용하여 동일한 주소에 바인딩될 수 있습니다. 한 프로세스가 소켓에 SO_REUSEPORT 옵션을 설정하면 첫 번째 프로세스와 동일한 유효 UID를 가진 다른 프로세스도 SO_REUSEPORT 옵션을 설정하고 동일한 주소에 바인딩할 수 있습니다.
불행하게도 내 버전에는 socat
간단한 TCP 예제를 제공하기 어렵게 만드는 버그가 있는 것 같습니다. 그러나 아래에 짧고 잘못 작성된 예제 프로그램을 제공했습니다. 두 개의 서로 다른 쉘에서 동일한 사용자로 프로그램을 실행하면 둘 다 문제 없이 바인딩()됩니다.
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 127.0.0.1:9000 *:* users:(("bind_tcp_reusep",pid=21254,fd=3))
LISTEN 0 128 127.0.0.1:9000 *:* users:(("bind_tcp_reusep",pid=21253,fd=3))
아이디어는 네트워크 서버를 작성하는 사람들에게 많은 수의 동시 연결을 처리할 수 있는 응용 프로그램을 만들 수 있는 새로운 도구를 제공하는 것입니다.
다음 LWN 문서에서는 이 옵션의 사용 사례에 대한 좋은 개요를 제공합니다.
http://lwn.net/Articles/542629/
설정된 연결의 트래픽 보기
robbat2가 언급했듯이 tcpdump
기존 트래픽을 모니터링하려는 경우 이것이 가장 좋은 옵션입니다.
SO_REUSEPORT 프로그램 예
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <errno.h>
int main() {
struct sockaddr_in bind_addr;
struct sockaddr peer_addr;
int optval = 1;
int tcp_socket;
int err;
int addr_len = sizeof(struct sockaddr);
memset(&bind_addr, 0, sizeof(struct sockaddr_in));
memset(&peer_addr, 0, sizeof(struct sockaddr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(9000);
if (inet_pton(AF_INET, "127.0.0.1", &(bind_addr.sin_addr)) != 1) {
perror("inet_pton");
exit(1);
}
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(tcp_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
err = bind(tcp_socket, (const struct sockaddr *)&bind_addr, sizeof(struct sockaddr));
if (err != 0) {
perror("bind failed");
exit(1);
}
err = listen(tcp_socket, 256);
if (err != 0) {
perror("listen failed");
exit(1);
}
accept(tcp_socket, &peer_addr, &addr_len);
}
답변3
로컬 Tomcat 서버를 시작하고 설정된 연결의 소스 주소로 나열된 포트에 바인딩하도록 구성을 조정하여 직접 시도했습니다. 이를 통해 다른 ESTABLISHED 연결이 이미 해당 포트를 로컬 주소로 사용하고 있는 동안 서버가 LISTEN 상태의 포트에 바인딩할 수 있습니다.