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

Вниз

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

 
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

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



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

Текущий архив: 2003.11.13;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.024 c
1-41343
qwe
2003-10-30 17:42
2003.11.13
Pointer


1-41286
k_len
2003-10-31 13:30
2003.11.13
Кодировка


1-41278
Nikolay M.
2003-10-31 11:32
2003.11.13
Ручной отлов Exception-ов - ?


14-42017
R
2003-10-17 02:43
2003.11.13
StringGrid с цветом


1-41205
sergious
2003-11-01 23:48
2003.11.13
ProgressBar & ListView