Главная страница
    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

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



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

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

Наверх




Память: 0.55 MB
Время: 0.037 c
3-41026
kaif
2003-10-15 15:47
2003.11.13
Толку от UDF RAND() ?


3-41115
SPA81
2003-10-22 17:14
2003.11.13
DBComboBox


1-41591
Alfred
2003-10-27 10:59
2003.11.13
Работа с прерываниями


6-41855
Yot
2003-09-10 13:11
2003.11.13
Client_Server


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