INDY 9.0 ★★★ idTcpClient.ReadStream 时停在那了?

lencon 2008-03-27 12:34:39
本例程是一个远程读SQL数据的例子,发送SQL,让服务器把查询的结果传过来,代码如下

客户端:
=============
procedure TF_ClientForm.BitBtn2Click(Sender: TObject);
var
s:string;
SqlStr:string;
I:INTEGER;
stream:TStringStream;
begin
SqlStr:=trim(edit2.Text); //sql语句
ado.Close;
Tcpc.Host:=ips;
tcpc.Port:=ports;
if not Tcpc.Connected then Tcpc.Connect;
while tcpc.Connected do
begin
try
Stream := TStringStream.Create('');
Tcpc.WriteLn('SQLO'+SqlStr); //能正确发出,服务器己收到
showmessage('1'); //这句能正常显示
Stream.Position:=0;
Tcpc.ReadStream(Stream, -1, true); //一直停在这了,等到超时才出错,不能显示下一句'2'了.
showmessage('2');
Stream.Position:=0;
s:=Stream.DataString;
finally
Stream.Free;
tcpc.Disconnect;
end;
end;
end;

服务端:
===================
procedure TF_ServerForm.TCPSExecute(AThread: TIdPeerThread);
var
Mess:string;
SqlStr:string;
SqlData:String;
Stream:TstringStream;
AdoQ:Tadoquery;
begin
Mess:=AThread.Connection.ReadLn;
if COPY(MESS,1,4)='SQLO' THEN //SQL查询
BEGIN
SqlStr:=copy(Mess,5,Length(Mess)-4);
memo.Lines.Add(SqlStr); //正常显示发来的SQL字符
AdoQ:=Tadoquery.Create(self);
AdoQ.Connection:=Ado;
with AdoQ do
begin
close;
Sql.Clear;
Sql.Text:=SqlStr;
open;
SqlData:=RecordsetToXML(recordset); //将数据集转成字符串,正常
CLose;
free;
end;
Stream:=TstringStream.Create(SqlData); //转到流,正常
Stream.Position:=0; //流还原 Stream.Datastring后,数据也正常,证明流有正确数据
AThread.Connection.OpenWriteBuffer;
AThread.Connection.WriteStream(Stream); //发送情况不清楚,客户端始终不动了.
AThread.Connection.CloseWriteBuffer;
Stream.Free;
exit;
END;
end;


请高手对照我的注释,给予帮助
...全文
485 12 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
散乱心绪 2008-03-27
  • 打赏
  • 举报
回复

procedure TForm1.Button1Click(Sender: TObject);
var
SqlStr:string;
Stream:TStringStream;
Size:Integer;
begin
SqlStr:=trim(edit2.Text); //sql语句
try
if not Tcpc.Connected then begin
Tcpc.Host:= '127.0.0.1';
tcpc.Port:= 11111;
Tcpc.Connect;
end;
except
end;

if not Tcpc.Connected then Exit;
Stream := TStringStream.Create('');
try
Tcpc.WriteLn('SQLO'+SqlStr);
// 读取流大小
Size:= Tcpc.ReadInteger;
if Size = 0 then Exit;
// 回应
Tcpc.WriteLn('send data');
// 接收流数据
Tcpc.ReadStream(Stream, Size);
// 接收完毕
Stream.Position:= 0;
showmessage('接收完毕'+#13#10+Stream.DataString);
finally
Stream.Free;
tcpc.Disconnect;
end;
end;




procedure TForm1.IdTCPServer1AfterCommandHandler(ASender: TIdTCPServer;
AThread: TIdPeerThread);
var
Mess:string;
SqlStr:string;
SqlData:String;
Stream:TstringStream;
AdoQ:Tadoquery;
begin
Mess:=AThread.Connection.ReadLn;
if COPY(MESS,1,4)='SQLO' THEN //SQL查询
BEGIN
SqlStr:=copy(Mess,5,Length(Mess)-4);
memo.Lines.Add(SqlStr); //正常显示发来的SQL字符
AdoQ:=Tadoquery.Create(self);
AdoQ.Connection:=Ado;
with AdoQ do
begin
close;
Sql.Clear;
Sql.Text:=SqlStr;
open;
SqlData:=RecordsetToXML(recordset); //将数据集转成字符串,正常
CLose;
free;
end;
Stream:=TstringStream.Create(SqlData); //转到流,正常
try
Stream.Position:=0; //流还原 Stream.Datastring后,数据也正常,证明流有正确数据

with AThread.Connection do begin
WriteInteger(Stream.Size);
if Stream.Size = 0 then Exit;
Mess:= ReadLn;
if Mess <> 'send data' then Exit;
WriteStream(Stream);
end;

finally
Stream.Free;
end;
END;
end;
lencon 2008-03-27
  • 打赏
  • 举报
回复
to slxx0712

你就不能帮我就着代码稍改一下吗,你的发送大小,我试了怎么还是不行
aniugee 2008-03-27
  • 打赏
  • 举报
回复
受教了
散乱心绪 2008-03-27
  • 打赏
  • 举报
回复
所以我说让indy自己发送stream大小实现的不是很好

server端,如果让indy自己发送大小

if AWriteByteCount then begin
// 发送流大小
WriteInteger(LSize);
end;
BeginWork(wmWrite, LSize); try
LBuffer := TMemoryStream.Create; try
LBuffer.SetSize(FSendBufferSize);
while True do begin
LSize := Min(LStreamEnd - AStream.Position, FSendBufferSize);
if LSize = 0 then begin
Break;
end;
LSize := AStream.Read(LBuffer.Memory^, LSize);
if LSize = 0 then begin
raise EIdNoDataToRead.Create(RSIdNoDataToRead);
end;
// 直接发送了,但这个时候,并不能确认客户端是否已经接收完毕流大小,并且在等待接收流数据了。
WriteBuffer(LBuffer.Memory^, LSize);
end;
finally FreeAndNil(LBuffer); end;
finally EndWork(wmWrite); end;
散乱心绪 2008-03-27
  • 打赏
  • 举报
回复
发表于:2008-03-27 10:33:514楼 得分:0
用readbuffer,它后面的语句在服务端响应之前不会执行吗?会处于等待状态还是会死掉?


不管是readbuff,readstream,readinteger,到最后都一样,
都是等待,不是死掉,你如果是在线程中做的,那对界面不会有影响,如果你在主线程中,那界面会假死
indy控件包里有一个控件 idAntiFreeze 可以让你在主线程中等待也不会让界面假死。

建议看看indy的demo,怎么会不稳定呢,
lencon 2008-03-27
  • 打赏
  • 举报
回复
改成这样writestream(stream,true,true,0)
还是不行,难道非要先告诉一下数据流大小不成
aniugee 2008-03-27
  • 打赏
  • 举报
回复
是我们自已没写好的原因,TCP协议比UPD安全多了
lencon 2008-03-27
  • 打赏
  • 举报
回复
为什么
indy DEMO 中可以那样写流发送,怎么这么不稳定
aniugee 2008-03-27
  • 打赏
  • 举报
回复
用readbuffer,它后面的语句在服务端响应之前不会执行吗?会处于等待状态还是会死掉?
散乱心绪 2008-03-27
  • 打赏
  • 举报
回复
你直接通过 Tcpc.ReadStream(Stream, -1, true);
参数-1是让idTcpClient自己去读,你看源码就能发现其实内部需要读取一次stream.size的,但他的实现方式我不是很认同,还是自己读取大小来的保险的多,还可以进行其他判断。

procedure TIdTCPConnection.ReadStream(AStream: TStream; AByteCount: Integer = -1;
const AReadUntilDisconnect: Boolean = False);
var
...
begin
...
if (AByteCount = -1) and (AReadUntilDisconnect = False) then begin
// Read size from connection
AByteCount := ReadInteger;
end;
...
end;
但服务端默认情况是不发送stream大小的,所以你的代码中服务端的 writestream(Stream)也写错,应该写成 writestream(stream,true,true,0)
procedure TIdTCPConnection.WriteStream(AStream: TStream; const AAll: boolean = true;
const AWriteByteCount: Boolean = False; const ASize: Integer = 0);
var
...
begin
....
if AWriteByteCount then begin
WriteInteger(LSize);
end;
...
end;
散乱心绪 2008-03-27
  • 打赏
  • 举报
回复
idtcp是阻塞式的,所以不应该用 while tcpc.connected等待连接成功,虽然你这样写基本上也不会出什么问题。
因为程序会等待 tcpc.connect 过程执行完毕后再执行下面的语句,你tcpc.connect执行结束后,其实已经知道到底连接成功了还是失败了。

另外就是 tcpc.readstream了,个人建议是自己去读取stream的长度,不要用参数-1,纯粹个人一直用下来后的感受。
给你写个流程吧,关键还是自己琢磨

client server
服务端准备状态
准备好SQL语句,并发送给Srv
发送完毕后,等待服务端返回查询结果
读取保存了查询结果的stream的size
readbuffer(***,sizeof(integer));//客户端会挂起在这里,等待服务端响应

服务端收到客户端请求,进行查询,得到结果和结果大小
将查询结果返回给client,并等待回应
readbuffer()...// 服务端也会挂起在这里,等待客户端响应

客户端收到到查询结果后(包括结果大小),
设定stream.size:= ***
发送指令给服务端,让服务端发送数据
同时 tcpclient.readstream // 客户端准备好接收服务端即将发送的数据

服务端收到发送数据指令后,发送 stream.data

收完数据。disconnect
aniugee 2008-03-27
  • 打赏
  • 举报
回复
我也遇到过这种问题,出错是在这一句 Tcpc.ReadStream(Stream, -1, true);
原因是TCPSExecute时,没有及时的WriteStream导致的。
好像是Tcpc.ReadStream不会停在那时等待,除非服务端立即WriteStream才不问题。
期待高手给分析一下如何解决。

1,594

社区成员

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

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