Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.09.10;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.03 c
15-1155466218
Andy BitOff
2006-08-13 14:50
2006.09.10
Windows в 85 году


3-1151493853
antoxa2005
2006-06-28 15:24
2006.09.10
Подскажите, а как пользоваться with lock в FB?


15-1155101339
Ega23
2006-08-09 09:28
2006.09.10
С Днём рождения! 9 августа


15-1155651735
hamster
2006-08-15 18:22
2006.09.10
Не запускается Windows


15-1155632295
vajo
2006-08-15 12:58
2006.09.10
где находится адресная Outlook Express?