Midas中的BUG, 无法捕捉SocketConnection连接失败的异常,造成程序死锁!快来帮帮我吧! (100分)

  • 主题发起人 LiChaoHui
  • 开始时间
不然的话,它的异常是异步的,也就是Open后,才能取到的,因为SupportCallback后,
它是由线程去连接,然后socket出错后,是发消息过来说出错了。。。去了SuppportCallback
就没问题了。
 
to copy_paste
问题没有那么简单, 我反复跟踪Delphi提供的源代码,
发现这种现象有时候出现, 有时候不出现, 好像和你说的东西没有关系呀
跟踪了上百遍, 也找到了点眉目, 是线程同步的问题造成的
 
to stuwe,
我在进行调试的时候, 有时候正常, 有时候不正常, 如果不调试,
则每次都不正常,可能和计算机配置性能有关系,
但可以肯定的是,Midas的源码的确有问题,
 
SuppportCallback = true(default)
连接,取数据,都是在一个线程中进行的。(主线程WaitFor)
否则的话就是主线程直接进行连接。
 
下面是我在跟踪源码时所取的的进步:
1.
我好像已经发现真正的原因了
每次当线程执行退出前的一个Synchronize方法时,在执行的过程中
正好切换到主线程,此时执行了一个
WaitForSingleObject(FTransport.Handle, 180000);
则每次必死锁,
我想,可能是Synchronize内部的同步控制和后者发生了冲突
解决的办法时,只要让后者在前者执行之前执行,就不会有问题了
2.
真是奇怪了,有时候连续出现,有时候调试的时候半天都等不到
3.
好像问题解决了,我的修改如下
procedure TStreamedConnection.InternalClose;
begin
if Assigned(FTransport) then
begin
FTransport.OnTerminate := nil;
FTransport.Terminate;
PostThreadMessage(FTransport.ThreadID, WM_USER, 0, 0);
Classes.CheckSynchronize;
WaitForSingleObject(FTransport.Handle, 180000);
FTransport := nil;
end else
if Assigned(FTransIntf) then
begin
FTransIntf.Connected := False;
FTransIntf := nil;
下面的这句是我加入的
Classes.CheckSynchronize;
意思是,在主线程中每次调用WaitSingleOject时
需要先调用这个过程来检查线程的同步过程调用,否则,可能发现死锁
 
没有的,socket连接是没有连接的timeout,所以很不确定它的线程的时间。
>>PostThreadMessage(FTransport.ThreadID, WM_USER, 0, 0);
是说它给线程发个通知消息,让它退出线程,但那线程正处于socket的连接阻塞养成,
根据响应不了那个WM_USER消息,所以肯定是要等到socket失败后,waitFor才返回的。。。
所以,最好的方法就是不要线程,即:SupportCallback = False
 
to:LiChaoHui
我是两年前写的
因为 网络不稳 所以 一旦网络断了(被人踢掉) 要马上提示 所以有以下代码
//代价是SuppportCallback=true
procedure TFrm_main.ApplicationEvents1Exception(Sender: TObject;
E: Exception);
var
str, strcaption: string;
begin
if Sender = dm.SocketConnection then
begin
application.MessageBox('数据连接被以外断开或数据操作失败。。。。。。。。。。。。。。
end

然后就遇到了你的问题
所以 用了 TPowersock测试 ip和端口

两个月前 有因为
ScktSrvr 有bug //在用了超线程技术的计算机上会有问题
//改用WebConnection速度又慢
unit SConnect;
//你最好再网上找 全版的
procedure TTransportThread.Execute;
procedure SynchronizeException;
var
SendException: TObject;
begin
SendException := AcquireExceptionObject;
if Assigned(FTransport) and (SendException is ESocketConnectionError) then
FTransport.Connected := False;
PostMessage(FParentHandle, THREAD_EXCEPTION, 0, Integer(Pointer(SendException)));
end;

var
msg: TMsg;
Data: IDataBlock;
Event: THandle;
Context: Integer;
begin
CoInitialize(nil);
try
PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
ReleaseSemaphore(FSemaphore, 1, nil);
try
FTransport.Connected := True;
try
Event := FTransport.GetWaitEvent;
while not Terminated and FTransport.Connecteddo
try
case MsgWaitForMultipleObjects(1, Event, False, INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0:
begin
WSAResetEvent(Event);
Data := FTransport.Receive(False, 0);
if Assigned(Data) then
begin
Data._AddRef;
PostMessage(FParentHandle, THREAD_RECEIVEDSTREAM, 0, Integer(Pointer(Data)));
Data := nil;
end;
end;
WAIT_OBJECT_0 + 1:
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE)do
begin
if (msg.hwnd = 0) then
case msg.message of
THREAD_SENDSTREAM:
begin
Data := IDataBlock(msg.lParam);
Data._Release;
Context := FTransport.Send(Data);
if msg.wParam = 1 then
begin
Data := FTransport.Receive(True, Context);
Data._AddRef;
PostMessage(FParentHandle, THREAD_RECEIVEDSTREAM, 0, Integer(Pointer(Data)));
Data := nil;
end else
PostMessage(FParentHandle, THREAD_SENDNOTIFY, 0, 0);
end;
THREAD_REPLACETRANSPORT:
begin
FTransport := ITransport(msg.lParam);
FTransport._Release;
end;
else
DispatchMessage(msg);
end
else
DispatchMessage(msg);
end;
end;
end;
except
SynchronizeException;
end;
finally
Data := nil;
FTransport.Connected := False;
end;
except
SynchronizeException;
end;
finally
FTransport := nil;
CoUninitialize();
end;
end;

 
造成死锁的机理如下:
在线程中,调用Synchronize方法, 会让主线程来执行某些特定的过程,
此时,线程等待主线程执行结束,然后继续运行,
但是,如果此时,主线程调用WaitForSingleObject(Thread.Handle, 180000);
等待线程结束, 那么将发生死锁,
因为,此时,双方都在等待对方结束, 除非有一方作出让步,否则,就是僵局,
所以,捕捉异常的时候, 有时候连接失败后180秒才能等到错误提示框出现
解决的办法是,在调用WaitForSingleObject之前,先调用
Classes.CheckSynchronize;
让线程的同步调用返回,则可以减少出现这种情况,但是好像不能完全避免
因为,你不能保证,
Classes.CheckSynchronize;

WaitForSingleObject
会在CPU的一个时间片内运行
 
2 hfghfghfg
procedure TTransportThread.Execute;
这段好像没有改什么代码啊?俺看错没有?
ApplicationOnException是可以取得异常,但好像不应该这样取,俺只是觉得。
 
多谢, copy_paste,
你使用过Callback吗, 我想用, 但是怎么用啊, 帮我一下吧, 单独给你100分吧
 
我想在各个客户端之间发送消息,但是,又不想单独打开Socket,
所以,想利用CallBack功能, 怎么做啊?
关于,本题目, 我得修改如下, 基本可以捕捉到异常了,并且也可以支持Callback
此代码位于SConnect.pas内, 有一行是我加的, 呵呵, 画龙点睛之笔:)
procedure TStreamedConnection.InternalClose;
begin
if Assigned(FTransport) then
begin
FTransport.OnTerminate := nil;
FTransport.Terminate;
PostThreadMessage(FTransport.ThreadID, WM_USER, 0, 0);
if GetCurrentThreadID = MainThreadID then
Classes.CheckSynchronize;
//这一行是我加的
WaitForSingleObject(FTransport.Handle, 180000);
FTransport := nil;
end else
if Assigned(FTransIntf) then
begin
FTransIntf.Connected := False;
FTransIntf := nil;
 
2 LiChaoHui
为什么你总觉得,WaitFor和Classes.CheckSynchronize有关系了?
Classes.CheckSynchronize一般是主线程在空闲的时候调用,当然线程调用Synchronize
的时候,会Post一个空闲的消息给App,让它会运行CheckSynchronize,而CheckSynchronize
的用处就是将其它非主线程的处理,集中到主线程来处理,所谓同步吧。
而WaitForSingleObject(Thread.Handle, 180000)跟checkSynchronize根本八杆子打不着。
WaitFor(Thread.Handle...)只会在线程退出的时候返回,虽然在线程有析构函数中,也
有CheckSynchroize处理,但也只是可能而已。因为有很多情况是在主线程调用Thread.Free
进行Free的,而不是线程自动Free,所以会调用。
WaitFor在这个应用中根本和CheckSynchronize没有关系。
 
那个SocketConnection.SupportCallback感觉好像不太对其意。
它是socketConnection运行时,决定是用线程来连接SERVER还是直接主线程连接。
而且,我以前跟人讨论的时候,还发现一个问题。这个这个问题现在想不起来。
它是用法就是将其设成False,其它的,都是按常规操作。
我不太清楚你将它理解成什么。
 
http://xianjun.vicp.net/temp/18265_borland_socket_server_fixed_d5_to_d7.zip
 
>>我想在各个客户端之间发送消息,但是,又不想单独打开Socket,
>>所以,想利用CallBack功能, 怎么做啊?
做应该是能做的,但这个callBack不是我们平常接触的CallBack概念。
我上面都说,它的SupportCallBack有点词不达意。
 
这是我无数次跟踪调试的结果, 而且做了我上面的修改后,
完全正常了, 说明我的看法是正确的,
我只所以会有这样的看法, 那是因为
每次的死锁都是发生在线程调调用Synchronize 的时候,
其中有一句WaitForSingleObject,引起线程切换, 此时切换到主线程,
主线程里面要执行的也有一个 WaitForSingleObject
两个一起执行, 不死锁都怪了
还有啊,copy_paste,老兄, 回调支持的特性怎么使用, 还请你教教我啊!!!
 
还是D5的线程保护方法好用
 
http://xianjun.vicp.net/temp/18265_borland_socket_server_fixed_d5_to_d7.zip
刚上传的
老外改的
我还没仔细看过
 
多谢几位兄弟, 非常感激, 每人100分作为答谢
Delphi的帮助中,关于SupportCallback是这么解释的,
Determines whether the streamed connection component can handle
callbacks in the application server interface.
property SupportCallbacks: Boolean;
Description
Use SupportCallbacks to indicate whether the connection component
can marshal callback functions in the application server's interface.
When SupportCallbacks is True, the connection component can marshal calls
from the application server to the client application over an interface
supplied as a callback.
For TSocketConnection, callbacks are optional. Set SupportCallbacks to True
if you are using callbacks as part of the application server抯 interface.
When SupportCallbacks is True, the socket connection component requires
WinSock2. Set SupportCallbacks to False if you are not using
callback functions and want to eliminate the dependency on WinSock2.
TWebConnection can抰 support callback functions. It initializes
SupportCallbacks to False in the constructor and never changes this value.
 
我得E文太差, 但是也能看出来
指的是,服务器端调用客户端, 这正是我所要的呀
 

Similar threads

S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
968
SUNSTONE的Delphi笔记
S
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
回复
0
查看
1K
天地弦
顶部