Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Как определить IP-адрес сетевого интерфейса, используемого при соединении в Delphi/Pascal, если LocalAddr не задан?

Delphi , Интернет и Сети , TCP/IP

Определение IP-адреса сетевого интерфейса при соединении в Delphi/Pascal

В работе с сетевыми приложениями на Delphi/Pascal иногда возникает необходимость определить, какой именно IP-адрес из нескольких доступных на компьютере был использован для установления соединения, когда параметр LocalAddr не был задан явно. Эта статья рассматривает несколько подходов к решению данной задачи.

Проблема

Когда компьютер имеет несколько сетевых интерфейсов и вы устанавливаете соединение без явного указания LocalAddr, операционная система самостоятельно выбирает интерфейс для соединения. В некоторых случаях разработчику необходимо узнать, какой именно IP-адрес был использован.

Решение с использованием SO_BSP_STATE

Начиная с Windows Vista, появилась возможность использовать опцию сокета SO_BSP_STATE для получения информации о соединении. Однако, как выяснилось, официальная документация Microsoft не совсем точна в описании необходимого размера буфера.

Вот пример реализации на Object Pascal:

type
  TSocketAddress = record
    Sockaddr: PSockAddrIn6;
    SockaddrLength: Integer;
  end;

  TCSAddrInfo = record
    LocalAddr: TSocketAddress;
    RemoteAddr: TSocketAddress;
    iSocketType: Integer;
    iProtocol: Integer;
    Buffer: array[0..63] of Byte; // Дополнительное пространство для структур
  end;

procedure TForm1.WSocket1SessionConnected(Sender: TObject; ErrCode: Word);
var
  AddrInfo: ^TCSAddrInfo;
  OptLen: Integer;
  Res: Integer;
begin
  // Выделяем память в куче (не в стеке!)
  OptLen := SizeOf(TCSAddrInfo);
  GetMem(AddrInfo, OptLen);
  try
    FillChar(AddrInfo^, OptLen, 0);

    Res := getsockopt(WSocket1.HSocket, SOL_SOCKET, SO_BSP_STATE, 
                     PAnsiChar(AddrInfo), OptLen);
    if Res = SOCKET_ERROR then
      ShowMessage('Ошибка getsockopt: ' + IntToStr(WSAGetLastError))
    else
    begin
      // Обработка полученных данных
      if AddrInfo^.LocalAddr.Sockaddr <> nil then
        ShowMessage('Локальный IP: ' + 
                   WSocket_inet_ntoa(AddrInfo^.LocalAddr.Sockaddr^.sin_addr));
    end;
  finally
    FreeMem(AddrInfo);
  end;
end;

Важные замечания:
1. Структура должна размещаться в куче, а не в стеке
2. Необходимо выделять дополнительное пространство (буфер) после основной структуры
3. Проверку нужно выполнять после установления соединения (в событии OnSessionConnected)

Альтернативное решение с использованием IP Helper API

Если SO_BSP_STATE не возвращает ожидаемый результат (например, возвращает 0.0.0.0), можно использовать функции из библиотеки IP Helper:

uses
  IpHlpApi, IpTypes;

function GetLocalIPForConnection(RemoteIP: string; RemotePort: Word): string;
var
  Table: PMibTcpTable;
  Size: Cardinal;
  i: Integer;
  Err: DWORD;
  Addr: TInAddr;
begin
  Result := '';
  Size := 0;

  // Получаем размер таблицы TCP-соединений
  GetTcpTable(nil, @Size, True);
  GetMem(Table, Size);
  try
    // Получаем таблицу соединений
    Err := GetTcpTable(Table, @Size, True);
    if Err = NO_ERROR then
    begin
      // Ищем нужное соединение
      for i := 0 to Table.dwNumEntries - 1 do
      begin
        Addr.S_addr := Table.table[i].dwRemoteAddr;
        if (WSocket_inet_ntoa(Addr) = RemoteIP) and 
           (ntohs(Table.table[i].dwRemotePort) = RemotePort) then
        begin
          Addr.S_addr := Table.table[i].dwLocalAddr;
          Result := WSocket_inet_ntoa(Addr);
          Break;
        end;
      end;
    end;
  finally
    FreeMem(Table);
  end;
end;

Заключение

  1. Для определения используемого IP-адреса при установленном соединении можно использовать SO_BSP_STATE, но с учетом особенностей работы с дополнительным буфером.
  2. Альтернативный подход через IP Helper API может быть более надежным, но требует больше ресурсов.
  3. В обоих случаях важно выполнять проверку после успешного установления соединения.

Выбор метода зависит от конкретных требований вашего приложения и версии операционной системы. Для большинства современных Windows-систем оба подхода должны работать корректно.

Создано по материалам из источника по ссылке.

Методы определения IP-адреса сетевого интерфейса в Delphi/Pascal с использованием SO_BSP_STATE и IP Helper API, включая примеры кода и рекомендации.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: TCP/IP ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-07 17:02:33/0.00636887550354/0