Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Delphi, .NET, Asm (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   [Delphi] Задержка в WinSock между send() и recv() (https://forum.antichat.xyz/showthread.php?t=144344)

GlooK 29.09.2009 04:22

[Delphi] Задержка в WinSock между send() и recv()
 
Возникла проблемка, которую я решил, но решение думаю не совсем верное.

Конструкция указанная в примере работала нормально,
до тех пор пока на запрос не возвращалось коротких ответов.
Но когда ответ приходит небольшой (например, 400 Bad Request, где одни хидеры), то возникает косяк.
Заключает он в том, что ответ не успевает приняться и следующий ответ от сервера включает в себя два(!) ответа.
В ответе идут хидеры 400 бед реквеста и 200 ок вместе.

Это не позволяет нормально анализировать принятые данные.

Код
Код:

function fWSWrite(hInput: string):string;
var
hOutput: array[0..5000] of char;
begin
Send(hSocket, hInput[1], length(hInput), 0);
FillChar(hOutput, SizeOf(hOutput), 0);
Recv(hSocket, hOutput, 5000, 0);
result := hOutput;
end;

Решение нашел такое:
Код:

function fWSWrite(hInput: string):string;
var
hOutput: array[0..5000] of char;
begin
Send(hSocket, hInput[1], length(hInput), 0);
sleep(1000);
FillChar(hOutput, SizeOf(hOutput), 0);
Recv(hSocket, hOutput, 5000, 0);
result := hOutput;
end;

Но считаю, что это не совсем корректно.
В MSDN ничего о задержки при отправке/приеме не увидел.

Подскажите, пожалуйста, верное решение?

UPD: Нашел вариант по лучше (через select):
Код:

function fWSWrite(hInput: string):string;
var
hOutput: string;
hArray: array[0..5000] of char;
rdfs: tfdset;
tmout: timeval;
ievnt: integer;
cntread: integer;
begin
cntread := 1;
Send(hSocket, hInput[1], length(hInput), 0);
FillChar(hArray, SizeOf(hArray), 0);
while (cntread > 0) do
begin
FD_ZERO(rdfs);
FD_SET(hSocket, rdfs);
tmout.tv_sec := 0;
tmout.tv_usec := 500000;
ievnt := select(0, @rdfs, nil, nil, @tmout);
if (ievnt <= 0) then break;
cntread := Recv(hSocket, hArray, 5000, 0);
hOutput := hOutput + copy(hArray, 1, cntread);
end;
result := hOutput;
end;


slesh 29.09.2009 09:08

Так это дело даже не в сети. потому что в винде идет полюбому буферизация и если неуспел считать то уже будут склеиваться данные.
Вообще всё зависит от реализации сервера.
Типичные сервера первым пакетом шлют хидер а вторым данные. А нетепичные сервера - шлют сразу всё вместе или хидер разбивают.
по этому когда шлеш данные то вписывай поле
Connection: Close
Как тока сделал send то в цикле гоняй recv пока будут идти данные.
Как только он вернул <=0 то выходи из цикла.
Также желательно замутить таймаут на recv чтобы небыло подвисаний на сверхглючных серваках (вернее самописном вебсервере где он не разрывает соединение потому что не воспринимает Connection: Close)

GlooK 29.09.2009 14:52

Про Connection: Close не знал. Спасибо.
Просто я делал эти запросы через браузер, и он этого не вписывал.

Тогда код получается такой (без таймаута)?

Код:

function fWSWrite(hInput: string):string;
var
hOutput: string;
hArray: array[0..5000] of char;
cntread: integer;
begin
cntread := 1;
Send(hSocket, hInput[1], length(hInput), 0);
FillChar(hArray, SizeOf(hArray), 0);
while (cntread > 0) do
begin
cntread := Recv(hSocket, hArray, 5000, 0);
hOutput := hOutput + copy(hArray, 1, cntread);
end;
result := hOutput;
end;

И если реализовывать таймаут, то обязательно переводить сокеты в неблокирующий режим?

Буду рад примерам из собственных реализаций:)

slesh 29.09.2009 15:03

наоборот строки поставь
Код:

while (true) do
begin
cntread := Recv(hSocket, hArray, 5000, 0);
if (cntread > 0) then hOutput := hOutput + copy(hArray, 1, cntread) else break;
end;
end;

насчет таймаутов я в своей статейке про юзанье winsock описывал как это делать. и там не нужно было переводить в неблокируемый режим. там юзалось setsockopt

GlooK 29.09.2009 15:18

Не получается.
Сейчас все запросы сразу посылаются. При снифе идет сразу четыре запроса GET.
Ответ только на первом.

Вот пример отправляемого запроса:
Код:

GET url HTTP/1.1
Host: url
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: close
Referer: url

P.S. Во всех запросах изменил Connection: keep-alive на Connection: close.

slesh 29.09.2009 15:27

вообще покажи полностью исходник.
Судя по тому что ты показывал у тебя дискриптор сокета - глобальная переменная.
Вот он и затирается скорее всего.

GlooK 29.09.2009 15:39

Цитата:

Сообщение от slesh
вообще покажи полностью исходник.
Судя по тому что ты показывал у тебя дискриптор сокета - глобальная переменная.
Вот он и затирается скорее всего.

Сорцы отправил в личку.
А сокет действительно глобальная переменная :)

slesh 29.09.2009 15:48

советую глянуть исходнки моего httpsender'a там всё нормально работало.
И там уже есть готовая функция для отправки и получения данных

GlooK 29.09.2009 15:53

Цитата:

Сообщение от slesh
советую глянуть исходнки моего httpsender'a там всё нормально работало.
И там уже есть готовая функция для отправки и получения данных

Спасибо за советы. Уже качаю :)

GlooK 29.09.2009 18:26

HTTP Sender архив битый. Перезалей плиз.

Кстати, при приеме данных, максимальное число принятых данных cntread не превышает 2048. Т.е. текст обрезается.
Код:

while (true) do
begin
cntread := Recv(hSocket, hArray, 5000, 0);
if (cntread > 0) then hOutput := hOutput + copy(hArray, 1, cntread) else break;
end;
end;

Нашел правильное решение. Думаю уже конечное.

Код:

function fWSWrite(hInput: string):string;
var
hOutput: string;
hArray: array[0..5000] of char;
iRead: integer;
begin
hOutput := '';
Send(hSocket, hInput[1], length(hInput), 0);
FillChar(hArray, SizeOf(hArray), 0);
while (TRUE) do
begin
iRead := Recv(hSocket, hArray, 5000, 0);
if (iRead > 0) then hOutput := hOutput + copy(hArray, 1, iRead);
if (iRead < 2048) then break;
end;
result := hOutput;
end;

Только вот вопрос: почему 2048?

P.S. Среда Turbo Delphi Lite (Portable)


Время: 22:06