Подтвердить что ты не робот

Python: Binding Socket: "Адрес уже используется"

У меня есть вопрос относительно клиентского сокета в сети 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()

Используя wirehark, я вижу, что сокет закрыт FIN, ACK и ACK с обеих сторон, я не могу использовать порт снова. Я получаю следующую ошибку:

[ERROR] Address already in use

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

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

setsockopt, похоже, не в состоянии решить проблему Спасибо!

4b9b3361

Ответ 1

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

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

Edit: Я вижу, у вас все еще есть проблемы с этим. Существует случай, когда SO_REUSEADDR не будет работать. Если вы попытаетесь связать сокет и снова подключиться к одному и тому же адресату (при включенном SO_REUSEADDR), то TIME_WAIT все равно будет действовать. Однако он позволит вам подключиться к другому хосту: порт.

На ум приходит пара решений. Вы можете продолжить повторную попытку, пока не сможете снова получить соединение. Или, если клиент инициирует закрытие сокета (а не сервера), тогда он должен волшебным образом работать.

Ответ 2

Вот полный код, который я тестировал, и абсолютно НЕ дает мне ошибку "адрес уже используется". Вы можете сохранить это в файле и запустить файл из базового каталога 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

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

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

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

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

Действительно хорошее объяснение сетевых конфигураций Linux можно найти https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait.

Ответ 4

Перед связыванием необходимо установить 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

Если вы столкнулись с проблемой, используя 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()

Ответ 6

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

Ответ 7

Как отметил Фелипе Круз, вы должны установить 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)

Ответ 8

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

Ответ 9

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

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

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

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

Ответ 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

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

Ответ 12

Я обнаружил, что просто использование этого в цикле while исправляет ошибку

if KeyboardInterrupt == 1 :
    sys.exit(1);