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

Вниз

Проблема с TClientSocket   Найти похожие ветки 

 
Gek1   (2005-04-21 16:13) [0]

Помогите решить проблему с TClientSocket.
Использую неблокирующий режим. Проблема заключается в том, что иногда пакет, который я отправляю серверу - недоходит. Происходит это очень редко - пару раз в сутки. При этом TClientSocket не генерирует ни каких ошибок и продолжает работать дальше.

Код отправки пакета:

procedure TCharacter.GameSendSHex(SHex : String);
var I, Len: Integer;
   Buff : PArray;
begin
if not GameClient.Active then Exit;
if Length(SHex) <= 1 then Exit;
Len := Length(SHex);
{Конвертируем SHex в Array of byte}
if (Len = 0) or (Len mod 2 <> 0) then
begin
AddToDebugLog(GetName+": "+"Error converting SHex to Array of byte"+ " (Game Socket)");
Disconnect;
Exit;
end;
New(Buff);
for I := 0 to ((Len div 2)-1) do BufIn^[I] := StrToInt("$"+Copy(SHex, I*2+1, 2));
Len := Len div 2;
{Пытаемся отослать байтики серверу}
if GameClient.Socket.Connected then
begin
try
GameClient.Socket.SendBuf(BufOut^,Len);
AddtoDebugLog(GetName+": "+"Client send "+inttostr(Len)+" bytes: "+SHex);
{Здесь я отловлю пакеты, отсылка которых влияет на св-ства класса}
CheckOutgoingPackets(SHex);
except
AddToDebugLog(GetName+": "+"Exception in Socket.SendBuf");
end;
end
else AddToDebugLog(GetName+": "+"You try send to shard some bytes, but you not Connected");
Dispose(Buff);
Dispose(BufOut);
end; {End TCharacter.GameSendSHex}


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


 
Gek1   (2005-04-21 16:15) [1]


> Dispose(BufOut);

Это не ошибка а опечатка. Этой строки нету.


 
Digitman ©   (2005-04-21 16:31) [2]


> Использую неблокирующий режим


> GameClient.Socket.SendBuf(


ГДЕ анализ результата, возвращаемого этой ф-цией ?

Нет его !

Все твои проблемы растут вокруг этого.


 
Gek1   (2005-04-21 16:52) [3]


> Digitman

SendBuf возвращает число. Что это число значит? Кол-во отправленных байт?

Скажи пожалуйста - как лучше сделать в моей ситуации?


 
Digitman ©   (2005-04-21 16:56) [4]


> SendBuf возвращает число. Что это число значит?


справку-то почитать - не судьба ?
ну не позорься уж ..

вроде при голове ты мужик ...


 
Gek1   (2005-04-21 17:15) [5]


> справку-то почитать - не судьба ?


Не повезло мне с Делфи. Имею Д2005 триал. А справки там нету :-(
Была бы - второй топик бы не создавал.
Д2005 выкинуть не могу. Слишком много кода. Без сворачивания кода - умру. :-(

Перерыл здесь статьи  - тоже молчат как партизаны.


 
Digitman ©   (2005-04-21 17:22) [6]

и нафих ты себе геморрой с Д2005 заработал - ума не приложу)


 
Gek1   (2005-04-21 17:28) [7]


> и нафих ты себе геморрой с Д2005 заработал - ума не приложу)

Я перешел на 2005 из-за удобства сворачивания кода.
Может кто-то привык к большим обьемам - а мне, к сожалению,  тяжело 15 тысяч строк кода листать вручную. Разбивка по модулям не помогает. :-(


 
Gek1   (2005-04-21 17:32) [8]

Яндекс подсказал, что надо проверять SendBuf() = SOCKET_ERROR

Что в этом случае делать? заново повторять?

А может ли отправится неполный кусочек буфера? :-)


 
Gek1   (2005-04-21 18:10) [9]


Syntax

[Delphi] public function SendBuf(var Buf: , Count: Integer): Integer;

Description
Use SendBuf to write to the socket connection. Call this method from the OnSocketEvent event handler of a Windows socket object or in the OnWrite or OnClientWrite event handler of a socket component. Alternately, Use SendBuf to write when a connection is first formed when the socket does not expect notification that the socket on the other end of the connection is expecting to read.

For non-blocking sockets, the data is sent to the WinSock DLL which has it"s own internal buffers. If the WinSock can accept additional data, SendBuf returns immediately with the number of bytes queued. If the WinSock internal buffer space is not able to accept the buffer being sent, SendBuf returns -1 and no data is queued at all. In this case, wait a bit for the WinSock to have a chance to send out already-queued data; then try again.

For blocking sockets, SendBuf returns the number of bytes actually written.
If an error occurs while writing to the connection, SendBuf terminates the connection and raises an ESocketError exception.


If the WinSock internal buffer space is not able to accept the buffer being sent, SendBuf returns -1 and no data is queued at all.
Как я понял если буфер переполненый, то вернет - 1. Надо подождать некоторое время и попытаться еще раз отправить.
Какое время ждать? пару мс хватит?

For blocking sockets, SendBuf returns the number of bytes actually written. Хотя это несказано про неблокирующий режим, но он возвращает кол-во отправленных байт тоже.


 
Ozone ©   (2005-04-22 06:18) [10]

> А может ли отправится неполный кусочек буфера? :-)

Может :)

Для того, чтобы передать все, использую следующую ф-ию:


int sendall(int s, char *buf, int len, int flags)
{
   int total = 0;
   int n;

   while(total < len)
   {
       n = send(s, buf+total, len-total, flags);
       if(n == -1) { break; }
       total += n;
   }

   return (n==-1 ? -1 : total);
}


Я думаю проблем с переводом и адаптацией к своему проекту не будет.


 
Digitman ©   (2005-04-22 08:15) [11]


> Какое время ждать? пару мс хватит?


не надо ничего ждать.

как только буфер освободится, гнездо сообщит тебе об этом событием OnWrite.


 
Gek1   (2005-04-22 09:49) [12]

Хм ...


> > А может ли отправится неполный кусочек буфера? :-)
>
> Может :)

Это касается и блокирующего и неблокирующего режима?
В хелпе несказано что это касается неблокирующего режима. Там сказано только что если буфер не может принять на отправку - от будет результат - 1.


> как только буфер освободится, гнездо сообщит тебе об этом
> событием OnWrite.

Т.е мне необходимо сождать очередь пакетов и вслучае проблем с отправкой отправлять их в событии OnWrite? или как?

И всетаки пролейте мне пожалуйста свет на следующие вопросы:
1. Может ли сокет отправить кусок моего буфера в случае неблокирующего режима.
2. Какой вариант [10] или [11]?


 
Digitman ©   (2005-04-22 10:06) [13]


> Т.е мне необходимо сождать очередь пакетов и вслучае проблем
> с отправкой отправлять их в событии OnWrite? или как?


да, примерно так.

и даже не очередь пакетов, а стрим - поток данных (неважно каких), требуемых к отправке

если send-метод вернул -1, ставь содержимое буфера (который был параметром метода) в хвост стрима .. в обработчике OnWrite выбирай из головы стрима очередную порцию (не более, скажем, 4кб), вызывай send-метод , и если он завершился успешно тут корректируй стрим, иначе жди очередного OnWrite или OnError


> 1. Может ли сокет отправить кусок моего буфера в случае
> неблокирующего режима


может.
но тебе об этом не нужно заботиться - send-метод, вернувший значение >= 0, укажет тебе сколько данных из затребованных тобой к передаче было реально поставлено в очередь на передачу (именно поставлено в очередь, а не физически передано и соотв-но принято другой стороной)


 
Gek1   (2005-04-22 10:30) [14]


> обработчике OnWrite выбирай из головы стрима очередную порцию
> (не более, скажем, 4кб),


Т.е в случае если у меня больше 4к, я должен отправить 4к и дальше ждать очередного OnWrite?

или же слать по 4к, пока SendBuf не вернет -1?

И что в плане если поставиться кусок на очередь?
(Т.е попытался поставить в очеред 4к а он принял 1к всего например). Опять же пытатся слать оставшийся кусок или ждать OnWrite?

И вообще:
OnWrite возникает после освобождения буфера отправки?
Т.е если я никогда ничего не отправлял - OnWrite никогда не возникнет?


 
Digitman ©   (2005-04-22 10:49) [15]


> или же слать по 4к, пока SendBuf не вернет -1?


именно так.

именно при -1 следует ожидать OnWrite, при ином результате никаких OnWrite не будет.

кр.того однократное OnWrite обязательно возникает сразу за событием OnConnect, говоря о готовности внутреннего буфера гнезда к записи в него данных send-методом


 
Gek1   (2005-04-22 12:45) [16]


> Digitman

Глянь пожалуйста. Верно  ... не верно ...


{$Region "GameSendSHex"}
procedure TCharacter.GameSendSHex(SHex : String; Encrypt : boolean);
{Отправляем Серверу строку в Hex ввиде}
var I, Len: Integer;
   BufIn, BufOut : PArray;
begin
if not GameClient.Active then Exit;
if Length(SHex) <= 1 then Exit;
Len := Length(SHex);
{Конвертируем SHex в Array of byte}
if (Len = 0) or (Len mod 2 <> 0) then
begin
AddToDebugLog(GetCharName+": "+"Error converting SHex to Array of byte"+ " (Game Socket)");
Disconnect;
Exit;
end;
New(BufIn);
New(BufOut);
for I := 0 to ((Len div 2)-1) do BufIn^[I] := StrToInt("$"+Copy(SHex, I*2+1, 2));
Len := Len div 2;
if Encrypt then
begin
case EncryptMetod of {Выбираем криптацию в зависимости от версии клиента}
0:  begin {0 - no encryption}
   for i := Low(BufOut^) to High(BufOut^) do BufOut^[i]:=BufIn^[i];
   end;

2:  begin {2 - <=2.0.0 (BlowFish)}
   BlowfishEncrypt(@BlowfishObj,BufIn,BufOut,Len);
   end;

3:  begin {3 - 2.0.3 (BlowFish+TwoFish)}
   BlowfishEncrypt(@BlowfishObj,BufIn,BufOut,Len);
   for i := Low(BufOut^) to High(BufOut^) do BufIn^[i]:=BufOut^[i];
   TwofishEncrypt(@TwofishObj,BufIn,BufOut,Len);
   end;

4:  begin {4 - >2.0.3 (TwoFish)}
   TwofishEncrypt(@TwofishObj,BufIn,BufOut,Len);
   end;
end; {End Case}
end  {End If}
else for i := Low(BufOut^) to High(BufOut^) do BufOut^[i]:=BufIn^[i];
{Добавляем BufOut в конец fGameBuff}
for i := 0 to Len - 1 do fGameBuff^[fGameBuffLen+i]:=BufOut^[i];
fGameBuffLen := fGameBuffLen + Len;
{Фиксируем отправляемый пакет в DebugLog}
AddtoDebugLog(GetCharName+": "+"Client send "+inttostr(Len)+" bytes: "+SHex);
{Отправляем пакет}
GameSendBuff;
{Здесь я отловлю пакеты, отсылка которых влияет на св-ства класса}
CheckOutgoingPackets(SHex);
Dispose(BufIn);
Dispose(BufOut);
end; {End TCharacter.GameSendSHex}
{$EndRegion}

{$Region "GameClientOnWrite"}
procedure TCharacter.GameClientOnWrite(Sender: TObject; Socket: TCustomWinSocket);
begin
{Отправляем то, что не получилось когда был переполнен буфер}
GameSendBuff;
end;
{$EndRegion}

{$Region "GameSendBuff"}
procedure TCharacter.GameSendBuff;
var I, Len: Integer;
   SendingLen : Integer;
begin
{Пытаемся отослать fGameBuff серверу}
if GameClient.Socket.Connected then
begin
try
SendingLen := 0;
while (SendingLen <> -1) and (fGameBuffLen > 0) do
   begin
   {Корректируем отправляемую длинну}
   if fGameBuffLen > 4000 then Len := 4000
   else Len := fGameBuffLen;
   {Пытаемся поставить в очередь}
   SendingLen := GameClient.Socket.SendBuf(fGameBuff^,Len);
   {Фиксируем в DebugLog случай неполной отправки пакета}
   if (SendingLen > 0) and (SendingLen < fGameBuffLen) then AddToDebugLog(GetCharName+": "+"Client Send not full packet");
   if SendingLen <= 0 then AddToDebugLog(GetCharName+": "+"SendingLen = "+IntToStr(SendingLen));
   {Если отправили чтото, то откусываем эту длинну от fGameBuff}
   if SendingLen > 0 then for i := 0 to (fGameBuffLen - SendingLen) - 1 do
       begin
       fGameBuff^[i] := fGameBuff^[i+SendingLen];
       end;
   fGameBuffLen := fGameBuffLen - SendingLen;
   end; {End While}
except
AddToDebugLog(GetCharName+": "+"Exception in Socket.SendBuf");
end;
end
else AddToDebugLog(GetCharName+": "+"You try send to shard some bytes, but Character is not Connected");
end;
{$EndRegion}


 
Digitman ©   (2005-04-22 14:15) [17]

ох что-то намудрил тут)

var
ms: TMemoryStream;

..

ms := TMemoryStream.Create;
try
Len := High(BufOut^) - Low(BufOut^);
ms.WriteBuffer(BufOut[Low(BufOut^)], Len);
cs.socket.Sendstream(ms);
except
ms.Free;
raise;
end;

и все !
даже OnWrite явно обрабатывать не нужно - cs все сам сделает и уничтожит стрим, когда либо передаст его целиком либо возникнет транспортное исключение


 
Gek1   (2005-04-22 15:14) [18]

спасибо :-)


 
Gek1   (2005-04-26 12:37) [19]

К сожалению проблема осталась.
Ситуация таже.
Добавил сигнальные записи в debug.log. Выяснил, что обработчик OnWrite возник только при установлении соединения и больше ниразу не возник.
Ф-ция SendBuf всегда отправляла полный пакет. Ни разу в результате не вернула ни 0 ни -1.

Вырезка из моего дебага:

10:48:12: Admin: Client trying send 8 bytes: 1200082431203000
10:48:12: Admin: Client Send 8 bytes
10:48:12: Admin: Client Receive = 38
10:48:12: Admin: Server[61]bytes:1C003D0101010101010003E8000353797374656D0000000
0000000000000000000000000000000000000000053656C6563742061207461726765742E00
10:48:12: Admin: Client Receive = 7
10:48:12: Admin: Server[19]bytes:6C000000000700000000000000000000000000
10:48:12: Admin: Client trying send 19 bytes: 6C000000000700001A0EDB058307F3000003DB
10:48:12: Admin: Client Send 19 bytes
10:48:12: Admin: Client Receive = 52
10:48:12: Admin: Server[76]bytes:1C004C0101010101010003E8000353797374656D0000000
000000000000000000000000000000000000000005468617420706572736F6E206C6F6F6B7320707
2657474792073696C6C792E00

10:48:22: Admin: Client trying send 8 bytes: 1200082431203000
10:48:22: Admin: Client Send 8 bytes
10:48:22: Admin: Client Receive = 38
10:48:22: Admin: Server[61]bytes:1C003D0101010101010003E8000353797374656D0000000
0000000000000000000000000000000000000000053656C6563742061207461726765742E00
10:48:22: Admin: Client Receive = 7
10:48:22: Admin: Server[19]bytes:6C000000000700000000000000000000000000
10:48:22: Admin: Client trying send 19 bytes: 6C000000000700001A0EDB058307F3000003DB
10:48:22: Admin: Client Send 19 bytes

10:48:32: Admin: Client trying send 8 bytes: 1200082431203000
10:48:32: Admin: Client Send 8 bytes
10:48:32: Admin: Client Receive = 59
10:48:32: Admin: Server[84]bytes:1C0054001A0EDB03DB0003B2000341646D696E000000000
000000000000000000000000000000000000000004920616D20616C726561647920706572666F726
D696E6720616E6F7468657220616374696F6E2E00

10:48:47: Admin: Client trying send 8 bytes: 1200082431203000
10:48:47: Admin: Client Send 8 bytes
10:48:47: Admin: Client Receive = 59
10:48:47: Admin: Server[84]bytes:1C0054001A0EDB03DB0003B2000341646D696E000000000
000000000000000000000000000000000000000004920616D20616C726561647920706572666F726
D696E6720616E6F7468657220616374696F6E2E00


Здесь 4 цикла пакетов ... 1 цикл успешный. Потом во втором идет сбой, причина которого мне так и не ясна. и далее 2 последних цикла уже не могу проавильно работать, потому как произошел збой.

Теперь что значит каждая строка:
(рассмотрим первый цикл)
10:48:12: Admin: Client trying send 8 bytes: 1200082431203000
Запись фиксируется, перед попыткой отправить пакет.

10:48:12: Admin: Client Send 8 bytes
в Случае если SendBuf возвращает число большее нуля - появится эта запись в debug-е с фиксированием кол-ва отправленных байт.

10:48:12: Admin: Client Receive = 38
Строка фиксирует что получили упакованные данные.

Распаковываем данные и фиксируем какие пакеты получили:
10:48:12: Admin: Server[61]bytes:1C003D0101010101010003E8000353797374656D0000000
0000000000000000000000000000000000000000053656C6563742061207461726765742E00

Аналогично:
10:48:12: Admin: Client Receive = 7
10:48:12: Admin: Server[19]bytes:6C000000000700000000000000000000000000

10:48:12: Admin: Client trying send 19 bytes: 6C000000000700001A0EDB058307F3000003DB
10:48:12: Admin: Client Send 19 bytes
10:48:12: Admin: Client Receive = 52
10:48:12: Admin: Server[76]bytes:1C004C0101010101010003E8000353797374656D0000000
000000000000000000000000000000000000000005468617420706572736F6E206C6F6F6B7320707
2657474792073696C6C792E00

Суть цикла:
Отправить пакет 0х12 ....
Дождаться пакета 0х6С и отправить пакет 0х6С с ответом.

Ну и так по кругу.

Если внимательно посмотреть 2 цикл, то там видно, что я отправил пакет 0х6С, но сервер на него не ответил (0х1С).
Потери связи не видно, так как далее идут пакеты и сервер на них отвечает.

Также в дебаге я фиксирую все события OnWrite и проблеммы с отправкой пакета.
Как видно, судя по логу, проблем небыло.
OnWrite возник один раз в самом начале.
SendBuf ни разу не вернул отрицательного результата.

Кто может сказать в чем может быть проблема?


 
Digitman ©   (2005-04-26 12:45) [20]


> К сожалению проблема осталась


после переделки на SendStream() ?


 
Gek1   (2005-04-26 12:49) [21]


> Digitman


Да ... пробовал и в моем (измененном) и в твоем варианте.
Результат один и тот же.


 
Digitman ©   (2005-04-26 12:51) [22]

приводи код передатчика (использующего именно SendStream) и соотв. код приемника


 
Gek1   (2005-04-26 20:16) [23]


> Gek1   (26.04.05 12:49) [21]

Беру свои слова обратно.
Результат еще хуже.

Отправлял 2 пакета подряд. В итоге неработает. Не могу понять чего именно. Возможно в работе с MemoryStream. Признаюсь - с MemoryStream мало знаком. :-(

Код (вставил твой в GameSendSHex):

procedure TCharacter.GameSendSHex(SHex : String);
var I, Len: Integer;
   Buff : PArray;
   ms: TMemoryStream;
begin
if not GameClient.Active then Exit;
if Length(SHex) <= 1 then Exit;
Len := Length(SHex);
{Конвертируем SHex в Array of byte}
if (Len = 0) or (Len mod 2 <> 0) then
  begin
  AddToDebugLog(GetName+": "+"Error converting SHex to Array of byte"+ " (Game Socket)");
  Disconnect;
  Exit;
  end;

New(Buff);

for I := 0 to ((Len div 2)-1) do Buff^[I] := StrToInt("$"+Copy(SHex, I*2+1, 2));
Len := Len div 2;

{Пытаемся отослать байтики серверу}
if GameClient.Socket.Connected then
 begin
 ms := TMemoryStream.Create;
   try
   ms.writeBuffer(Buff^[0], Len);
   GameClient.Socket.Sendstream(ms);
   {Фиксируем отправляемый пакет в DebugLog}
   AddtoDebugLog(GetCharName+": "+"Client send "+inttostr(Len)+" bytes: "+SHex);
   except
   AddToDebugLog(GetCharName+": "+"Exception in Socket.SendBuf");
ms.Free;
   raise;
   end;
 end
else AddToDebugLog(GetName+": "+"You try send to shard some bytes, but you not Connected");

Dispose(Buff);

end; {End TCharacter.GameSendSHex}


Уже голову поломал ... не подскажешь в чем проблема и как правильно сделать?


 
kami ©   (2005-04-26 22:09) [24]

Sorry, что вмешиваюсь в продуктивную беседу.
2 [23] Gek1   (26.04.05 20:16)
  ms.writeBuffer(Buff^[0], Len);
  GameClient.Socket.Sendstream(ms);

А вообще отправляется что-нибудь?
По-моему, после write buffer указатель потока будет в конце, а для посылки его в гнездо нужно установить в начало.
Поэтому попробуй так:

ms.writeBuffer(Buff^[0], Len);
ms.Seek(0,soFromBeginning);
GameClient.Socket.Sendstream(ms);


И зачем конвертить строку в массив? почему бы ее не отослать так?


 
Digitman ©   (2005-04-27 08:16) [25]

угу.

перед sendstream(ms) нужно установить св-во ms.position = 0


 
2ksion ©   (2005-04-30 02:03) [26]

а может стои на winapi сокетах это делать?


 
Gek1   (2005-04-30 09:24) [27]


> 2ksion ©   (30.04.05 02:03) [26]
> а может стои на winapi сокетах это делать?


Покачто остановился на TClientSocket. Проблем вроде бы небыло (кроме этой).

Могу ли я вообще положится на TClientSocket?
Кто с ним хорошо работал и может просветить про его недостатки?

Задача такова, что мне необходимо, чтобы приложение работало месяцами без збоев. Могу ли я доверять эту задачу TClientSocket?


 
Digitman ©   (2005-04-30 14:11) [28]


> Могу ли я доверять эту задачу TClientSocket?


можешь.

простейший, надежнейший и прозрачнейший транспорт, организованный Борландом в коде этого компонента, не оставляет шансов иным компонентам того же назначения



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

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

Наверх





Память: 0.55 MB
Время: 0.031 c
14-1121414851
syte_ser78
2005-07-15 12:07
2005.08.07
Задачка для 6 класса.


14-1121366741
BorisMor
2005-07-14 22:45
2005.08.07
Еще одна провокационная ветка про 2 мировую.


14-1121458098
DiamondShark
2005-07-16 00:08
2005.08.07
Братья-славяне, помогите!


6-1114085582
Gek1
2005-04-21 16:13
2005.08.07
Проблема с TClientSocket


14-1121326706
Виталий123
2005-07-14 11:38
2005.08.07
Написание драйверов





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