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] %sn" % msg[1])
sys.exit(1)

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

comSocket.connect()

except socket.error, msg:

sys.stderr.write("[ERROR] %sn" % 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, кажется, не в состоянии решить эту проблему
Спасибо!

1181   12  

12 ответов:

попробуйте использовать SO_REUSEADDR опция сокета перед привязкой сокета.

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

Edit: Я вижу, у тебя все еще проблемы с этим. Есть случай, когда 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

на самом деле, флаг SO_REUSEADDR может привести к гораздо большим последствиям: SO_REUSADDR позволяет вам использовать порт, который застрял в TIME_WAIT, но вы все равно не можете использовать этот порт для установления соединения с последним местом, к которому он подключен. Что? Предположим, я выбираю локальный порт 1010 и подключаюсь к нему foobar.com порт 300, а затем закрыть локально, оставив этот порт в TIME_WAIT. Я могу сразу же использовать локальный порт 1010 для подключения к любому месту, кроме foobar.com порт 300.

однако вы можете полностью избегайте состояния TIME_WAIT, гарантируя, что удаленный конец инициирует закрытие (событие close). Таким образом, сервер может избежать проблем, позволяя клиенту закрыть. Протокол приложения должен быть разработан таким образом, чтобы клиент знал, когда закрывать. Сервер может безопасно закрываться в ответ на EOF от клиента, однако ему также нужно будет установить тайм-аут, когда он ожидает EOF в случае, если клиент покинул сеть неблагодарно. Во многих случаях просто ждать несколько секунд перед закрытие сервера будет адекватным.

Я также советую вам узнать больше о сети и сетевом программировании. Теперь вы должны по крайней мере, как работает протокол tcp. Протокол довольно тривиален и мал и, следовательно, может сэкономить вам много времени в будущем.

С netstat команда вы можете легко увидеть, какие программы ((имя_программы,pid) кортеж) привязаны к каким портам и каково текущее состояние сокета: TIME_WAIT, закрытие, FIN_WAIT и так далее.

очень хорошая объяснение сетевых конфигураций linux можно найти https://serverfault.com/questions/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()

это предотвращает привязку сервера до того, как мы получим возможность установить флаги.

в случае, если вы столкнулись с проблемой, используя TCPServer или SimpleHTTPServer, переопределить SocketServer.TCPServer.allow_reuse_address (python 2.7.икс) или socketserver.TCPServer.allow_reuse_address (python 3.x) атрибут

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

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

socket.socket() для выполнения socket.bind() и использовать REUSEADDR Как сказал

Как упоминал Фелипе Круз, вы должны установить SO_REUSEADDR перед привязкой. Я нашел решение на другом сайте - решение на другом сайте, воспроизведенное ниже

проблема в том, что параметр сокета SO_REUSEADDR должен быть установлен раньше адрес привязан к розетке. Это может быть сделано путем создания подклассов ThreadingTCPServer и переопределение метода server_bind следующим образом:

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)

для меня лучшим решением было следующее. Так как инициатива закрытия соединения была сделана сервером, то 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 

Я, наконец, использовал решение, чтобы позволить ОС выбрать сам порт, а затем используется другой порт, если прецедент все еще находится в TIME_WAIT.

Я заменил:

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

С:

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

как было указано в документация сокета python адреса tcp:

если указано, source_address должен быть 2-кортеж (хост, порт) для сокета для привязки к как его адрес источника перед подключением. Если хост или порт ‘ или 0 соответственно ОС по умолчанию будет использоваться.

Я знаю, что вы уже приняли ответ, но я считаю, что проблема связана с вызовом bind() на клиентском сокете. Это может быть нормально, но bind() и shutdown (), похоже, не очень хорошо играют вместе. Кроме того, SO_REUSEADDR обычно используется с сокетами прослушивания. т. е. на стороне сервера.

вы должны передавать и ip / порт для подключения (). Вот так:

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

не вызывайте bind (), не устанавливайте SO_REUSEADDR.

другое решение, в среде разработки, конечно, убивает процесс, используя его, например

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

Я думаю, что лучший способ - это просто убить процесс на этом порту, введя в терминал fuser -k [PORT NUMBER]/tcp, например,fuser -k 5001/tcp.

Я нашел еще одну причину для этого исключения. При запуске приложения из Spyder IDE (в моем случае это был Spyder3 на Raspbian) и программа завершилась ^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 помог. Запуск скрипта python из командной строки, например

python3 <app-name>.py

всегда работал хорошо, когда SO_REUSEADDR был установлен в true. У новой IDE Thonny или IDLE3 IDE не было этой проблемы.

Comments

    Ничего не найдено.