[python] Python : 바인딩 소켓 :“이미 사용중인 주소”

TCP / IP 네트워크의 클라이언트 소켓에 관한 질문이 있습니다. 내가 사용한다고 해보자

try:

    comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

except socket.error, msg:

    sys.stderr.write("[ERROR] %s\n" % msg[1])
    sys.exit(1)

try:
    comSocket.bind(('', 5555))

    comSocket.connect()

except socket.error, msg:

    sys.stderr.write("[ERROR] %s\n" % msg[1])

    sys.exit(2)

생성 된 소켓은 포트 5555에 바인딩됩니다. 문제는 연결을 종료 한 후

comSocket.shutdown(1)
comSocket.close()

wireshark를 사용하면 양쪽에서 FIN, ACK 및 ACK로 소켓이 닫혀있는 것을 볼 수 있습니다. 포트를 다시 사용할 수 없습니다. 다음과 같은 오류가 발생합니다.

[ERROR] Address already in use

다음 번에 동일한 포트를 계속 사용할 수 있도록 포트를 즉시 지우는 방법이 궁금합니다.

comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

setsockopt가 문제를 해결할 수없는 것 같습니다. 감사합니다!



답변

SO_REUSEADDR소켓을 바인딩하기 전에 소켓 옵션을 사용해보십시오 .

comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

편집 :
여전히 문제가있는 것 같습니다. SO_REUSEADDR작동하지 않는 경우 가 있습니다. 소켓을 바인드하고 동일한 대상 ( SO_REUSEADDR활성화 된 상태)에 다시 연결하려고하면TIME_WAIT 하면 여전히 유효합니다. 그러나 다른 호스트 : 포트에 연결할 수 있습니다.

몇 가지 해결책이 떠 오릅니다. 다시 연결할 수있을 때까지 계속 재 시도 할 수 있습니다. 또는 클라이언트가 (서버가 아닌) 소켓 닫기를 시작하면 마술처럼 작동합니다.


답변

다음은 내가 테스트 한 완전한 코드이며 절대적으로 “이미 사용중인 주소”오류를 표시하지 않습니다. 이를 파일에 저장하고 제공하려는 HTML 파일의 기본 디렉토리 내에서 파일을 실행할 수 있습니다. 또한 서버를 시작하기 전에 프로그래밍 방식으로 디렉토리를 변경할 수 있습니다.

import socket
import SimpleHTTPServer
import SocketServer
# import os # uncomment if you want to change directories within the program

PORT = 8000

# Absolutely essential!  This ensures that socket resuse is setup BEFORE
# it is bound.  Will avoid the TIME_WAIT issue

class MyTCPServer(SocketServer.TCPServer):
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = MyTCPServer(("", PORT), Handler)

# os.chdir("/My/Webpages/Live/here.html")

httpd.serve_forever()

# httpd.shutdown() # If you want to programmatically shut off the server


답변

TCPServer또는을 사용하여 문제가 발생하는 SimpleHTTPServer경우 SocketServer.TCPServer.allow_reuse_address(python 2.7.x) 또는 socketserver.TCPServer.allow_reuse_address(python 3.x) 속성을 재정의하십시오.

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

server = MyServer((HOST, PORT), MyHandler)
server.serve_forever()


답변

이 링크 에 따르면

실제로 SO_REUSEADDR 플래그는 훨씬 더 큰 결과를 초래할 수 있습니다. SO_REUSADDR은 TIME_WAIT에 멈춘 포트를 사용할 수 있도록 허용하지만 여전히 해당 포트를 사용하여 마지막으로 연결된 위치에 연결할 수는 없습니다. 뭐? 로컬 포트 ​​1010을 선택하고 foobar.com 포트 300에 연결 한 다음 로컬에서 닫고 해당 포트를 TIME_WAIT에 남겨 둔다고 가정합니다. 로컬 포트 ​​1010을 즉시 재사용하여 foobar.com 포트 300을 제외한 모든 곳에 연결할 수 있습니다.

그러나 원격 끝이 종료 (닫기 이벤트)를 시작하도록함으로써 TIME_WAIT 상태를 완전히 피할 수 있습니다. 따라서 서버는 클라이언트를 먼저 닫아 문제를 피할 수 있습니다. 응용 프로그램 프로토콜은 클라이언트가 종료시기를 알 수 있도록 설계되어야합니다. 서버는 클라이언트의 EOF에 대한 응답으로 안전하게 닫을 수 있지만 클라이언트가 네트워크를 비정상적으로 떠난 경우 EOF를 예상 할 때 시간 제한을 설정해야합니다. 많은 경우 서버가 닫히기 전에 몇 초만 기다리면 충분합니다.

또한 네트워킹 및 네트워크 프로그래밍에 대해 자세히 알아 보도록 조언합니다. 이제 최소한 tcp 프로토콜이 어떻게 작동하는지 확인해야합니다. 프로토콜은 매우 사소하고 작기 때문에 미래에 많은 시간을 절약 할 수 있습니다.

netstat명령을 어떤 프로그램 ((program_name, pid) 튜플)이 어떤 포트에 바인딩되어 있으며 소켓 현재 상태는 무엇인지 쉽게 확인할 수 있습니다 : TIME_WAIT, CLOSING, FIN_WAIT 등.

Linux 네트워크 구성에 대한 정말 좋은 설명은 /server/212093/how-to-reduce-number-of-sockets-in-time-wait 에서 찾을 수 있습니다 .


답변

바인딩하기 전에 allow_reuse_address를 설정해야합니다. SimpleHTTPServer 대신 다음 스 니펫을 실행하십시오.

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False)
httpd.allow_reuse_address = True
httpd.server_bind()
httpd.server_activate()
httpd.serve_forever()

이것은 플래그를 설정하기 전에 서버가 바인딩되는 것을 방지합니다.


답변

Felipe Cruze가 언급했듯이 바인딩하기 전에 SO_REUSEADDR을 설정해야합니다. 다른 사이트에서 솔루션을 찾았습니다-다른 사이트의 솔루션, 아래에 재현 됨

문제는 주소가 소켓에 바인딩되기 전에 SO_REUSEADDR 소켓 옵션을 설정해야한다는 것입니다. ThreadingTCPServer를 서브 클래 싱하고 다음과 같이 server_bind 메서드를 재정 의하여이를 수행 할 수 있습니다.

import SocketServer, 소켓

클래스 MyThreadingTCPServer (SocketServer.ThreadingTCPServer) :
    def server_bind (self) :
        self.socket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind (self.server_address)


답변

이 예외에 대한 또 다른 이유를 찾았습니다. Spyder IDE (제 경우에는 Raspbian의 Spyder3)에서 응용 프로그램을 실행하고 프로그램이 ^ C 또는 예외로 종료 될 때 소켓은 여전히 ​​활성 상태였습니다.

sudo netstat -ap | grep 31416
tcp  0  0 0.0.0.0:31416  0.0.0.0:*    LISTEN      13210/python3

프로그램을 다시 실행하면 “이미 사용중인 주소”가 발견되었습니다. IDE는 이전 ‘실행’에서 사용 된 소켓을 찾는 별도의 프로세스로 새로운 ‘실행’을 시작하는 것 같습니다.

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

도움이되지 않았습니다.

킬링 프로세스 13210이 도움이되었습니다. 다음과 같은 명령 줄에서 파이썬 스크립트 시작

python3 <app-name>.py

SO_REUSEADDR이 true로 설정되면 항상 잘 작동했습니다. 새로운 Thonny IDE 또는 Idle3 IDE에는이 문제가 없었습니다.