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

Вниз

WinSock Api Функция "Recv" : Что делать если буфер пуст ?   Найти похожие ветки 

 
SPACE!!   (2006-04-16 18:29) [0]

Функция Recv вызывается в потоке TServerChatThread и потом
вызывается процедура ReciveChatData .
Вот она

procedure TServerChatThread.ReciveChatData;
var
ipos,g,i : integer;
argument1,argument2,argument3,argument4,commnd : string;
ClientData : String;

begin
g :=1;
i :=sizeof(g);
getsockopt(CLChat_socket,SOL_SOCKET,SO_ERROR,@g,i);

if (g = 0) and (In_buf[0] <> #0) then
begin
  ClientData :=In_buf;
  ipos := pos(#32,ClientData);\\До пробела идет команда.
  commnd := copy(ClientData,1,ipos-1);
  Delete(ClientData,1,ipos);
  try
   if strtoint(commnd) = CL_CHAT_MESSAGE  then
    begin
     if Assigned(ChatForm) then ChatForm.Memo1.Lines.Append(Oponnents.Nik_2+" : "+ClientData);

     Form1.Memo1.Lines.Append(In_buf);
    end;
   if strtoint(commnd) = CL_CHAT_TRYCONNECT  then
    begin
     ShowDialogForm(clientdata,svChat);
    end;

   Except
    Form1.Memo1.Lines.Append(In_buf);
   end;
 end

else
begin
 bconnect :=False;
 OnLostConnect;
if g = 0 then
  begin
   closesocket(CLChat_socket);
  end;
end;

FillChar(In_buf,sizeof(In_buf),#0);

end;


Локально все работает прекрасно, а вот на просторах инета
In_buf оказывается в некоторых моментах пуст. Теоретически
помойму такого недолжно быть так-как сокеты работают в синхронном режиме.Я попытался несколько изменить код примерно иак ;

if (g = 0)  then
begin
 while In_buf[0] = #0 do
 begin
  ;
 end;  
 ClientData :=In_buf;
 ....
 ....

Также пробовал такой вариант :


if (g = 0)  then
begin
 if In_buf[0] = #0 then Exit;

 ClientData :=In_buf;
 ....
 ....

Тоже ничего хорошего.. Help :)


 
Сергей М. ©   (2006-04-17 08:32) [1]

Показывай, как Recv() используешь ..


 
SPACE!!   (2006-04-17 17:55) [2]

Recv вызывается в потоке TServerChatThread , в процедуре Execute :

while ((not Terminated) and (bconnect=true)) do
 begin
  recv(ChatClientSocket,Chat_In_buf,sizeof(Chat_In_buf),0);
  Synchronize(ReciveServerData);
 end;


 
SPACE!!   (2006-04-17 20:17) [3]

Тоесть Synchronize(ReciveChatData).
Я непонимаю как Recv может вернуть управления при этом не заполнить
буфер и фиг что после этого сделаеш. Впринципе я фижу пару вариантов,
но это извращение.
1 . В процедуре ReciveChatData сделать так

var
z : integer
.....
z :=0;
while z <> 5000 do
begin
 if In_buf[0] <> #0 then break;
 inc(z);
end;

Но вопервых буфер не всегда успевает заполниться во вторых тормозит.

2. В конец данных ставить определенный символ и сканировать буффер
пока не найдется этот символ, но это тоже гнилой вариант.


 
Rouse_ ©   (2006-04-17 21:48) [4]

Т.е. ты просто в цикле ждешь ответа от синхронного recv? Мощно задвинуто...
recv вообщето функция которая возвращает результат...
Ты его не анализируешь.


 
SPACE!!   (2006-04-17 22:39) [5]

Что-ты хочешь сказать что Recv вернет 0 в случаях когда буффер пуст ?
Ну допустим и что я после этого должен опять вызывать Recv ? :))
Насколько я понимаю событие возникшее при получение данных сбросится как только вызов Recv вернет управление ...


 
SPACE!!   (2006-04-18 00:12) [6]

Блин ну что неужто все забыли что такое Winsock и перешли на компоненты ?


 
SPACE!!   (2006-04-18 01:01) [7]

Вот нашел нормальное описание функции Recv .

Функция Recv копирует пришедшие данные из входного буфера сокета в буфер, заданный параметром Buf, но не более BufLen байт. Скопированные данные удаляются из буфера сокета. При этом все полученные данные сливаются в один поток, поэтому получатель может самостоятельно выбирать, какой объём данных считывать за один раз. Если за один раз была скопирована только часть пришедшего пакета, оставшаяся часть не пропадает, а будет скопирована при следующем вызове Recv. Функция Recv возвращает количество байт, скопированных в буфер. Если на момент её вызова входной буфер сокета пуст, она ждёт, когда там что-то появится, затем копирует полученные данные и лишь после этого возвращает управление вызвавшей её программе. Если Recv возвращает 0, это значит, что удалённый сокет корректно завершил соединение. Если соединение завершено некорректно (например, из-за обрыва кабеля или сбоя удалённого компьютера), функция завершается с ошибкой (т.е. возвращает Socket_Error).

Нифига не понимаю.

> Функция Recv возвращает количество байт, скопированных в
> буфер


Вопщето она возвращает размер переданого ей буфера ...

Что касается ошибок то их обрабатывает getsockopt заметьте.

var
z : integer
.....
z :=0;
while z <> 3000 do
begin
if In_buf[0] <> #0 then break;
inc(z);
end;


A вот при таком варианте наблюдалось следущая картина.
Я клиент (Diul-Up) шлю 7 сообсчений тестеру(ADSL)  все доходят.
Он шлет сообсчения некоторые как в землю проваливаются видно
таймаут в 3000 маловат был . :))
И я уверен на 99% что у сервера при получении данных не возникает
фигни когда буфер оказывается пустым.  Да и вопще судя из вышеописаного
описания Recv возвращает управления в случаи ошибок или получения данных исключения конешно асинхронный режим. Либо где-то ошибка либо я незнаю. Но данные застревают где-то. И клиент и сервер выполняются в отдельных потоках.


 
Сергей М. ©   (2006-04-18 08:33) [8]


> Нифига не понимаю


Что не понимаешь ?
В этом тексте логика работы Recv() достаточно подробно описана ..


> Что касается ошибок то их обрабатывает getsockopt заметьте


1. если результат N > 0, в указанный буфер будет записано N байт;

2. если результат N = 0, следует закрывать гнездо - соединение закрыто по инициативе партнера;

3. если результат N < 0, произошла сетевая ошибка, код которой можно и нужно получить следом же вызовом WSAGetLastError()

Т.е. вызывать ReciveServerData() следует лишь в том случае, если N > 0, во всех остальных случаях вызов ReciveServerData() попросту не имеет смысла, ибо нет данных для обработки.

Да и смысл синхронизации тобой вызова ReciveServerData() тоже не понятен ...

Получается что у тебя организован одновременный прием данных от разных клиентов, а обработка принимаемых данных происходит последовательно...


 
Сергей М. ©   (2006-04-18 08:34) [9]


> где-то ошибка либо я незнаю


На то есть встроенный отладчик.


 
SPACE!!   (2006-04-18 20:24) [10]


> 1. если результат N > 0, в указанный буфер будет записано
> N байт;
>
> 2. если результат N = 0, следует закрывать гнездо - соединение
> закрыто по инициативе партнера;
>
> 3. если результат N < 0, произошла сетевая ошибка, код которой
> можно и нужно получить следом же вызовом WSAGetLastError()


i :=sizeof(g);
getsockopt(CLChat_socket,SOL_SOCKET,SO_ERROR,@g,i);
if (g = 0) and (In_buf[0] <> #0) then
 begin
 
  Если нет ошибок то выполняем если они имеются значит закрываем
  соеденения. Если их нет но буфер пуст значит Recv вернул 0 .  
  Чем тебя не устраивает такая проверка ? Я конешно поставлб проверку
  на Recv , но знаешь я практически уверен что это не поможет.
 end;


> Да и смысл синхронизации тобой вызова ReciveServerData()
> тоже не понятен

Она оаньше была другой да и чем она тебе мешает ?
 

> На то есть встроенный отладчик.

Ну это ты вопще зря.


> 1. если результат N > 0, в указанный буфер будет записано

> N байт;


Еще раз повторю фунция Recv  возвращает  размер переданого ей буфера  
тоесть если мы передаем буфер размером 1024 байта, а пришло всего 10 байт, то  N будет равен 1024 , но не 10 ... (Ненадо морочить людям головы)


 
Rouse_ ©   (2006-04-18 21:57) [11]


> Еще раз повторю фунция Recv  возвращает  размер переданого
> ей буфера  

Это у тебя странный Recv. MSDN же имеет по этому поводу другое мнение, а именно:

> If no error occurs, recv returns the number of bytes received


 
SPACE!!   (2006-04-18 23:49) [12]


> Это у тебя странный Recv. MSDN же имеет по этому поводу
> другое мнение, а именно

Да да признаю ошибочка , яж забыл что Send шлет и #0-ли. А зря. Ясно
надо будет сделать динамический буфер.. Но все-равно это не решает
проблемы. Думаю послезавтра вечером проверю свои идеи и сообщу
о результатах.


 
Сергей М. ©   (2006-04-19 09:19) [13]


> SPACE!!   (18.04.06 20:24) [10]


> Чем тебя не устраивает такая проверка ?


Да зачем так извращаться-то ?
Ты тем самым замесил в одну кучу транспортный и прикладной уровни, а теперь разобраться не можешь, что, где и почему у тебя происходит ...

Проверка на ошибку ввода-вывода должна происходить на транспортном уровне, а ты ее почему-то вынес в прикладной. ReciveChatData() - это у тебя как раз и есть прикладной уровень, т.е. уровень, предназначенный для обработки фактически принятых данных. Прикладному уровню не должно быть никакого дела до транспортного, его задача - обработать то что ему передано на обработку, поэтому процедуру ReciveChatData() следовало бы  даже назвать иначе ...
Проиллюстрирую эту мысль в псевдокоде, чтобы тебе стало понятно :

var Buf: array[0..4096] of Byte; //буфер можно распределить и иначе, это не суть как важно

..

//транспортый уровень
//цикл, принимающий поток данных от партнера по соединению
while Connected do
try
 BytesReceived := Recv(hSocket, Buf, SizeOf(Buf));
 CheckWSAResult(BytesReceived); //здесь генерируется исключение, если BytesReceived < 0
 Connected := BytesReceived > 0;
//если имеются фактияески принятые данные, то передаем их на обработку прикл.уровню, укказав их местонахождение и размер
 if Connected then ProcessIncomingData(Buf, BytesReceived);
except
 Connected := False;
end;

..

//прикл.уровень
procedure ProcessIncomingData(var Data; Size: Integer);
begin
.. разбор принятого ..
end;


 
SPACE!!   (2006-04-19 20:21) [14]

Сергей М спасибо конешно за жедания помочь , но вопервых все ошибки
связанные с передачей данных решаются как раз таки транспортным протоколом... тоесть данные просто не дайдут до уровня приложения
если где-то к примеру в заголовке небудет  совподать чексумма я прав или
я прав :) ?  А вот при не нормальном закрытие соеденения при передаче
или при получение данных возникнет ошибка это-то и проверяет getsockopt.
Во вторых локально все работает прекрасно. В третих яж сказал что
добавлю такую проверку. Ну а вопще я вроде нашел ошибку проверю только завтра : Дело в том что функция Send шлет буфер размером MAXPACKETSIZE(65535) предварительно он заполняется нулями

FillChar(buf,sizeof(buf),#0);

Ну я незнаю просто меня переклинило что он шлет только полезную информацию и все тут :))) . Вот данные и застревают.... Впервые
наверно допускаю такую смешную ошибку на которую уходит столько времени . Ладненька народ завтра проверю сообщу результаты .


 
Сергей М. ©   (2006-04-21 08:53) [15]


> вопервых все ошибки
> связанные с передачей данных решаются как раз таки транспортным
> протоколом


Не путай транспортный протокол в соответствии с OSI (в дан.случае - TCP) с транспортным уровнем в прикладной задаче (в дан.случае - набор каких-то твоих ф-ций/процедур, занимающихся исключительно установкой соединения,  приемом-передачей неких данных по этому соединению и обработкой ошибок приема-передачи).


> если где-то к примеру в заголовке небудет  совподать чексумма
> я прав или


"чексуммы" тебя никак не касаются в дан.случае.
Этим занимается TCP/IP.


> добавлю такую проверку


Тогда getsockopt() становится нужным как 5-е колесо телеге.


 
SPACE!!   (2006-04-22 23:55) [16]

Ох ну и нудный ты Сергей  =)


> > вопервых все ошибки
> > связанные с передачей данных решаются как раз таки транспортным
> > протоколом
>
>
> Не путай транспортный протокол в соответствии с OSI (в дан.
> случае - TCP) с транспортным уровнем в прикладной задаче
> (в дан.случае - набор каких-то твоих ф-ций/процедур, занимающихся
> исключительно установкой соединения,  приемом-передачей
> неких данных по этому соединению и обработкой ошибок приема-
> передачи).

А никто и не путает это ты чего-то путаешь. Eсть ошибка вызов getsockopt
в твоем транспортно-прикладном уровне все обнаружит главное правильно
пользоваться этой функцией и проверять не заполнен ли буфер с прошлого вызова. Поверь мне getsockopt прекрасно справляется со своими обязаностями и в моем случаи проверка Recv была бы уже лишней ... :)
И конешно было бы лишней вызывать getsockopt еслиб я проверял Recv .


> "чексуммы" тебя никак не касаются в дан.случае.
> Этим занимается TCP/IP

Вот именно я тебе это и хотел сказать, тоесть нас интересует только в порядке ли "socket" я думаю первым делом этим и занимаются вызовы
Recv и Send не удивлюсь даже если они используют для этого функцию getsockopt :))) .

Только давай небудем спорить как лучше проверять ... :)  Так-как я уже
сказал что добавлю проверку на Recv.  

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


 
Сергей М. ©   (2006-04-24 08:49) [17]

var
 MyDynArray: array of Byte;
..

SendResult := Send(@MyDynArray[0], Length(MyDynArray));
..
RecvResult := Recv(@MyDynArray[0], Length(MyDynArray));


 
SPACE!!   (2006-04-24 23:54) [18]

Спасибо , я честно говоря хотел так попробовать , но чето не рискнул =)



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

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

Наверх





Память: 0.52 MB
Время: 0.043 c
2-1155884743
her
2006-08-18 11:05
2006.09.10
TBytes и TIdSocketHandle


15-1155301390
ms1
2006-08-11 17:03
2006.09.10
Дeти - цвeты жизни.


15-1156085949
vajo
2006-08-20 18:59
2006.09.10
Автовключение компьютера.


2-1156164736
Alral
2006-08-21 16:52
2006.09.10
Full-screen


1-1153996431
zrv
2006-07-27 14:33
2006.09.10
формирование XML-файла





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский