Определение 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;
Заключение
Для определения используемого IP-адреса при установленном соединении можно использовать SO_BSP_STATE, но с учетом особенностей работы с дополнительным буфером.
Альтернативный подход через IP Helper API может быть более надежным, но требует больше ресурсов.
В обоих случаях важно выполнять проверку после успешного установления соединения.
Выбор метода зависит от конкретных требований вашего приложения и версии операционной системы. Для большинства современных Windows-систем оба подхода должны работать корректно.
Методы определения IP-адреса сетевого интерфейса в Delphi/Pascal с использованием SO_BSP_STATE и IP Helper API, включая примеры кода и рекомендации.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.