求助:Indy 9.0.18中TIdTCPServer停止服务时出错!

梦想家大魔王 2005-03-16 01:15:56
Demo里的Chat程序,当下来后不做任何改动直接在IDE里运行,当有客户端连接在服务端的时候,如果服务端按停止服务按钮,就会弹出一系列错误:
{ IdIOHandlerSocket.pas }
function TIdIOHandlerSocket.Recv(var ABuf; ALen: integer): integer;
begin
if Connected then
begin
Result := Binding.Recv(ABuf, ALen, 0);
end
else begin { 第一个停在这句,错误:Disconnected }
raise EIdClosedSocket.Create(RSStatusDisconnected);
end;
end;

{ IdTCPServer.pas }
procedure TIdTCPServer.TerminateAllThreads;
const
LSleepTime: Integer = 250;
var
i: Integer;
LThreads: TList;
LTimedOut: Boolean;
begin
if Assigned(Threads) then begin
LThreads := Threads.LockList; try
for i := 0 to LThreads.Count - 1 do begin
with TIdPeerThread(LThreads[i]) do begin
Connection.DisconnectSocket;
end;
end;
finally Threads.UnlockList; end;

if not TIdThreadSafeList(Threads).IsCountLessThan(1) then begin
LTimedOut := True;
for i := 1 to (TerminateWaitTime div LSleepTime) do begin
Sleep(LSleepTime);
if TIdThreadSafeList(Threads).IsCountLessThan(1) then begin
LTimedOut := False;
Break;
end;
end;
if LTimedOut then begin { 第二个停在这句,错误:TerminateTimeOut }
raise EIdTerminateThreadTimeout.Create(RSTerminateThreadTimeout);
end;
end;
end;
End;

前面有帖子说这是Indy的一个BUG,我按照帖子说得改了源代码重新编译了包再安装上,还是错误依旧。请各位朋友们帮忙看看问题到底出在哪,谢谢!
...全文
478 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
niutuoshaozhe 2005-04-12
  • 打赏
  • 举报
回复
如果不能解决,有什么办法能避开吗,不让用户看到系统错误,比如“thread timed out”之类的
hackingtruth 2005-04-12
  • 打赏
  • 举报
回复
学习
cjf1009 2005-03-18
  • 打赏
  • 举报
回复
服务器端需要关闭的时候,我采用的是给列表里的连接线程都发信息,告诉他们退出,和上边类似,之后再关闭程序。

如果是客户端异常死机,我看了些资料,好象没什么好的解决方案,一般可在服务器端定时检查有没有没有响应的连接。
梦想家大魔王 2005-03-17
  • 打赏
  • 举报
回复
在客户端正常断开后停止服务器不会出现异常,但问题是服务器因为某些原因需要重启或者关闭的时候,这是如果还有客户端没有断开,停止就会抛出异常。
握手之后再停确实可以避开异常,但我不知道是Indy本来就设计成这样还是因为BUG而不得不这样。
niutuoshaozhe 2005-03-17
  • 打赏
  • 举报
回复
但是,在客户端异常中断的情况下,怎么办?比如死机,掉电等。
线程表当中可能仍保存着客户端线程,关闭server的时候仍然会有错误提示
cjf1009 2005-03-17
  • 打赏
  • 举报
回复
IndyServer为每个客户端建立一个线程,然后在线程里read数据。
我们一般这样写服务器端的接收程序:
if not AThread.Terminated and AThread.Connection.Connected then //注意这里
begin
AThread.Connection.ReadBuffer(Command,SizeOf(TCommand));
if Command='1' then
……

现在以服务器端为例,研究退出。
因为indy是阻塞的,所以程序会等停在read那里;接到数据后,程序继续运行,处理完数据后,线程自动又会运行到read那里等待数据。如果客户端突然disconnect,这里的read自然会报错;所以正确的退出方式应该是:在接收数据以后的处理代码里,服务器端断开连接:

注意我标注的地方:
if not AThread.Terminated and AThread.Connection.Connected then //注意这里
程序再运行到这里的时候,AThread.Connection.Connected 已经为false,则线程自动退出了,连接断开了。
示例:

if not AThread.Terminated and AThread.Connection.Connected then //注意这里
begin
AThread.Connection.ReadBuffer(Command,SizeOf(TCommand));
if Command='Server Quit' then
begin
Command := 'Client Quit';
AThread.Connection.WriteBuffer(Command,Sizeof(Command));
AThread.Connection.Disconnect;
end;

在客户端建立线程来接收数据的,基本也是这样写的。所以也需要得到一个退出的统治(程序中的'Client Quit'),然后主动退出。这就是我所说的握手退出。

自己研究的,大家讨论。
getit911 2005-03-16
  • 打赏
  • 举报
回复
第1个Disconnected错误脱离IDE环境就没有了,第2个错误是OnExecute代码有错误或用到了非线程安全的代码
cjf1009 2005-03-16
  • 打赏
  • 举报
回复
关注
xinqingbucuo 2005-03-16
  • 打赏
  • 举报
回复
procedure TServerFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseConnect;
Sleep(200);
try
Server.Active := False;
except
end;
Clients.Free;
end;

procedure TServerFrmMain.CloseConnect;
var
DestClient : PClient;
RecThread: TIdPeerThread;
i : Integer;
begin
try
with Clients.LockList do
for i:=0 to Count-1 do
begin
DestClient:=Items[i];
RecThread:=DestClient.Thread;
RecThread.Connection.Disconnect;
end;
finally
Clients.UnlockList;
end;
end;

procedure TServerFrmMain.ServerDisconnect(AThread: TIdPeerThread);
var
ActClient: PClient;
begin

ActClient := PClient(AThread.Data);
if ActClient=nil then Exit;
//RecvMemo.Lines.Add ('客户端 "'+ActClient^.IP+'" 断开连接 时间:'+DateTimeToStr(Now));
try
Clients.LockList.Remove(ActClient);
finally
Clients.UnlockList;
end;
FreeMem(ActClient); //释放断开连接的客户端的信息结构内存
AThread.Data := nil;
RefreshList;
//DisPlayDisconnect
end;
你试试行不行
记住不要再disconnection中写界面代码,好像要用到同步执行什么东西
梦想家大魔王 2005-03-16
  • 打赏
  • 举报
回复
郁闷中。
刚看了IndyProject的邮件列表,里面也有好几个人提出了这个问题,但至今仍然没有解决的方法。
BTW,OnDisconnect中我只是把用户相关的信息从一个TThreadList中删除,其中没有涉及主窗口界面的代码。
梦想家大魔王 2005-03-16
  • 打赏
  • 举报
回复
的确脱离IDE运行不会弹出错误,但点关闭按钮时可以明显感觉到窗口冻住了,因为它在结束客户连接工作线程的时候超时了。
的确关闭服务器之前先断开所有客户连接可以绕开这个错误(好像还必须是客户端先断开才行),可是谁听说过有哪个服务器要关闭之前还得问问客户端是不是都断开了?
天哪!我要关服务器还得看客户端的脸色?他要是死不下线那我关闭就得出错?
kernelspirit 2005-03-16
  • 打赏
  • 举报
回复
这是Indy固定的异常处理机制,脱离IDE就不会有,对使用也不会有什么影响。
梦想家大魔王 2005-03-16
  • 打赏
  • 举报
回复
不知道Indy10里面有没有这样的问题,不过10的改动太大了,升级的话有很多代码需要改。
vinsonshen 2005-03-16
  • 打赏
  • 举报
回复
up

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧