TClientSocket不断尝试与TServerSocket连接,运行一段时间后的问题! (200分)

  • 主题发起人 叮叮当当
  • 开始时间

叮叮当当

Unregistered / Unconfirmed
GUEST, unregistred user!
运行环境:WindowsXP + Delphi 6.0

要求:使用一个TClientSocket尝试与尚未运行的TServerSocket服务端应用程序连接,力求在TServerSocket运行后尽快与之建立连接并通信,因此使用一个TTimer定时为1秒不断尝试连接。

注意:TServerSocket平时是关闭的,很少开启,而TClientSocket要在其开启后尽最大努力尽快与之连接。

另外:TClientSocket和TServerSocket均采用ctNonBlocking(非阻塞方式)。

在TClientSocket的OnError事件中屏蔽连接失败错误:
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode := 0;
end;

定时器中代码如下:
procedure TFrom1.Timer1Timer(Sender: TObject);
begin
if not ClientSocket1.Active then ClientSocket1.Active := True;
end;

结果:TServerSocket服务端应用程序启动时,TClientSocket能正常与TServerSocket连接,但如果TClientSocket运行一段时间后仍未启动TServerSocket服务器(大约10分钟),TClientSocket应用程序就会出错!

错误为:Project BNTester.exe raised exception class ESocketError with message 'Windows socket error: 当该操作在进行中,由于保持活动的操作检测到一个故障,该连接中断。(10055), on API 'connect". Process stopped. Use Step or Run to continue.

继续运行还会引发另一个错误:Project BNTester.exe raised exception class ESocketError with message 'Windows socket error: 提供的文件句柄无效。(10022), on API 'WSACancelASyncRequest"". Process stopped. Use Step or Run to continue.

我初步分析是由于Socket句柄资源用尽导致的,有什么方法可以解决?
 
我用winsock进行多线程扫描的时候也碰到过这个问题,由于每次open的时候都
调用socket建立新的套结字,导致资源耗尽。
我想如果你用api来解决就释放对winsock.dll的引用,下次建立socket之前,在
增加winsock.dll的引用记数。试试看!
 
API我用不来啊,再说TClientSocket、TServerSocket控件归根到底不也是对Winsock API的调用嘛,你会用API的话,说说它们哪里有错了?
 
我想TClientSocket和TServerSocket都是利用WSAAsyncSelect()来分派Wsocket I/O消息
如果应用程序调用了WSAAsyncSelect()这个函数, 那么除非调用CloseSocket()关闭套
解字,否则消息通知永远有效,如果你没有关闭连接的套解字那么很有可能是这个原因
导致出错,还有winsock.dll每个引用的引用程序的资源是有限的,如果调用winsock.dll
的应用程序建立的Tsocket数量太多,资源就可能耗光。好象这个数目是可以从WSADATA的
返回值里得到详细的数目。
WSADATA中的iMaxSockets就是最大可打开socket 的数目。
 
我也碰到类似的问题也没有找到答案
 
To: 张无忌

可我自始至终只用了一个TClientSocket呀,它应该也只用一个Socket吧?
 
错了,你如果看了他的员代码,每次open前他都建立了一个套解字!!!!
 
这就是我找到的代码
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);
try
Event(Self, seLookUp);
if Block then
begin
FAddr := InitSocket(Name, Address, Service, Port, True);
DoOpen;
end else
AsyncInitSocket(Name, Address, Service, Port, 0, True);
except
Disconnect(FSocket);
raise;
end;
end;
 
我确实在ScktComp.pas中找到了你说的这段代码,但是我在第一行“if FConnected then raise ESocketError.CreateRes(@sSocketAlreadyOpen);”设置了断点,并运行我的程序,发现根本不会运行到这段代码嘛。
 
最简单的解决办法是将ClientSocket改用Blocking方式试连,
直至连上后再断开,改回NonBlocking方式
 
To: 猛禽

阻塞方式连接和非阻塞方式连接的唯一区别是不是阻塞方式不会触发OnRead事件,要自己在代码里判断是否有接收数据到达?

选择阻塞方式或非阻塞方式时,服务器端和客户端是否必须一致?
 
非阻塞方式下全部传输操作都是异步的,不仅仅是事件那么简单,在非阻塞方式下:
Client Server
Connect---------Accept
OnConnect OnClientConnect
OnRead/OnWrite--OnClientWrite/OnClientRead
(注意,要在相应该事件中用SendBuf/ReceiveBuf等读写数据)

在阻塞方式下:
Client----------Server
Connect---------Accept
TWinSocketStream.Read/Write
(只能通过TWinSocketStream进行,不能用SendBuf/ReceiveBuf等)

两端最好一致,否则数据同步会有问题,所以:
直至连上后再断开,改回NonBlocking方式
 
To: 猛禽

>直至连上后再断开,改回NonBlocking方式
这么做有点牵强,如果一连接上,Server就要主动向Client发送数据呢?

你的意思是说如果两端都用Blocking方式就没有问题了?
 
如果你用Blocking那么消息将阻塞,如果连不上去,你的程序就像死了一样。
而且你连上去以后把它变成非阻塞能不能用都是问题,要涉及到ioctlsocket
等一些winsock函数的调用问题,我昨天仔细分析了一下,ClientSocket中的
哪个socket就是TCustomWinSocket,
 
最好的建议是你用一个线程来连接服务器,用winsock API来写,如果不会写,我可以提供
帮助!
 
最好不要用TClientsock的非阻塞式,他的实现是用readfile和writefile配合重叠I/O
效率不高,相当耗资源,就其原因是他用的是winsock1.1版本,在winsock2版本中,
可以用WSARecv()中的lpOverlapped这个参数来进行重叠I/O操作,效率更高!
 
我的qq :775033联系
 
我在Win2K下运行了20多分钟都不出问题,但是SocketHandle的值却一直在增加,可能
是Win2K支持的最大句柄数远大于Win98或者其它原因,但这样的话最终还是要出问题的。
建议在ClientSocket的OnError事件中加入这行代码试试看:
closesocket(Socket.SocketHandle);
 
每次连接TClientSocket都会建一个新的SOCKET,
 
To: 张无忌

那你可知TClientSocket何时释放它创建的Socket?我采用动态创建TClientSocket的方法,每次重试连接服务器都Free后再创建也没用!
 
顶部