Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2008.03.23;
Скачать: [xml.tar.bz2];

Вниз

сокеты на winapi   Найти похожие ветки 

 
Dmitry S ©   (2008-01-20 12:12) [0]

Написал такую функцию. Делает простой запрос к HTTP серверу и возвращает ответ вместе с заголовками (или пустую строку, если ошибка):

function Query(host, uri:string; Method:string = "GET"; Data:string = ""):string;
var
 MySocket:TSocket;
 Addr:TSockAddr;
 HostEnt:PHostEnt;
 Buf:PChar;
 Text, S:String;
 Num: Integer;
begin
 Result := "";

 // Получаем IP
 HostEnt := gethostbyname(PChar(host));
 if HostEnt = nil then Exit;
 if HostEnt.h_length < 4 then Exit;

 // Устанавливаем адрес
 Addr.sin_family := AF_INET;
 Addr.sin_port := htons(80); // http
 CopyMemory(@Addr.sin_addr, HostEnt^.h_addr^, 4);
 FillMemory(@Addr.sin_zero, 8, 0);

 // Создаем сокет
 MySocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if MySocket = INVALID_SOCKET then Exit;

 // Канектим его
 Num := connect(MySocket, Addr, sizeof(Addr));
 if Num = SOCKET_ERROR then
 begin
   closesocket(MySocket);
   Exit;
 end;

 // Подготавливаем запрос
 Text :=
   Method + " " + uri + " HTTP/1.0"#13#10 +
   "Host: " + host + #13#10 +
   "Connection: Close" + #13#10;

 if Method = "POST" then
 begin
   Str(Length(Data), S);

   Text := Text +
   "Content-Type: application/x-www-form-urlencoded"#13#10 +
   "Content-Length: " + S + #13#10#13#10 + Data;
 end
 else
 begin
    Text := Text +#13#10;
 end;

 // Посылаем запрос

 while true do
 begin
   Num := send(MySocket, PChar(Text)^, Length(Text), 0);
   if Num = SOCKET_ERROR then
   begin
     closesocket(MySocket);
     Exit;
   end;
   if Num = 0 then sleep(100);
   if Num = Length(Text) then break;
   Delete(Text, 1, Num);
 end;

 // Получаем ответ
 GetMem(Buf, 1024);
 FillMemory(Buf, 1024, 0);

 while true do
 begin
   Num := recv(MySocket, Buf^, 1023, 0);

   if Num = SOCKET_ERROR then
   begin
     closesocket(MySocket);
     Result := "";
     FreeMem(Buf);
     Exit;
   end;

   if Num = 0 then break;
   Result := Result + Buf;
   FillMemory(Buf, 1024, 0);
 end;
 FreeMem(Buf);
 closesocket(MySocket);
end;


Все ли правильно?

Меня интересует момент:
HostEnt := gethostbyname(PChar(host));

По идее gethostbyname выделяет память под ответ, но она нигде не освобождается. Как и где ее освобождать, чтобы не вызвать утечки?

Ну и в остальном правильно ли ?


 
Dmitry S ©   (2008-01-20 12:15) [1]

Правильно ли я сделал работаю с функцией send? Сокет по умолчанию ведь должен быть блокирующемся. И вот я не знаю заблокируется ли он до тех пока сам все не отправит, или нужно ему "помогать" как я это сделал.


 
DVM ©   (2008-01-20 12:24) [2]


> По идее gethostbyname выделяет память под ответ, но она
> нигде не освобождается. Как и где ее освобождать, чтобы
> не вызвать утечки?

Справку читал?

The pointer which is returned points to a structure which is allocated by Windows Sockets. The application must never attempt to modify this structure or to free any of its components. Furthermore, only one copy of this structure is allocated per thread, and so the application should copy any information which it needs before issuing any other Windows Sockets function calls.


 
Dmitry S ©   (2008-01-20 12:28) [3]


> DVM ©

читал... Но ведь я таких вызовов могу сделать сколь угодно много, и тем самым закончу память. Я ведь даже нигде не указываю, что больше не хочу работать с этой структурой. Винда ее вечно чтоли будет хранить?


 
DVM ©   (2008-01-20 12:30) [4]


> читал... Но ведь я таких вызовов могу сделать сколь угодно
> много, и тем самым закончу память.

читай еще, но теперь внимательно.


 
DVM ©   (2008-01-20 12:36) [5]

кстати, лучше так вот наверное:

function InetAddr(const AHost: string): integer;
var
 PHost:PChar;
 HostEnt: PHostEnt;
begin
 if AHost = "" then
   result := INADDR_NONE
 else
   begin
     PHost := PChar(AHost);
     Result := inet_addr(PHost);
     if Result = INADDR_NONE then
       begin
         HostEnt := GetHostByName(PHost);
         if HostEnt <> nil then
           Result := integer(pointer(HostEnt^.h_addr^)^);
       end;
   end;
end;


И потом

Addr.sin_addr.s_addr := InetAddr(Host);


 
Dmitry S ©   (2008-01-20 12:59) [6]

А все понял... Одна на поток.

А чем твой способ лучше?


 
DVM ©   (2008-01-20 13:12) [7]


> А чем твой способ лучше?

на вход можно подать как IP так и имя - результат будт один s_addr.

И все вот эти строки твои:

CopyMemory(@Addr.sin_addr, HostEnt^.h_addr^, 4);
FillMemory(@Addr.sin_zero, 8, 0);


не нужны.


 
DVM ©   (2008-01-20 13:13) [8]


> А все понял... Одна на поток.

А память будет освобождена скорее всего вызовом WSACleanUp


 
Dmitry S ©   (2008-01-20 13:45) [9]

А WSACleanUp надо вызывать в конце работы процесса или потока?


 
DVM ©   (2008-01-20 14:05) [10]


> Dmitry S ©   (20.01.08 13:45) [9]

 
 WsaErr := WSAStartUp($0101, Wsa);
 if WsaErr = 0 then
   begin
     try
       ... тут работает с сетью ...
     finally
       WSACleanUp;
     end;
   end


Т.е. в том же потоке, что и WSAStartUp


 
Dmitry S ©   (2008-01-20 14:20) [11]

Еще два вопроса тогда...
1. $0101 обязательно?  я использую вторую версию, это ничего страшного?

2. Вобщем то приложение у меня такое:
Есть основной поток с формами и прочим. И есть дополнительный поток, который создается только для того чтобы получить данные с сервера. Затем его убивают, и через некоторое время создают заново. Потом после того как он получил данные его опять убивают. И так далее, пока пользователь не закроет программу.
В данном случае я должен в каждом новом потоке выполнять WSAStartUp и WSACleanUp? Или один раз при старте и завершении приложения?


 
DVM ©   (2008-01-20 14:27) [12]


> 1. $0101 обязательно?  я использую вторую версию, это ничего
> страшного?

Тогда ставь свою версию место этого


> В данном случае я должен в каждом новом потоке выполнять
> WSAStartUp и WSACleanUp?

да


 
ketmar ©   (2008-01-20 17:06) [13]

>[12] DVM©(20.01.08 14:27)
а нафига? завсегда хватало один раз на всех…


 
DVM ©   (2008-01-20 17:27) [14]


> ketmar ©   (20.01.08 17:06) [13]

Конечно достаточно вызвать один раз WSAStartUp и один раз WSACleanUp на весь процесс, главное, чтобы количество вызовов WSAStartUp и WSACleanUp совпадало. Просто бывает иногда удобнее (случай автора вопроса имхо) отделить код ответственный за работу с сокетами в отдельный модуль. Например, в модуль в котором описан класс потока. Тогда и WSAStartUp и WSACleanUp лучше разместить там. И ничего страшного, что вызовов будет несколько, зато обе функции будут находиться рядом.
Есть еще правда вариант с initialization / finalization модуля.


 
ketmar ©   (2008-01-20 17:35) [15]

>[14] DVM©(20.01.08 17:27)
>Есть еще правда вариант с initialization / finalization модуля.
как я лично завсегда и делал. %-)


 
ketmar ©   (2008-01-20 17:36) [16]

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


 
wp2 ©   (2008-01-20 23:10) [17]

>if HostEnt.h_length < 4 then Exit;

впервые вижу такое...


 
DVM ©   (2008-01-20 23:18) [18]


> впервые вижу такое...

Это чтобы не нарваться на адреса IPv3 и ниже вероятно :)


 
Dmitry S ©   (2008-02-09 11:37) [19]


> впервые вижу такое...

На случай, если адресов не будет: 0 < 4 ;)



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2008.03.23;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.5 MB
Время: 0.006 c
2-1204115691
Иван Владимирович
2008-02-27 15:34
2008.03.23
MSSqlServer 2000


10-1144060743
Vir
2006-04-03 14:39
2008.03.23
TWebBrowser root


2-1203513538
..::KraN::..
2008-02-20 16:18
2008.03.23
Вставка картинки в Synedit


2-1203800509
antonn
2008-02-24 00:01
2008.03.23
Глупый вопрос по Application.ProcessMessages;


2-1203937022
Washington
2008-02-25 13:57
2008.03.23
Параметры владельца





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский