복붙노트

[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가 문제를 해결할 수없는 것 같습니다. 고맙습니다!

해결법

  1. ==============================

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

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

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

    편집하다: 너 아직도 이걸로 문제가있는 것 같아. SO_REUSEADDR이 작동하지 않는 경우가 있습니다. 소켓을 바인드하고 동일한 대상 (SO_REUSEADDR 사용 가능)에 다시 연결하려고하면 TIME_WAIT가 여전히 유효합니다. 그러나 다른 호스트 : 포트에 연결할 수 있습니다.

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

  2. ==============================

    2.여기에 테스트를 거친 코드가 있으며 "주소가 이미 사용 중입니다"라는 오류는 절대 발생하지 않습니다. 이 파일을 파일에 저장하고 제공 할 HTML 파일의 기본 디렉토리에서 파일을 실행할 수 있습니다. 또한 서버를 시작하기 전에 프로그래밍 방식으로 디렉터리를 변경할 수 있습니다

    여기에 테스트를 거친 코드가 있으며 "주소가 이미 사용 중입니다"라는 오류는 절대 발생하지 않습니다. 이 파일을 파일에 저장하고 제공 할 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
    
  3. ==============================

    3.실제로 SO_REUSEADDR 플래그는 훨씬 더 큰 결과를 초래할 수 있습니다. SO_REUSADDR을 사용하면 TIME_WAIT에 걸린 포트를 사용할 수 있지만 그 포트를 사용하여 연결된 마지막 위치에 대한 연결을 설정할 수는 없습니다. 뭐? 로컬 포트 ​​1010을 선택하고 foobar.com 포트 300에 연결 한 다음 로컬에서 닫고 해당 포트를 TIME_WAIT에 남겨 두었다고 가정합니다. 로컬 포트 ​​1010을 바로 재사용하여 foobar.com 포트 300을 제외하고 어디에도 연결할 수 있습니다.

    실제로 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) tuple)이 어떤 포트에 바인드되고 소켓 현재 상태 (TIME_WAIT, CLOSING, FIN_WAIT 등)가 무엇인지 쉽게 알 수 있습니다.

    리눅스 네트워크 설정에 대한 좋은 설명은 https://serverfault.com/questions/212093/how-to-reduce-number-of-socket-in-time-wait에서 찾을 수 있습니다.

  4. ==============================

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

    바인딩하기 전에 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()
    

    이렇게하면 플래그를 설정하기 전에 서버가 바인딩되지 않습니다.

  5. ==============================

    5.TCPServer 또는 SimpleHTTPServer를 사용하여 문제가 발생하는 경우, SocketServer.TCPServer.allow_reuse_address (python 2.7.x)를 오버라이드 (override)합니다.  또는 socketserver.TCPServer.allow_reuse_address (파이썬 3.x) 속성

    TCPServer 또는 SimpleHTTPServer를 사용하여 문제가 발생하는 경우, SocketServer.TCPServer.allow_reuse_address (python 2.7.x)를 오버라이드 (override)합니다.  또는 socketserver.TCPServer.allow_reuse_address (파이썬 3.x) 속성

    class MyServer(SocketServer.TCPServer):
        allow_reuse_address = True
    
    server = MyServer((HOST, PORT), MyHandler)
    server.serve_forever()
    
  6. ==============================

    6.socket.socket ()은 socket.bind () 전에 실행하고 REUSEADDR을 사용한다고합니다.

    socket.socket ()은 socket.bind () 전에 실행하고 REUSEADDR을 사용한다고합니다.

  7. ==============================

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

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

    import SocketServer, socket
    
    class MyThreadingTCPServer(SocketServer.ThreadingTCPServer):
        def server_bind(self):
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.socket.bind(self.server_address)
    
    
  8. ==============================

    8.나를 위해 더 나은 해결책은 다음과 같습니다. 연결을 닫기위한 시도가 서버에 의해 수행 되었기 때문에 setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)는 아무 효과가 없었고 TIME_WAIT는 동일한 포트에서 오류가 발생하여 새 연결을 피했습니다.

    나를 위해 더 나은 해결책은 다음과 같습니다. 연결을 닫기위한 시도가 서버에 의해 수행 되었기 때문에 setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)는 아무 효과가 없었고 TIME_WAIT는 동일한 포트에서 오류가 발생하여 새 연결을 피했습니다.

    [Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted 
    

    마지막으로 솔루션을 사용하여 OS가 포트 자체를 선택하게하고 선행이 TIME_WAIT 인 경우 다른 포트가 사용됩니다.

    나는 바꿨다.

    self._socket.bind((guest, port))
    

    와:

    self._socket.bind((guest, 0))
    

    그것은 TCP 주소의 python 소켓 문서에 나와 있듯이 :

  9. ==============================

    9.나는 당신이 이미 대답을 받아 들였지만, 문제는 클라이언트 소켓에서 bind ()를 호출하는 것과 관련이 있다고 생각한다. 이것은 괜찮지 만 bind ()와 shutdown ()은 잘 작동하지 않는 것 같습니다. 또한 SO_REUSEADDR은 일반적으로 청취 소켓과 함께 사용됩니다. 즉 서버 측에서는

    나는 당신이 이미 대답을 받아 들였지만, 문제는 클라이언트 소켓에서 bind ()를 호출하는 것과 관련이 있다고 생각한다. 이것은 괜찮지 만 bind ()와 shutdown ()은 잘 작동하지 않는 것 같습니다. 또한 SO_REUSEADDR은 일반적으로 청취 소켓과 함께 사용됩니다. 즉 서버 측에서는

    당신은 ip / port를 connect ()에 전달해야합니다. 이렇게 :

    comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    comSocket.connect(('', 5555))
    

    bind ()를 호출하지 말고 SO_REUSEADDR을 설정하지 마십시오.

  10. ==============================

    10.물론 개발 환경에서 또 하나의 솔루션은 그것을 사용하는 프로세스를 죽이는 것입니다.

    물론 개발 환경에서 또 하나의 솔루션은 그것을 사용하는 프로세스를 죽이는 것입니다.

    def serve():
        server = HTTPServer(('', PORT_NUMBER), BaseHTTPRequestHandler)
        print 'Started httpserver on port ' , PORT_NUMBER
        server.serve_forever()
    try:
        serve()
    except Exception, e:
        print "probably port is used. killing processes using given port %d, %s"%(PORT_NUMBER,e)
        os.system("xterm -e 'sudo fuser -kuv %d/tcp'" % PORT_NUMBER)
        serve()
        raise e
    
  11. ==============================

    11.가장 좋은 방법은 터미널 fuser -k [PORT NUMBER] / tcp를 입력하여 해당 포트에서 프로세스를 종료하는 것입니다. fuser -k 5001 / tcp.

    가장 좋은 방법은 터미널 fuser -k [PORT NUMBER] / tcp를 입력하여 해당 포트에서 프로세스를 종료하는 것입니다. fuser -k 5001 / tcp.

  12. ==============================

    12.나는이 예외에 대한 또 다른 이유를 발견했다. Spyder IDE (제 경우에는 Raspbian의 Spyder3)에서 응용 프로그램을 실행할 때 프로그램이 ^ C 또는 예외로 종료되었지만 소켓은 여전히 ​​활성 상태입니다.

    나는이 예외에 대한 또 다른 이유를 발견했다. 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
    

    프로그램을 다시 실행하면 "Address already in use"가 발견되었습니다. IDE는 새로운 '실행'을 이전 '실행'에서 사용 된 소켓을 찾는 별도의 프로세스로 시작하는 것으로 보입니다.

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

    도와주지 않았어.

    Killing 프로세스 13210이 도움이되었습니다. 명령 줄에서 파이썬 스크립트 시작하기

    python3 <app-name>.py
    

    SO_REUSEADDR이 true로 설정된 경우 항상 잘 작동합니다. 새로운 Thonny IDE 또는 Idle3 IDE에는이 문제가 없습니다.

  13. from https://stackoverflow.com/questions/6380057/python-binding-socket-address-already-in-use by cc-by-sa and MIT license