Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Сети > Функция getaddrinfo


Автор: salik 5.3.2011, 14:28
Помогите разобраться как получить данные из этой функции.

Код

procedure TForm2.Button2Click(Sender: TObject);
type

    //http://msdn.microsoft.com/en-us/library/ms737529%28v=VS.85%29.aspx
    Psockaddr = ^sockaddr;
    Paddrinfo = ^addrinfo;

    addrinfo =  record
      ai_flags     : Integer;
      ai_family    : Integer;
      ai_socktype  : Integer;
      ai_protocol  : Integer;
      ai_addrlen   : Cardinal;
      ai_canonname : PAnsiChar;
      ai_addr      : Psockaddr;
      ai_next      : ^addrinfo;
    end;


var  rez1, rez2 : Paddrinfo;
     wVersionRequested :Integer;
     lwsaData : WSAData;
     error : DWORD;
     hWs2_32 : THandle;
     getaddrinfo : function ( pNodeName : PAnsiChar;
                              pServiceName : PAnsiChar;
                              const pHints : Paddrinfo;
                              out ppResult : Paddrinfo
                             ) : DWORD; stdcall;
     freeaddrinfo : function ( ai : Paddrinfo) : DWORD; stdcall;

begin
  hWs2_32 := LoadLibraryA('Ws2_32.dll');
  getaddrinfo := GetProcAddress(hWs2_32,'getaddrinfo');
  freeaddrinfo := GetProcAddress(hWs2_32,'freeaddrinfo');
  if not (Assigned(getaddrinfo) and Assigned (freeaddrinfo)) then begin
    if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
    Memo1.Lines.Add('Необходимые процедуры или функции не найдены в библиотеке Ws2_32.dll.');
     Memo1.Lines.Add(IntToStr(GetLastError));
     if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
    Exit;
  end;

  try
    wVersionRequested := MakeWord(2,2);
    if (WSAStartup(wVersionRequested,lwsaData) <> 0) then
    begin
      Memo1.Lines.Add('Ошибка при вызове функции "WSAStartup".');
    end;

    rez1 := nil;
    rez2 := nil;
    if rez1 = nil then New(rez1);

    with rez1^ do begin
      ai_family := AF_INET;
      ai_socktype := SOCK_STREAM;
      ai_protocol := IPPROTO_TCP;
    end;

    error := getaddrinfo('forum.vingrad.ru',nil,rez1,rez2);

    if error = 0 then begin
      Memo1.Lines.Add(rez2^.ai_canonname);
    end;
    Memo1.Lines.Add('Код '+IntToStr(error));

  finally
    WSACleanup;
    if rez1 <> nil then begin
      Dispose(rez1);
      rez1 := nil;
    end;
    freeaddrinfo(rez2);
    if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
  end;

end;

Автор: MetalFan 5.3.2011, 15:27
Сначала неплохо бы сформулировать понятный другим вопрос.
Какие данные? То, что в Memo1 добавляются?
И вообще код местами бредовый написан.

Автор: salik 5.3.2011, 16:17
Цитата(MetalFan @ 5.3.2011,  15:27)
Сначала неплохо бы сформулировать понятный другим вопрос.
Какие данные? То, что в Memo1 добавляются?
И вообще код местами бредовый написан.

Хорошо. Я напишу как сам понимаю эту кухню, а вы меня поправьте, если не прав.

Основная задача - это определить IP по имени хоста. Функция getaddrinfo как раз этим и занимается.
После изучения MSDN пришел к следующему описанию структуры

Код

//http://msdn.microsoft.com/en-us/library/ms737529%28v=VS.85%29.aspx
    Psockaddr = ^sockaddr;
    Paddrinfo = ^addrinfo;

    addrinfo = packed record
      ai_flags     : Integer;
      ai_family    : Integer;
      ai_socktype  : Integer;
      ai_protocol  : Integer;
      ai_addrlen   : Cardinal;
      ai_canonname : PAnsiChar;
      ai_addr      : Psockaddr;
      ai_next      : ^addrinfo;
    end;


функция принимает 4 параметра. Первые два: имя хоста и имя сервиса, соответственно.
остальные два указатели на структуру addrinfo;

Из примера видно что третий параметр заполняется программистом, а четвертый служит как результат выполнения функции, который нужно потом проанализировать.

Сложность заключается в том, что я не знаком с языком C++ и приведенные http://msdn.microsoft.com/en-us/library/ms738520%28v=VS.85%29.aspx не дают ответы.

Проблема в том, что функция отрабатывает с ошибкой (11003). Это возможно связано с неправильным описанием структуры.
Если третий параметр задать nil, то ошибок нет, но опятьже, как мне получить IP в текстовом формате.

Автор: Snowy 5.3.2011, 16:26
Цитата(salik @  5.3.2011,  16:17 Найти цитируемый пост)
Основная задача - это определить IP по имени хоста
gethostbyname

Автор: salik 5.3.2011, 19:53
Цитата(Snowy @ 5.3.2011,  16:26)
gethostbyname

Эта функция уже устарела. И передовые разработчики Windows Sockets 2 рекомендуют использовать getaddrinfo.
Об этом написано на сайте http://msdn.microsoft.com/en-us/library/ms738524%28VS.85%29.aspx

Поймите меня правильно, надо разобраться именно со структурой и в каком виде "закинуть" в функцию параметры. Если написано, что надо определить адрес конкретной функцией, значит именно ей. 

 
Если есть что по существу, то помогите пожалуйста.

Автор: Чучмек 5.3.2011, 22:00
Заполни все поля структуры rez1
Код

rez1.ai_flags:=0;
rez1.ai_family:=af_inet;
rez1.ai_socktype:=SOCK_STREAM;
rez1.ai_protocol:=IPPROTO_TCP;
rez1.ai_addrlen:=0;
rez1.ai_canonname:=nil;
rez1.ai_addr:=nil;
rez1.ai_next:=nil;

Все поля кроме ai_flags,ai_family,ai_socktype и ai_protocol  должны содержать либо 0, либо nil 

Автор: Чучмек 5.3.2011, 22:41
Цитата(salik @  5.3.2011,  16:17 Найти цитируемый пост)
 но опятьже, как мне получить IP в текстовом формате


Код

type 
tm=array[0..3]of byte;
var
m:tm;
str:string;

m:=tm(rez2^.ai_addr^.sin_addr);
str:=inttostr(m[0])+'.'+inttostr(m[1])+'.'+inttostr(m[2])+'.'+inttostr(m[3]);

Автор: salik 5.3.2011, 22:41
Цитата(MetalFan @ 5.3.2011,  15:27)
Сначала неплохо бы сформулировать понятный другим вопрос.
Какие данные? То, что в Memo1 добавляются?
И вообще код местами бредовый написан.

А что именно бредового вы нашли в коде?


"Чучмек"  спасибо за участие.

Эта переменная (rez1) своего рода подсказка для функции. Это написано почти в самом конце комментариев к функции. 
Я так полагаю это должно ускорить вычисления.

Небольшая поправка в описании структуры. 
Оказывается указатель на тип SOCKADDR уже объявлен в модуле WinSock2 под именем LPSOCKADDR.

Немного изменив код
Код

if error = 0 then begin
      Memo1.Lines.Add('ai_family: '+ IntToStr(rez2^.ai_family));
      Memo1.Lines.Add('ai_socktype: '+ IntToStr(rez2^.ai_socktype));
      Memo1.Lines.Add('ai_addrlen: '+ IntToStr(rez2^.ai_addrlen));
      Memo1.Lines.Add('ai_canonname: '+ rez2^.ai_addr^.sa_data);
      Memo1.Lines.Add(inet_ntoa(in_addr(rez2^.ai_addr)));
      //WSAAddressToString(rez3^,DWORD(rez2.ai_addrlen),ww1,host,hlP);
    end;


получаем следующее значение поля Memo
Код

ai_family: 2
ai_socktype: 1
ai_addrlen: 16
ai_canonname: 
88.192.209.1
Код 0


Как видите адрес не правильный. Остается теперь с ним разобраться.


Автор: salik 6.3.2011, 09:16
Задача решена.
Ниже представлен рабочий код. Не забудьте подключить модуль WinSock2.

Код

procedure TForm2.Button2Click(Sender: TObject);
type

    //http://msdn.microsoft.com/en-us/library/ms737529%28v=VS.85%29.aspx
    Paddrinfo = ^addrinfo;

    addrinfo = packed record
      ai_flags     : Integer;
      ai_family    : Integer;
      ai_socktype  : Integer;
      ai_protocol  : Integer;
      ai_addrlen   : Cardinal;
      ai_canonname : PPAnsiChar;
      ai_addr      : LPSOCKADDR;
      ai_next      : ^addrinfo;
    end;


var  rez1, rez2 : Paddrinfo;
     lwsaData : WSAData;
     error : DWORD;
     hWs2_32 : THandle;
     getaddrinfo : function ( pNodeName : PAnsiChar;
                              pServiceName : PAnsiChar;
                              const pHints : Paddrinfo;
                              out ppResult : Paddrinfo
                             ) : DWORD; stdcall;
     freeaddrinfo : function ( ai : Paddrinfo) : DWORD; stdcall;


begin
  hWs2_32 := LoadLibraryA('Ws2_32.dll');
  getaddrinfo := GetProcAddress(hWs2_32,'getaddrinfo');
  freeaddrinfo := GetProcAddress(hWs2_32,'freeaddrinfo');
  if not (Assigned(getaddrinfo) and Assigned (freeaddrinfo)) then begin
    if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
     Memo1.Lines.Add('Необходимые процедуры или функции не найдены в библиотеке Ws2_32.dll.');
     Memo1.Lines.Add(IntToStr(GetLastError));
     if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
    Exit;
  end;

  try
    if WSAStartup(MakeWord(2,2),lwsaData) <> 0 then
    begin
      Memo1.Lines.Add('Ошибка при вызове функции "WSAStartup".');
    end;

    rez1 := nil;
    rez2 := nil;
    if rez1 = nil then New(rez1);
    with rez1^ do begin
      ai_flags:=0;
      ai_family:=af_inet;
      ai_socktype:=SOCK_STREAM;
      ai_protocol:=IPPROTO_TCP;
      ai_addrlen:=0;
      ai_canonname:=nil;
      ai_addr:=nil;
      ai_next:=nil;
    end;

    error := getaddrinfo('forum.vingrad.ru','hostnames',rez1,rez2);

    if error = 0 then begin
      Memo1.Lines.Add('ai_family: '+ IntToStr(rez2^.ai_family));
      Memo1.Lines.Add('ai_socktype: '+ IntToStr(rez2^.ai_socktype));
      Memo1.Lines.Add('ai_addrlen: '+ IntToStr(rez2^.ai_addrlen));
      Memo1.Lines.Add('IP : '+ inet_ntoa(SOCKADDR_IN(rez2^.ai_addr^).sin_addr));
    end;
    Memo1.Lines.Add('Код '+IntToStr(error));

  finally
    WSACleanup;
    if rez1 <> nil then begin
      Dispose(rez1);
      rez1 := nil;
    end;
    if rez2 <> nil then begin
      freeaddrinfo(rez2);
      rez2 := nil;
    end;
    if hWs2_32 <> 0 then FreeLibrary(hWs2_32);
  end;

end;

Автор: MetalFan 9.3.2011, 15:19
Цитата(salik @  5.3.2011,  22:41 Найти цитируемый пост)
А что именно бредового вы нашли в коде?

Да, есть пара замечаний по коду.
Код

...
    rez1 := nil;
    rez2 := nil;
    if rez1 = nil then New(rez1); // <========= ЗАЧЕМ эта проверка, если на две строчки выше и так rez1 = nil?
...


Код

    with rez1^ do begin
      ai_flags:=0;
      ai_family:=af_inet;
      ai_socktype:=SOCK_STREAM;
      ai_protocol:=IPPROTO_TCP;
      ai_addrlen:=0;
      ai_canonname:=nil;
      ai_addr:=nil;
      ai_next:=nil;
    end;

Зачем нужно по кускам "обнилять", если можно либо FillChar/ZeroMemory вызвать, либо сразу выделить память через AllocMem?

Автор: salik 9.3.2011, 15:57
Код

...
    rez1 := nil;
    rez2 := nil;
    if rez1 = nil then New(rez1); // <========= ЗАЧЕМ эта проверка, если на две строчки выше и так rez1 = nil?
...


Код

    with rez1^ do begin
      ai_flags:=0;
      ai_family:=af_inet;
      ai_socktype:=SOCK_STREAM;
      ai_protocol:=IPPROTO_TCP;
      ai_addrlen:=0;
      ai_canonname:=nil;
      ai_addr:=nil;
      ai_next:=nil;
    end;

Цитата

Зачем нужно по кускам "обнилять", если можно либо FillChar/ZeroMemory вызвать, либо сразу выделить память через AllocMem?


1) Если переменная "rez1" не будет равена "nil", то при вызове функции "new" будет ошибка. Это я использовал, чтобы подстраховаться.

2) А что касаемо заполнения, вы правы можно было вызвать сначала ZeroMemory, а потом подправить некоторые поля переменной. 
На сайте MSDN именно так и показано в примере на C++. Надо будет протестировать на досуге.


Автор: MetalFan 9.3.2011, 17:25
1. бесполезная подстраховка для данного случая.
2. А можно вообще обойтись AllocMem - она сразу память "зачистит" после выделения.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)