Форум: "Сети";
Текущий архив: 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
Можно по-подробнее...
Страницы: 1 2 вся ветка
Форум: "Сети";
Текущий архив: 2003.11.13;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.033 c