关于TCLIENTSOCKET的奇怪问题。大家都来讨论下。(100分)

  • 主题发起人 主题发起人 ebow
  • 开始时间 开始时间
E

ebow

Unregistered / Unconfirmed
GUEST, unregistred user!
在写一个多线程通信软件,为了加快开发速度,我使用了动态生成TClientSocket的方法。
然而却发现一个问题。程序流程如下:
.....
var
clients: array[1..20] of tclientsocket;
begin

{这是一个循环线程。负责建立clientsocket对象}
function socketthread( param: pointer): integer;
begin
...
clients := tclientsocket.create(nil);
with clients do begin
Host := HostName;
Port := HostPort;
OnLookUp := clientLookup; //事件处理过程。
OnConnecting:= clientConnecting;
OnConnect := clientconnect;
OnRead := clientRead;
OnError := clientError;
Open;
end;
procedure tForm1.OnButton1Clicked(Sender: tobject);
var
threadid:cardnial;
begin
beginthread(nil,0,@Socketthread,nil,0,threadid);
...
end;
... 省略其他操作。
end;
...省略
end.
当在线程创建clientsocket对象并打开后,只有lookup和connecting事件能产生,但并不
产生连接,connect等其他事件不能产生,但是,如果我不是在线程里生成对象,一切都
正常。
有没有人碰到这个问题?


 
在线程中生成可视对象是有问题的,包括页面;可用同步解决此问题
Synchronize(“创建对象的过程”)
 
TclientSocket是可视对象吗?
它不过是个封装了SOCKET操作的类。更何况在我程序里不是竞争资源。
大家可以动手测测。
UP。
 
有没有人知道这个问题?白出了这么多分。再UP一下。
 
不要去创建clientConnecting 事件直接创建 clientConnect事件
在 clientConnect事件中发送数据
我的代码
unit ThreadSocket;

interface

uses
Classes,ScktComp,sysutils;

type
TCsocket = class(TThread)
private
cskccr:Tclientsocket;
{ Private declarations }
protected
procedure Execute; override;
end;
var
CmdStr:string;
DataStr:string;
AddrStr:string;
procedure Receive_comm(ReccmdStr:string); //接收命令字符串过程
procedure Receive_data(RecDataStr:string);//接收数据字符串过程
procedure Recive_addr(RecAddr:string);//接收IP地址字符串过程
implementation

{ Important: Methods and properties of objects in VCL can only be used in a
method called using Synchronize, for example,

Synchronize(UpdateCaption);

and UpdateCaption could look like,

procedure TCsocket.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }

{ TCsocket }
uses trans,unitmain;
procedure Receive_comm(ReccmdStr:string); //接收命令字符串
begin
CmdStr:=ReccmdStr;
end;
procedure Receive_data(RecDataStr:string);//接收数据字符串
begin
DataStr:=RecDataStr;
end;
procedure Recive_addr(RecAddr:string);//接收IP地址
begin
AddrStr:=trim(RecAddr);
end;
procedure TCsocket.Execute;
begin
{ Place thread code here }
freeonterminate:=true;
cskCCR:=Tclientsocket.Create(nil);
cskCCR.Address:=AddrStr;
cskCCR.Port:=8001;
cskCCR.ClientType:=ctBlocking;//用同步方式
try
cskCCR.Active:=true;
cskCCR.Socket.SendText(CmdStr);//命令包
cskCCR.Socket.SendText(DataStr);//数据抱
except
cskCCR.Active:=false;
frmtrans.mem.lines.Add('error');
exit;
end;
cskCCR.Active:=false;
frmtrans.mem.Lines.Add('ok');

end;

end.
 
To moses1999:
很好,你碰到了问题的关键,就是用BLOCKING堵塞方式,原来的问题就是因为创建
对象是默认的ctNonBlocking方式,这种方式在线程方式下有问题。至于connecting
和connect事件的区别,不过就是看是在Host里放地址,还是在Address里放地址,
Host必须经过LookUp查找才能得到address.
我原本希望使用Noblocking方式。但是看来tclientsocket还是不够完善。
大家可以试试,换成NonBlocking类型立马就不行了。
 
to:ebow
你看过Delphi自带的scktsrvr的源代码吗?也是在线程中创建ClientSocket。
 
不好意思,写错实例了
其实在线程中创建包括TQuery,TClientSocket等类型的控件时,虽然不是可视的,若不
用同步,则系统是不稳定的
 
to hclee:
你可以看到在scktsrvr中使用的是tserverclientthread,
这个线程也是TserverSocket采用的。它和Tclientsocket不同,
我的问题是TClientSocket,因为它在NonBlocking方式下是用的消息通知模式,
这种模式正是问题所在,因为我发现在线程中创建的TclientSocket无法响应消息。
具体为什么我也没有时间去研究了,现在我把它改成了select模式,问题已经解决了。
同步的问题在这里并不存在。
呵呵。发分了。谢谢关注。
 
to ebow:
你好,近来我也碰到了你所说的问题,很急。
不知你是怎么解决的?能否指教一下。万分感谢!
 
应该在你的线程中构造一个消息循环来进行等待onreader事件的触发
代码如下:
unit threadSocket3;

interface

uses
Classes,ScktComp,sysutils,Graphics,windows,messages,ExtCtrls,forms,decode,Controls,BatchRefresh;

type
tcsocket3 = class(TThread)
private
cskccr:Tclientsocket;
{ Private declarations }
protected
procedure write_canves;
procedure doconnect(Sender: TObject;Socket: TCustomWinSocket);
procedure doError(Sender: TObject;Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
procedure doRead(Sender: TObject;Socket: TCustomWinSocket);
procedure Execute; override;
public

end;
var
CmdStr:string;
DataStr:string;
AddrStr:string;
typeid:integer;
msg:tagMSG;
net_str:string;//网络状态提示字符串;
readerserial:string;//读卡器序列号字符串
time_str:integer;
pcmname:string;
PrintStr:string;
timecount:Ttimer;
k:integer;
function labfaile3(typeid:integer;net_str:string):string;//提示下载失败
function labsucc3(typeid:integer):string;//提示下载成功
procedure Receive_comm3(ReccmdStr:string); //接收命令字符串过程
procedure Receive_data3(RecDataStr:string);//接收数据字符串过程
procedure Recive_addr3(RecAddr:string);//接收IP地址字符串过程
procedure recive_type3(rectype:integer);//接收下载项目类型
procedure recive_name3(reader_name:string);//接收读卡器序名称
procedure recive_pcmid3(pcm_ID:integer);//取得PCM的ID
procedure net_messages3(net_code:integer);//网络状况信息提示;
procedure checkvalue3();
procedure time_count3();//计时器功能,如果超过时间未能接收到返回确认包,即强行结束线程

implementation
procedure checkvalue3();
begin
k:=0;
end;
procedure time_count3();
begin
timecount:=Ttimer.Create(nil);
timecount.Interval:=2;
timecount.Enabled:=true;
k:=k+1;
if (k=2000) and (hid1<>1) then
begin
PostThreadMessage(0,$0012,0,0);
end;


end;

procedure TCsocket3.write_canves;
var
tmp:string;
begin
tmp:=PrintStr;
FrmBatchRefresh.rededit.Lines.Add(tmp);
end;
procedure Receive_comm3(ReccmdStr:string); //接收命令字符串
begin
CmdStr:=ReccmdStr;
end;

procedure Receive_data3(RecDataStr:string);//接收数据字符串
begin
DataStr:=RecDataStr;
end;

procedure Recive_addr3(RecAddr:string);//接收IP地址
begin
AddrStr:=trim(RecAddr);
end;

procedure recive_type3(rectype:integer);//接收下载项目类型
begin
typeid:=rectype;
end;

procedure recive_pcmid3(pcm_id:integer);
begin
pcmname:=inttostr(pcm_id);
end;

procedure recive_name3(reader_name:string);
begin
readerserial:=reader_name;
end;

{连接事件}
procedure TCsocket3.doconnect(Sender: TObject;Socket: TCustomWinSocket);
begin
Socket.SendText(CmdStr);//命令包
Socket.SendText(DataStr);//数据抱
end;
{错误处理事件}
procedure TCsocket3.doError(Sender: TObject;Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
net_messages3(errorcode);
FrmBatchRefresh.rededit.SelAttributes.Color:=clyellow;
PrintStr:=labfaile3(typeid,net_str);
synchronize(write_canves);
errorcode:=0;
PostThreadMessage(0,$0012,0,0);


end;
{读取文本事件}
procedure TCsocket3.doRead(Sender: TObject;Socket: TCustomWinSocket);
var
FStr:string;
begin
FStr:=Socket.ReceiveText;
if Copy(Fstr,1,2)='DT' then
begin
hid1:=1;
PrintStr:=labsucc3(typeid);
FrmBatchRefresh.rededit.SelAttributes.Color:=claqua;
synchronize(write_canves);
PostThreadMessage(0,$0012,0,0);
end;
end;

procedure TCsocket3.Execute;
begin

freeonterminate:=true;
cskCCR:=Tclientsocket.Create(nil);
cskCCR.Address:=AddrStr;
cskCCR.Port:=8001;
cskCCR.ClientType:=ctNonBlocking;
cskCCR.OnConnect:=doconnect;
cskCCR.OnError:=doError;
cskCCR.OnRead :=doread;
cskCCR.Active:=true;
hid1:=0;
checkvalue3();
time_count3();
//__________消息循环________________________
while (GetMessage(msg,0,0,0)) do
begin
dispatchMessage(msg);
end;
//________________________________________
timecount.Enabled:=false;
timecount.Free;
hid:=1; //信号变量,标识线程是否结束0:未结束 1:结束 声明人:王志伟
cskCCR.Close;
cskCCR.Free;
terminate;
end;

function labsucc3(typeid:integer):string;
var
i:integer;
begin
i:=typeid;
case i of
11://门禁机优先序列
begin
result:='向'+pcmname+'号PCM下发'+trim(readerserial)+'的'+'门禁机配置文件成功';
end;
end;
end;
function labfaile3(typeid:integer;net_str:string):string;
var
i:integer;
netstr:string;
begin
i:=typeid;
case i of
11://门禁机优先序列
begin
result:=netstr+' '+'向'+pcmname+'号PCM下发'+trim(readerserial)+'的'+'门禁机配置文件失败';
end;
end;
end;
procedure net_messages3(net_code:integer);
begin
case net_code of
10004:begin
net_str:='操作被终止';
end;
10013:begin
net_str:='访问被拒绝';
end;
10024:begin
net_str:='打开太多的SOCKET';
end;
10048:begin
net_str:='地址已经被使用';
end;
10049:begin
net_str:='设置地址失败';
end;
10050:begin
net_str:='网络关闭';
end;
10051:begin
net_str:='网络不可达';
end;
10052:begin
net_str:='网络被重置';
end;
10055:begin
net_str:='缓冲不足';
end;
10057:begin
net_str:='SOCKET未连接';
end;
10058:begin
net_str:='SOCKET已经关闭';
end;
10060:begin
net_str:='连接超时';
end;
10061:begin
net_str:='连接被拒绝';
end;
10064:begin
net_str:='主机已经关机';
end;
10065:begin
net_str:=' 找不到路由';
end;
10067:begin
net_str:='进程太多';
end;
11001:begin
net_str:='找不到主机';
end
else
net_str:='其他原因导致网络错误';
end;
end;

end.

 
后退
顶部