Сервер Debian с SO_REUSEADDR на Python

Вопрос или проблема

Я написал простой сокет-сервер на Python 3.11.2 на Bookworm. Очень простой на данный момент.

root@9b6f7:~# python3 w1.py
Сокет успешно создан
Traceback (most recent call last):
    Файл "/root/w1.py", строка 7, в <module>
        s.bind(('', port))
OSError: [Errno 98] Адрес уже используется

Во время тестирования и написания скрипта я продолжаю получать ошибку “Адрес уже используется” при запуске скрипта, и, изучая информацию об этой ошибке на сокетах, я увидел, что мне нужно добавить SO_REUSEADDR.

Я добавил s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) в свой скрипт, но все равно продолжаю получать ошибку “Адрес уже используется”, особенно если скрипт падает.

Вот мой простой скрипт, создающий ошибку при запуске. Может ли кто-нибудь помочь, где я мог ошибиться.

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

print ("Сокет успешно создан")
port = 1712
s.bind(('', port))
print ("сокет привязан к %s" %(port))
# перевести сокет в режим прослушивания
s.listen(5)
print ("сокет прослушивается")
# вечный цикл до прерывания или возникновения ошибки
while True:
    # Установить соединение с клиентом.
    c, addr = s.accept()
    print ('Получено соединение от', addr )
    while True:
        data = c.recv(1024)
        if not data:
            break
    c.send('Спасибо за подключение'.encode())
    
    c.close()

Спасибо

.

Ответ или решение

Конечно, давайте подробно разберем вашу проблему с сокет-сервером на Python, работающим на Debian (версия Bookworm) и использование опции SO_REUSEADDR.

Проблема:
Вы создали простой TCP-сервер, но сталкиваетесь с ошибкой OSError: [Errno 98] Address already in use при попытке забиндить порт, даже после добавления опции SO_REUSEADDR.

Анализ:
Ошибка "Address already in use" указывает на то, что сокет, пытающийся привязать порт, сталкивается с конфликтом: порт уже занят другим процессом или недавним завершением предыдущего сокет-соединения. Опция SO_REUSEADDR предназначена для того, чтобы сокет мог использовать локальный адрес, который может быть занят в результате активации предыдущей программы, но она не стимулирует немедленное освобождение портов, которые используются активно.

Потенциальные причины и решения:

  1. Процессы, использующие порт:

    • Проверьте, если другие процессы используют этот порт. Используйте команду lsof -i :1712 или netstat -tuln | grep 1712 для проверки, занят ли порт другим процессом. Если занят, уберите этот процесс.
  2. Исходящий таймаут:

    • Даже с SO_REUSEADDR, пока полностью не истечет время ожидания завершения предыдущего соединения (TIME_WAIT), может появляться эта ошибка. Вы можете использовать команду ss -s для просмотра состояния сокетов и выявления долгоживущих соединений в состоянии TIME_WAIT.
  3. Изменение версии Python или конфигурации:

    • Перепроверьте вашу версию Python. Иногда возможно, что дело в особенностях версии интерпретатора. Также убедитесь в корректности вашей системы конфигурации.
  4. Правильное закрытие сокетов:

    • Убедитесь, что сокеты корректно закрываются методом close() после их использования. Это предотвратит зависание открытых соединений и снижает вероятность появления TIME_WAIT.
  5. Обработка исключений:

    • Добавьте обработку исключений, чтобы ваш сервер корректно обрабатывал ошибки во время выполнения. Это улучшит стабильность работы серверного скрипта.

Пример улучшенного кода:

import socket

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    print("Socket создан успешно")
    port = 1712
    s.bind(('', port))
    print(f"Сокет привязан к порту {port}")

    s.listen(5)
    print("Сокет находится в режиме прослушивания")

    while True:
        client_socket, addr = s.accept()
        print(f'Получено соединение от {addr}')
        try:
            while True:
                data = client_socket.recv(1024)
                if not data:
                    break
                client_socket.sendall('Спасибо за подключение'.encode())
        finally:
            client_socket.close()
except Exception as e:
    print(f"Произошла ошибка: {e}")
finally:
    s.close()

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

Оцените материал
Добавить комментарий

Капча загружается...