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

Вниз

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

 
SidDN   (2003-09-10 14:10) [0]

Мастера ХЕЛП!!!
Пишу клиент-сервер на блок. сокетах. Проблема в том, что на одних машинах есть глюк, а на других нет. Глюк заключается в том, что первый раз клиент обменивается данными с сервером нормально, а потом перестает, т.е. не устанавливается соединение с сервером. Хотя на некоторых машинах (и локально на одной) всё впорядке.
Целый день долбаюсь. Оси везде WinXP. Может у кого есть идеи, могу привести код.


 
Digitman   (2003-09-10 14:12) [1]


> могу привести код


приводи


 
SidDN   (2003-09-10 14:18) [2]

Это сервак

TCommServerThreadNew=class(TServerClientThread)
public
procedure ClientExecute; override;
end;

{......}

procedure TCommServerThreadNew.ClientExecute;
var
Stream: TWinSocketStream;
s1,Buffer, strIn: string;
DataSize,nRead: Integer;
begin
Stream := TWinSocketStream.Create(ClientSocket, 5000);
while not Terminated and ClientSocket.Connected do
begin
Buffer:="";
strIn := "";
SetLength(Buffer, 64);
nRead := Stream.Read(Buffer[1], 64);
if nRead = 0 then
begin
ClientSocket.Close;
Exit;
end;
// Получаем длину пакета
SetLength (Buffer, nRead);
SetLength(s1, StrLen(PChar(Buffer))+1);
StrLCopy(@s1[1], PChar(Buffer), Length(s1)-1);
DataSize:=StrToInt(s1);
Delete(Buffer, 1, Length(s1));
StrIn := StrIn + Buffer;

while Length(StrIn)<DataSize do // Получаем весь пакет
begin
SetLength(Buffer, nRead);
nRead := Stream.Read(Buffer[1], nRead);

if nRead = 0 then
begin
ClientSocket.Close;
Break;
end;
SetLength (Buffer, nRead);
StrIn := StrIn + Buffer;
end;

if StrIn = "" then Exit else
begin
// Чё-то делаем, и отправляем ответ (в данном случае эту-же строку)
StrIn:=IntToStr(Length(StrIn))+#0+StrIn;
Stream.Write(StrIn[1],Length(StrIn));
end;
end;
Stream.Free;
end;

{....}

// Так сервак стартуем
ServerBlocked:=TServerSocket.Create(nil);
ServerBlocked.Port:=17785;
ServerBlocked.ServerType:=stThreadBlocking;
ServerBlocked.OnGetThread:=Self.OnGetThreadNew;
ServerBlocked.Active:=True;

{...}
procedure TForm1.OnGetThreadNew(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
begin
SocketThread := TCommServerThreadNew.Create(False, ClientSocket)
end;



Это клиент


TDataShow= procedure(StrIn:string)of object;

// Поток клиента
TComIOThread=class(TThread)
private
ClientSocket:TClientSocket;
Buffer:TMemoryStream;
InDataSize:integer;
Address:string;
Port:word;
Data:string;
protected
procedure Execute; override;
public
DataShow:TDataShow;
constructor Create(CreateSuspended: Boolean; FDataShow: TDataShow;FAddress:string;FPort:word;FData:string);
procedure Show;
end;

constructor TComIOThread.Create;
begin
DataShow:=FDataShow;
Data:=FData;
Address:=FAddress;
Port:=FPort;
FreeOnTerminate:=True;
inherited Create(CreateSuspended);
end;

procedure TComIOThread.Execute;
var
Stream:TWinSocketStream;
nRead,nRead1:integer;
TMP,s1:string;
begin
ClientSocket:=TClientSocket.Create(nil);
Stream:=nil;

try
ClientSocket.Address:=Address;
ClientSocket.ClientType:=ctBlocking;
ClientSocket.Port:=Port;
ClientSocket.Active:=True;
Stream:=TWinSocketStream.Create(ClientSocket.Socket, 30000);

ClientSocket.Socket.SendText(Data); // Посылаем запрос
if Stream.WaitForData(30000) then // Ждём ответ
begin
SetLength(TMP, 65535);
nRead1 := Stream.Read(TMP[1], 65535);
if nRead1 = 0 then
begin
ClientSocket.Close;
end;
SetLength (TMP, nRead1);
// Определяем размер данных
SetLength(s1, StrLen(PChar(TMP))+1);
StrLCopy(@s1[1], PChar(TMP), Length(s1)-1);
InDataSize:=StrToInt(s1);
Delete(TMP, 1, Length(s1));
Buffer:=TMemoryStream.Create;
Buffer.Write(TMP[1],Length(TMP));
while Buffer.Size<InDataSize do // Получаем остальные
begin
SetLength(TMP, nRead1);
nRead := Stream.Read(TMP[1], nRead1);
if nRead = 0 then
begin
ClientSocket.Close;
Break;
end;
Buffer.Write(TMP[1],nRead);
end;
SetLength(Data,Buffer.Size);
Buffer.Position:=0;
Buffer.Read(Data[1],Buffer.Size);
Buffer.Free;

Synchronize(Show);

end else
begin
// TimeOut
end;
finally
ClientSocket.Active:=False;
ClientSocket.Free;
Stream.Free;
end;
end;

procedure TComIOThread.Show;
begin
if Assigned(DataShow)then DataShow(Data);
end;

// Запуск клиента

st:="EXPENSE[A1,A2,R1,R2] COUNT[TEST1] PERIOD[08.09.03]";
TComIOThread.Create(False,DataShow,"192.168.0.77",17785, IntToStr(Length(st))+#0+st);



 
SidDN   (2003-09-10 14:26) [3]

Было замечено, что при глюках не уничтожается клиентский поток.
И при повторных запросах к серверу при трассировке

ClientSocket.Address:=Address;
ClientSocket.ClientType:=ctBlocking;
ClientSocket.Port:=Port;
ClientSocket.Active:=True; <- Вот после этого места происходит выход из процедуры

А на других машинах, повторяюсь, всё проходит нормально.


 
Verg   (2003-09-10 14:49) [4]

Не хватает Resume, ну ладно, где-то он все равно есть.


> Было замечено, что при глюках не уничтожается клиентский
> поток.


Каким образом это было замечено?


 
SidDN   (2003-09-10 14:54) [5]

Resume не надо, в Create false передаётся

Замечено было Task Manager-ом. С каждой попыткой запроса к серверу увеличивается число потоков приложения, хотя на других компах поток создавался и сразу по приходу ответа грохался.


 
Digitman   (2003-09-10 15:01) [6]


> ClientSocket.Active:=True; <- Вот после этого места происходит
> выход из процедуры


из какой процедуры ?


 
Verg   (2003-09-10 15:02) [7]


> Resume не надо, в Create false передаётся


> constructor TComIOThread.Create;
> begin
> DataShow:=FDataShow;
> Data:=FData;
> Address:=FAddress;
> Port:=FPort;
> FreeOnTerminate:=True;
> inherited Create( CreateSuspended); - это?
> end;


увеличивается число потоков приложения

На сколько? На 2 или на 1, в смысле на каждый запрос?


 
SidDN   (2003-09-10 15:02) [8]

Из procedure TComIOThread.Execute


 
SidDN   (2003-09-10 15:07) [9]

To Verg

При первом запросе увеличивается сразу на 5!!! потом 3 убиваются, и с каждым новым запросом число потоков увеличивается на 1.
Мистика!


 
Verg   (2003-09-10 15:10) [10]

Давай начнем с этого участка:


> ClientSocket.Active:=True;
> Stream:=TWinSocketStream.Create(ClientSocket.Socket,
> 30000);

Ты понимаешь, что если Active:=true вызовет исключение, то поток кинется выполнять
> finally
> ClientSocket.Active:=False;
> ClientSocket.Free;
> Stream.Free;> end;


Заметь: Stream - переменная в стеке (не поле объекта) и nil быть равна не обязана


 
Verg   (2003-09-10 15:13) [11]

А все, пардон, вижу есть там Stream:=nil


 
SidDN   (2003-09-10 15:20) [12]

При трассировке компилятор после этой строки
ClientSocket.Active:=True;
прекращает выполнение процедуры, в блок Finally не попадает


 
Digitman   (2003-09-10 15:24) [13]


> ClientSocket.Address:=Address;
> ClientSocket.ClientType:=ctBlocking;
> ClientSocket.Port:=Port;
> ClientSocket.Active:=True; <- Вот после этого места
> происходит выход из процедуры


ничего подобного.

если в момент выполнения этого кода ClientSocket был Active, то
на любой из этих строчек (на первой же !)

ClientSocket.Address:=Address;
ClientSocket.ClientType:=ctBlocking;
ClientSocket.Port:=Port;

ты схлопочешь исключение, и до строчки

ClientSocket.Active:=True

даже дело не дойдет


 
Verg   (2003-09-10 15:25) [14]


> При трассировке компилятор после этой строки
> ClientSocket.Active:=True;
> прекращает выполнение процедуры, в блок Finally не попадает


Это понятно, так и должно быть (именно при трассировке), если хочешь расскажу как и почему, но к данному вопросу это не относится.
Просто для проверки поставь точку останова (Ctrl+F8) на первом операторе в finally...


 
SidDN   (2003-09-10 15:29) [15]

to Digitman © (10.09.03 15:24)

Но при втором запросе это уже другой поток и другой ClientSocket

to Verg © (10.09.03 15:25)

Ставил, не заходит.
Если можно, расскажи, плз.


 
Verg   (2003-09-10 15:37) [16]


> Ставил, не заходит.


Не может быть.


> Если можно, расскажи, плз.


Когда ты делашь шаг (без захода, т.е. F8) отладчик делает так:
Заменяет байт следующий за операцией вызова процедуры на прерывание отладчика (int 3) и пускает поток на выполнение, ожидая того самого прерывания. Дождавшись, восстанавливает тот самый байт. В данном случае внутри процедуры возникает исключение и поток никогда не попадает на инструкцию следующую за операцией вызова, а начинает выполнять цепочки finally, except. Ну вот ты и видишь этот эффект.


 
SidDN   (2003-09-10 15:44) [17]

Спасибо за объяснение.
Но в finally он действительно не заходит.
Кстати я закомментировал в блоке finally уничтожение сокета, и всё стало работать, в чём дело?


 
Verg   (2003-09-10 15:44) [18]

А по сути, мне почему-то кажется, что рыть надо в сторону эту:

> procedure TComIOThread.Show;
> begin
> if Assigned(DataShow)then DataShow(Data);
> end


 
Digitman   (2003-09-10 15:46) [19]


> при втором запросе это уже другой поток


не факт.
тот же поток м.б. взят объектом TServerWinSocket из кэша, с передачей ему параметром нового ClientSocket


 
Verg   (2003-09-10 15:46) [20]


> Но в finally он действительно не заходит.


Не верю. Заходит.
Иначе никакое "зокментаривание" бы и близко не повлияло, не так ли?


 
Verg   (2003-09-10 15:48) [21]


> Digitman © (10.09.03 15:46) [19]


Подождите! Мы про какую часть говорим? Про клиентскую или про серверную?


 
SidDN   (2003-09-10 15:51) [22]

to Verg © (10.09.03 15:46)

В первый раз заходит, не спорю, я имею в виду последующие запросы. DataShow, помоему ни причём, без нее тоже не работает.
И почему если закомментировать ClientSocket.Free начинает работать. А ведь на некоторых машинах всё работает и так нормально.

В чём может быть дело?


 
Digitman   (2003-09-10 15:54) [23]


> Verg


я - пока про серверную


 
Verg   (2003-09-10 15:57) [24]

первый раз заходит, не спорю, я имею в виду последующие запросы

И в последующие заходит, а если не заходить значит гдето просто умирает (засыпает) внутри SetActive.


> И почему если закомментировать ClientSocket.Free начинает
> работать


Закоментаривая ClientSocket.Free ты по-сути убираешь WSACleanUp из приложения.
Если его оставить (Free, а значит WSACleanUp), то происходит деинициализация winsock. Между прочим, с этим тоже как-то может быть связано... (Порченный winsock на некоторых твоих машинах?)


 
Digitman   (2003-09-10 15:57) [25]


> И почему если закомментировать ClientSocket.Free начинает
> работать


а это, кстати, и не допустимо - самостоятельно разрушать объект TServerClientWinSocket

за его разрушение ответственен объект-диспетчер TServerWinSocket

в ClientExecute() же допустимо только метод TServerClientWinSocket.Close вызывать


 
SidDN   (2003-09-10 15:58) [26]

Да, кстати, иногда при последующем запросе вываливается окно CPU с точкой в функции DbgBreakPoint или DbgUserBreakPoint


 
SidDN   (2003-09-10 16:03) [27]

to Digitman © (10.09.03 15:57)
Проверял MemCheck-ом, если ClientSocket не прибить, он остаётся.
Да и ClientSocket это не TServerClientWinSocket.


 
Digitman   (2003-09-10 16:11) [28]


> если ClientSocket не прибить, он остаётся


и правильно остается. для повторного использования при коннекте со след.клиентом


> ClientSocket это не TServerClientWinSocket.


чушь. именно - TServerClientWinSocket


 
SidDN   (2003-09-10 16:14) [29]

Стоп. Я вобще про клиента говорю.

// Поток клиента
TComIOThread=class(TThread)
private
ClientSocket:TClientSocket; <-----------
Buffer:TMemoryStream;
InDataSize:integer;
Address:string;
Port:word;
Data:string;
protected
procedure Execute; override;
public
DataShow:TDataShow;
constructor Create(CreateSuspended: Boolean; FDataShow: TDataShow;FAddress:string;FPort:word;FData:string);
procedure Show;
end;


 
SidDN   (2003-09-10 16:23) [30]

Протрассировал. Поток засыпает при создании сокета

procedure TCustomWinSocket.Open(const Name, Address, Service: string; Port: Word; Block: Boolean);
begin
if FConnected then raise ESocketError.CreateRes(@sSocketAlreadyOpen);
FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP); <<---
if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);
...
...


 
Digitman   (2003-09-10 16:26) [31]


> Поток засыпает при создании сокета


как это "засыпает" ? блокируется что ли на исполнении какой-то строчки ?


 
SidDN   (2003-09-10 16:32) [32]

Прохожу строку
FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
и все, на следующую строчку не переходит.
Через время выпадает окно CPU с точкой в функции DbgBreakPoint или DbgUserBreakPoint


 
Digitman   (2003-09-10 16:42) [33]

отложим на время "высокие материи"
начнем с явной несуразицы

constructor TComIOThread.Create; // где параметры конструктора ?
begin
DataShow:=FDataShow; // ты же на них ссылаешься сдесь !
Data:=FData;
Address:=FAddress;
Port:=FPort;
FreeOnTerminate:=True; //почему идет ссылка на св-во еще неинициализированного объекта ?
inherited Create(CreateSuspended); // вот только здесь инициализация начинается !
end;


 
SidDN   (2003-09-10 16:55) [34]

Параметры я опустил.
constructor TComIOThread.Create(CreateSuspended: Boolean; FDataShow:TDataShow;FAddress:string;FPort:word;FData:string);

А в чём проблема с FreeOnTerminated? Помоему здесь его ставить и надо.


 
Digitman   (2003-09-10 17:03) [35]

в ряде случаев, конечно, это и прокатит, но правильным будет вот так :

constructor TSomeDescendantOfThread.Create(параметры);
begin
inherited Create(True);

//здесь инициализируем свои поля на основании параметров

if not CreateSuspended then
Resume;
end;


 
Digitman   (2003-09-10 17:04) [36]

в ряде случаев, конечно, это и прокатит, но правильным будет вот так :

constructor TSomeDescendantOfThread.Create(параметры);
begin
inherited Create(True);

//здесь инициализируем свои поля на основании параметров

if not CreateSuspended then
Resume;
end;


 
SidDN   (2003-09-10 17:08) [37]

Согласен.

Ну а где всетаки "собака порылась"?
Почему не работает?
А можно попросить Вас выслать какой нибудь пример с работой блок. сокета. Хочу проверить машины. Тачки все вроде одинаковые, XP-я везде стоит. Нифига не пойму!


 
Digitman   (2003-09-10 17:14) [38]

для начала сделай так

procedure TComIOThread.Execute;
var
Stream:TWinSocketStream;
nRead,nRead1:integer;
TMP,s1:string;
begin
try
ClientSocket:=TClientSocket.Create(nil);
.... здесь все остальное
except
on E: Exception do НекаяПроцедураЗаписиПротоколаОбИсключенияхВПоточнойФункции (E.ClassName + " " E.message);

end;
end;


 
Verg   (2003-09-10 17:22) [39]

Я на XP не работаю (нет нигде под рукой), но
продпологаю вот что (wsacleanup):


> Verg © (10.09.03 15:57) [24]


 
SidDN   (2003-09-10 17:29) [40]

> Verg

Можно по-подробнее...


 
Verg   (2003-09-10 17:35) [41]


> Можно по-подробнее...

Там более подробно некуда

Давай лучше поищем "ведьм" :)


> SetLength (TMP, nRead1);
> // Определяем размер данных
> SetLength(s1, StrLen(PChar(TMP))+1);

Объясни в этом куске - чего ты хош? Особенно StrLen - что вернет и почему? Как думаешь?


 
SidDN   (2003-09-10 17:43) [42]

В начале пакета идёт его длина в символьном виде, отделённая от пакета #0.
Напр.: "10"#0"1234567890"

В этом коде я выкусываю из пакета его длину:
SetLength(s1, StrLen(PChar(TMP))+1);
StrLCopy(@s1[1], PChar(TMP), Length(s1)-1);
InDataSize:=StrToInt(s1);
Delete(TMP, 1, Length(s1));

В приведённом примере StrLen(PChar(TMP)) вернёт 2.
Помоему все в порядке.


 
Verg   (2003-09-10 18:23) [43]

Между запросами серверу сколько проходит время (между первым с нормальным обменом и следующим "с умиранием")?
Они идут подряд?


 
SidDN   (2003-09-10 18:34) [44]

Да по разному.Пробовал и подряд, и через время.
Дело в том, что первый запрос нормальным назвать нельзя, т.к. данные то приходят, а поток не убивается, и почему при запросе создаются 5-ть потоков? На рабочей машине создаётся всего один, и оный после отработки грохается.


 
SidDN   (2003-09-10 18:40) [45]

А давай я тебе проекты вышлю и ты у себя попробуешь?


 
Verg   (2003-09-11 09:06) [46]

Ну, давай.

andruk@mail.ru

Прикольный глюк. Вот только нет у меня XP.
Кстати, клиентскую часть (поток) я вчера гонял. Ничего криминального. Ездил на Nt4 И W2K.


 
SidDN   (2003-09-11 09:50) [47]

Послал.
Я уже в своём отделе операционки переставлять на двух машинах начал, может это поможет :)


 
Verg   (2003-09-11 10:53) [48]

1. Проверил, работает. Хоть убейся работает. Даже на 98 (подвернулась тут....)
2. Модернизировал часть клиентского кода (извини, перевел на на Паскаль :)), а то "глаз режет") - тоже работает хоть убейся.
вот модернизированный кусок (из клиентской части)

ClientSocket.Socket.SendText(Data); // Посылаем запрос
SetLength(TMP, 65535);
if Stream.WaitForData(30000) then // Ждём ответ
begin
nRead1 := Stream.Read(TMP[1], length(Tmp));
if nRead1 = 0 then exit;
// Определяем размер данных
S1:=pchar(Tmp);
InDataSize:=StrToIntDef(S1,-1);
if InDataSize<=0 then exit;
Data:=Copy(TMP, length(s1)+2, nRead1-length(s1)+2+1);
while length(Data)<InDataSize do // Поучаем остальные
begin
if not Stream.WaitForData(30000) then exit;
nRead := Stream.Read(TMP[1], length(Tmp));
if nRead = 0 then exit;
Data:=Data+Copy(Tmp, 1, nRead);
end;
Synchronize(Show);
end else
begin
// TimeOut
end;


 
Verg   (2003-09-11 10:58) [49]

Слушай а на кой там два штука TIdTCPClient? Мусор или перспектива?


 
SidDN   (2003-09-11 11:01) [50]

Да это я Инди пробовал, убить забыл.

Наверное глюк в машинах всётаки, щас XP переставлю попробую опять.
Спасибо за помощь!!!

Кстати а ты Инди пробовал?


 
Verg   (2003-09-11 11:01) [51]


> nRead1-length(s1) -2+1


конечно же


 
Verg   (2003-09-11 11:05) [52]


> Кстати а ты Инди пробовал?


Пробовал давно, не понравилось. К тому времени, когда ОНО появилось у меня было уже достаточно наработок с Client/Server сокетами и я решил, что "свои баги ближе к телу" :))


 
SidDN   (2003-09-11 11:28) [53]

Слушай, а вышли мне exe-шники клиента и сервера (если не напряжно), может уменя компилер багует, только что пол предприятия оббежал, глючит стабильно!!! :(((


 
Verg   (2003-09-11 11:51) [54]

Лови.
Кстати, ты специально отключил оптимизацию и установил выравниване полей=1?


 
Verg   (2003-09-11 11:55) [55]

Да, компилировано D6 - UpdatePack2.


 
Verg   (2003-09-11 12:00) [56]

И это....,
по-моему ты рискуешь жестко нарваться с выравниванием. У меня, например исходники ScktComp (и не только) лежат в пути компиляции. И я могу с любым выравниванием работать.
Не уверен, но как гипотеза...


 
SidDN   (2003-09-11 12:04) [57]

С оптимизацией и выравниванием - это я пробовал, думал может влияет както.

Твои exe-шники тоже глючат :(((((

Кстати на одной машине перебил операционку, начало работать.

Теперь остаётся выяснить в чём был бок.


 
Verg   (2003-09-11 12:08) [58]

"А ведь говорил Мослатый Слизняку: держись, дурак, от канав подальше, а то ведь и хоронить нечего будет... " (С) Стругацкие

Это я про XP.


 
SidDN   (2003-09-11 12:15) [59]

:))))
Спасибо большое за помощь!
Ну а насчёт XP - мне деваться некуда, надо писать под неё.
Буду разбираться... :(



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

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

Наверх




Память: 0.6 MB
Время: 0.049 c
3-40924
VID
2003-10-16 01:01
2003.11.13
Непонятное поведение клиента при потере связи с сервером


6-41840
Multy
2003-09-12 03:59
2003.11.13
Два вопроса по TWebBrowser


14-42045
Malkolinge
2003-10-15 15:53
2003.11.13
Хлопци е така гра - Starcraft.


3-41048
safarov
2003-10-24 06:27
2003.11.13
Импортирование


6-41800
Artem
2003-09-17 11:52
2003.11.13
Передача сообщения по сети





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