7

Поиск локальных IP-адресов с использованием стандартной библиотеки Python

6

Как я могу найти локальные IP-адреса (то есть 192.168.x.x или 10.0.x.x) в Python, независимо от платформы и используя только стандартную библиотеку?

5 ответ(ов)

6

Вы правы, данный способ может показаться немного "хитрым", однако он действительно работает и позволяет получить IP-адрес вашего устройства в сети. Приведенный вами код создает UDP-сокет и устанавливает его соединение с удаленным адресом (в данном случае с DNS-сервером 8.8.8.8). После этого метод getsockname() возвращает локальный адрес, используемый для этого соединения.

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

Вот аналогичный пример кода на Python:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])  # Выводит локальный IP-адрес
s.close()

Если вы испробовали это на Windows и у вас все сработало, то это хороший индикатор того, что подход работает в разных операционных системах. Однако при использовании такого метода всегда стоит помнить о зависимости от сетевых условий.

5

Это решение не всегда будет работать, так как может возвращать 127.0.0.1, если на вашем компьютере в файле /etc/hosts имя хоста сопоставлено с 127.0.0.1. Устранить эту проблему можно с помощью использования socket.getfqdn(), как показывает gimel. Однако стоит отметить, что ваше имя хоста должно быть разрешаемым для корректной работы данного подхода.

0

Если компьютер имеет маршрут к Интернету, данный код всегда будет работать для получения предпочтительного локального IP-адреса, даже если файл /etc/hosts настроен некорректно.

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))  # connect() для UDP не отправляет пакеты
local_ip_address = s.getsockname()[0]

Этот код создает UDP-сокет и устанавливает соединение с публичным DNS-сервером Google (8.8.8.8). Метод connect() используется, чтобы получить локальный IP-адрес, который будет использоваться для подключения к указанному адресу. В случае, если сервер недоступен, метод connect() все равно не отправляет никаких пакетов, что делает его безопасным для этого применения. Таким образом, даже если возникают проблемы с конфигурацией сети, код способен корректно вернуть локальный IP-адрес.

0

Метод Sокета

Смотрите это сообщение на StackOverflow.

Недостатки:

  • Не кроссплатформенный.
  • Требуется больше кода для обработки ошибок, зависимого от наличия конкретных адресов в интернете.
  • Это также не сработает, если вы находитесь за NAT.
  • Вероятно, создает UDP-соединение, которое не зависит от доступности DNS (обычно принадлежащего вашему ISP) (см. другие ответы с идеями, например, использовать 8.8.8.8 – сервер Google (который, кстати, также является DNS-сервером)).
  • Убедитесь, что целевой адрес находится вне досягаемости, например, используйте числовой IP-адрес, который согласно спецификации гарантированно не используется. НЕ используйте такие домены, как fakesubdomain.google.com или somefakewebsite.com; вы все равно будете спамить эту сторону (сейчас или в будущем), и при этом будете также засорять свои сетевые устройства.

Метод Рефлектора

(Обратите внимание, что это не отвечает на вопрос ОП о локальном IP-адресе, например, 192.168...; он предоставляет ваш публичный IP-адрес, который может оказаться более желаемым в зависимости от случая использования.)

Вы можете запросить некоторые сайты, такие как whatismyip.com (но с API), например:

from urllib.request import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

или, если вы используете Python 2:

from urllib import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

Преимущества:

  • Одним из плюсов этого метода является его кроссплатформенность.
  • Он работает и за «некрасивыми» NAT (например, вашим домашним маршрутизатором).

Недостатки (и обходные решения):

  • Требует, чтобы данный сайт был доступен, формат не изменился (что маловероятно), и ваши DNS-серверы работали. Можно смягчить эту проблему, запрашивая также других сторонних рефлекторов IP-адресов на случай сбоя.
  • Возможен вектор атаки, если вы не запрашиваете несколько рефлекторов (чтобы предотвратить ситуацию, когда скомпрометированный рефлектор говорит вам, что ваш адрес - это не так), или если вы не используете HTTPS (чтобы предотвратить атаку «человек посередине», выдающего себя за сервер).

Редактировать: Хотя изначально я думал, что эти методы действительно плохие (если не использовать много запасных вариантов, код может стать неактуальным через несколько лет), это ставит вопрос: «что такое интернет?». Компьютер может иметь множество интерфейсов, подключенных к разным сетям. Для более подробного описания темы, погуглите шлюзы и маршруты. Компьютер может получить доступ к внутренней сети через внутренний шлюз или получить доступ к всемирной паутине через шлюз, например, на маршрутизаторе (что обычно и происходит). Локальный IP-адрес, о котором спрашивает ОП, четко определен только в отношении одного канала передачи, поэтому вы должны это указать («это сетевой адаптер или Ethernet-кабель, о котором мы говорим?»). На этот вопрос может быть несколько неопределенных ответов, однако глобальный IP-адрес в сети интернет, вероятно, имеет четкое определение (при отсутствии значительной фрагментации сети): вероятно, это обратный путь через шлюз, который может получить доступ к TLD.

0

Чтобы получить IP-адрес сетевого интерфейса в Linux с использованием Python, воспользуйтесь следующей программой:

import socket
import struct
import fcntl

# Создаем сокет
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockfd = sock.fileno()

# Константа для получения IP-адреса
SIOCGIFADDR = 0x8915

def get_ip(iface='eth0'):
    # Создаем структуру запроса для интерфейса
    ifreq = struct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
    try:
        # Выполняем ioctl для получения информации об IP
        res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
    except:
        return None
    # Извлекаем IP-адрес из результата
    ip = struct.unpack('16sH2x4s8x', res)[2]
    return socket.inet_ntoa(ip)

# Пример использования
print(get_ip('eth0'))  # Выводит IP-адрес (например, '10.80.40.234')

В этом коде мы создаем сокет и используем системный вызов ioctl, чтобы получить IP-адрес для указанного интерфейса (eth0 по умолчанию). Если интерфейс недоступен или возникает ошибка, функция возвращает None. Обратите внимание, что вам может понадобиться запустить скрипт с правами суперпользователя, чтобы получить доступ к интерфейсам сети.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь