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