TClientSocket的连接问题,说什么windows socket error (api Connect)

jianlinlong 2003-07-15 05:24:42
现在很少用TClientSocket了(用Indy),最近修补旧的程序,还是得用TClientSockt。老而未解的问题又来了:

在Form1上放一个TClientSocket

ClientSocket->Acitve = false;
ClientSocket->Port = 56789;
ClientSocket->Host = "123-没有的机器名";//如果设置ip,或LAN上存在的机器名就没问题;如果这个机器名不存在的话,就只能一次。第二次出就...
ClientSocket->Active = true;

第一次执行上面的代码是没有问题的(不会触发exception),第二次(or later)再执行的时候就出现exception了:Windows socket error(on api 'Connect').

纳闷ing.
...全文
164 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
kissfire 2003-08-17
  • 打赏
  • 举报
回复
IC
kingcaiyao 2003-07-20
  • 打赏
  • 举报
回复
用阻塞模式(即设置ClientType=stThreadBlocking),这样就可以同步捕捉错误了。
yesry 2003-07-18
  • 打赏
  • 举报
回复
我觉得第一次没有完成之前不要做第二次。
codearts 2003-07-18
  • 打赏
  • 举报
回复
好像这个问题延申到对异常的捕捉的态度了。^_^

基本上我同意你上面所说的方法,但在TApplication.OnException里面写代码我个人感觉太过霸道。除非有些异常(或者说错误)已经超出了我的控制之外,比如了Access vilation at ...这种错误,或者用多线程在某种情况下出现了异常等等,这种情况我才不得不用TApplication.OnException了。用TApplication.OnException比较好的例子就是《金山词霸》(好像是它,记得不是很清楚),它有一个运行错误日记(这样就免去了出现异常对话框的麻烦),我想它应该是在TApplication.OnException事件中把那个异常的消息写进一个文本文件里面...

另外:
ClientSockt1->Active = false;
ClientSocket1->Host = "这是个本LAN没有的机器名";
ClientSocket1->Port = 12345;//目标主机都没有,端口也就无所谓了
//ClientSockt1->ClientType = ctBlocking;******注意这里没有设为ctBlocking******
try {
ClientSocket1->Active = true;
//......
}
catch(Exception& e) {
ShowMessage(e.Message); //****第一次(奇数次)是在ClientSocket1的OnError事件中,第二次(偶数次)就会捕捉到那个 "windows socket error on api connect"的异常,所以我说这是Borland的BUG了
}


jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
要注意:

(1)ClientSocket->Host必须设为一个本局域网没有的机器名(*****特别要注意******)

(2)第一次点击Button1,不会出现异常,而是进入了TClientSocket的OnError事件
第二次点击Button1, 出现了异常,不触发TClientSocket的OnError事件
第三次点击Button1,不会出现异常,而是进入了TClientSocket的OnError事件
第四次点击Button1, 出现了异常,不触发TClientSocket的OnError事件
...(奇数次触发OnError事件,偶数次得到一个异常)
jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
to JSP:

看到你就像看到了阳光,^_^


那个OnError是笔误, 是在OnError事件中写入代码:
ErrorCode = 0;
ShowMessage("出错了,连接不上");
jishiping 2003-07-17
  • 打赏
  • 举报
回复
我试了一下,没有发现你说的问题啊。你说的在TClientSocket,在其OnError事件中写入
OnError = 0; //---这儿的OnError是什么?
ShowMessage("出错了,连接不上");
jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
跟踪了源码好久,没个结果! 不熟悉socket api呀,痛苦。

jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
to gyj_china(透明) :
我不是做多层系统呀,多层用TSocketConnection才会出现你说的那种情况吧.
jishiping 2003-07-17
  • 打赏
  • 举报
回复
没有出现错误怎么捕捉???只不过有些情况是BCB主动调用实现给定好的事件,有些是程序
主动去捕捉。比如数据库的错误,都是使用try{} catch{}来捕捉错误。而TClientSocket的
属性为ClientType为ctNonBlocking时,为事件驱动,没有办法使用try catch捕捉,只能依
靠BCB本身提供的事件来完成。而这个转换HostName为IP Address出现的错误,BCB没有提供
事件,只能依靠我上面的方法(除非你重新继承TClientSocket)。但是如果ClientType为
ctBlocking时,那么任何错误,都是通过try catch完成的。比如:
ClientSockt1->Active = false;
ClientSocket1->Host = "这是个本LAN没有的机器名";
ClientSocket1->Port = 12345;//目标主机都没有,端口也就无所谓了
ClientSockt1->ClientType = ctBlocking;
try {
ClientSocket1->Active = true;
//......
}
catch(Exception& e) {
ShowMessage(e.Message);
}
codearts 2003-07-17
  • 打赏
  • 举报
回复
to JSP:

你上面给出的方案是治标没有治本,彼有亡羊补牢的味道(等错误出现了,再去捕捉)。尽管我也经常这样做,但只有没有办法的时候我才如此。或许你这一招是杀手铜,不到万不得已还是少有为妙!

-------------------------------------------------------------------------
今天追到TClientSocket的一个"BUG", 高兴ing
jishiping 2003-07-17
  • 打赏
  • 举报
回复
上面已经给出了解决方案了,楼主好像没有看到哦。
codearts 2003-07-17
  • 打赏
  • 举报
回复
又测试几次,我上面那样改以后就没有错了。不过尝试与LAN中不存在的机器连接的确要花相对较长的时间,Borland的工程师们是不是考虑到这个原因才不加上那一句呢?

不过,依我这算是Borland的Bug。在表单上放两个Button
(1)Button1的OnClick
ClientSocket1->Active = false;
ClientSocket->Host = "lan中不存在的机器名";
ClientSocket1->Port = 12345;//端口无所谓
ClientSocket1->Active = true;

(2)Button2的OnClick事件
ClientSocket1->Active = false;
ClientSocket->Host = "Server1";//Server1正在运行
ClientSocket1->Port = 53333;//这个端口正在被Server1监听
ClientSocket1->Active = true;

先点Button1,再点Button2,结果Button2出现了异常!
codearts 2003-07-17
  • 打赏
  • 举报
回复
这个是我的马甲,^_^
codearts 2003-07-17
  • 打赏
  • 举报
回复
很奇怪,为什么Borland不在CMLookupComplete这样写呢?

procedure TCustomWinSocket.CMLookupComplete(var Message: TCMLookupComplete);
var
ErrorCode: Integer;
begin
if Message.LookupHandle = FLookupHandle then
begin
FLookupHandle := 0;
if Message.AsyncError <> 0 then
begin
ErrorCode := Message.AsyncError;
Error(Self, eeLookup, ErrorCode);
Disconnect(FSocket);
FLookupState := lsIdle;//********加这一句就没有问题了吧!会有负作用吗?***JSP,烦请你看看这样会不会有负作用
if ErrorCode <> 0 then
raise ESocketError.CreateResFmt(@sWindowsSocketError,
[SysErrorMessage(Message.AsyncError), Message.ASyncError, 'ASync Lookup']);
Exit;
end;
.............(略去部分代码)..
jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
然后第二次执行(也就是偶数次执行),由于在第一次执行时FLookupState 被置为了lsLookupAddress值(我很纳闷,为什么第一次执行完后它不被重置为lsIdle?),所以这一次执行结果就不一样了。执行TCustomWinSocket.AsyncInitSocket(...)会跳转到这部分代码:

lsLookupAddress:
begin
if Service <> '' then //如果设置了Service属性,结果第二次执行也不出现异常。可到第三次又出现异常了
begin
if FGetHostData = nil then
FGetHostData := AllocMem(MAXGETHOSTSTRUCT);
FLookupHandle := WSAASyncGetServByName(Handle, CM_LOOKUPCOMPLETE,
PChar(Service), 'tcp' , FGetHostData, MAXGETHOSTSTRUCT);
CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetServByName');
FLookupState := lsLookupService;
Exit;
end else //我没设置Sercie属性,执行下面的
begin
FLookupState := lsLookupService;
FAddr.sin_port := htons(Port);
end;
end;
.....结果它又递归调用自已:
if FLookupState <> lsIdle then
ASyncInitSocket(Name, Address, Service, Port, QueueSize, Client);
.....再继续执行:
lsLookupService: //******这才是主要打开连接的代码,为什么第一次不执行呢?***********
//原因就是第一次执行的时候TCustomWinSocket.CMLookupComplete收到的消息里面Message.AsyncError<>0
//而如果ClientSocket1->Host的值为LAN中一个主机名的话,Message.AsyncError就等于0
//这样结果就完全不一样了
begin
FLookupState := lsIdle;
if Client then
DoOpen// 它会调用 CheckSocketResult(WinSock.connect(FSocket, FAddr, SizeOf(FAddr)), 'connect');
//然后就触发异常了
else DoListen(QueueSize);
end;
jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
FLookupState 第一次执行的进候是lsIdle, 然后被赋值为lsLookupAddress就退出了。之后可能是Socket发来消息,触发到TCustomWinSocket.CMLookupComplete:::
//.....CMLookupComplete所执行到的部分
if Message.LookupHandle = FLookupHandle then
begin
FLookupHandle := 0;
if Message.AsyncError <> 0 then
begin
ErrorCode := Message.AsyncError;
Error(Self, eeLookup, ErrorCode);
Disconnect(FSocket);
if ErrorCode <> 0 then
raise ESocketError.CreateResFmt(@sWindowsSocketError,
[SysErrorMessage(Message.AsyncError), Message.ASyncError, 'ASync Lookup']);
Exit;
end;
//.............
jianlinlong 2003-07-17
  • 打赏
  • 举报
回复
我跟踪源码的结果是:

procedure TCustomWinSocket.AsyncInitSocket(const Name, Address,
Service: string; Port: Word; QueueSize: Integer; Client: Boolean);
var
ErrorCode: Integer;
begin
try
case FLookupState of
lsIdle:
begin
if not Client then
begin
FLookupState := lsLookupAddress;
FAddr.sin_addr.S_addr := INADDR_ANY;
end else if Name <> '' then
begin
if FGetHostData = nil then
FGetHostData := AllocMem(MAXGETHOSTSTRUCT);
FLookupHandle := WSAAsyncGetHostByName(Handle, CM_LOOKUPCOMPLETE,
PChar(Name), FGetHostData, MAXGETHOSTSTRUCT);
CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetHostByName');
FService := Service;
FPort := Port;
FQueueSize := QueueSize;
FClient := Client;
FLookupState := lsLookupAddress;
Exit;
end else if Address <> '' then
begin
FLookupState := lsLookupAddress;
FAddr.sin_addr.S_addr := inet_addr(PChar(Address));
end else
begin
ErrorCode := 1110;
Error(Self, eeLookup, ErrorCode);
Disconnect(FSocket);
if ErrorCode <> 0 then
raise ESocketError.CreateRes(@sNoAddress);
Exit;
end;
end;
lsLookupAddress:
begin
if Service <> '' then
begin
if FGetHostData = nil then
FGetHostData := AllocMem(MAXGETHOSTSTRUCT);
FLookupHandle := WSAASyncGetServByName(Handle, CM_LOOKUPCOMPLETE,
PChar(Service), 'tcp' , FGetHostData, MAXGETHOSTSTRUCT);
CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetServByName');
FLookupState := lsLookupService;
Exit;
end else
begin
FLookupState := lsLookupService;
FAddr.sin_port := htons(Port);
end;
end;
lsLookupService:
begin
FLookupState := lsIdle;
if Client then
DoOpen
else DoListen(QueueSize);
end;
end;
if FLookupState <> lsIdle then
ASyncInitSocket(Name, Address, Service, Port, QueueSize, Client);
except
Disconnect(FSocket);
raise;
end;
end;
jishiping 2003-07-17
  • 打赏
  • 举报
回复
相关的源程序在scktcomp.pas的TCustomWinSocket.CMLookupComplete里,VCL调用
raise ESocketError.CreateResFmt(......)
然后转到 forms.pas 的 TApplication.HandleException 函数中。
jishiping 2003-07-17
  • 打赏
  • 举报
回复
你拦不到的那个错误,实际上是程序将主机名转换为IP地址时错误,这个错误不会调用
ClientSocket的任何事件。想要拦截这个错误的话,在Form上放一个TApplicationEvents
控件(在Additional工具栏里面),然后在TApplicationEvents控件的OnException事件
里写代码:
void __fastcall TForm1::ApplicationEvents1Exception(TObject *Sender,
Exception *E)
{
if (dynamic_cast<ESocketError*>(E) &&
E->Message.Pos("Lookup")>0)
ShowMessage("连接不上,找不到主机");
else
Application->ShowException(E);
}
加载更多回复(7)

1,316

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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