IdTCPClient与 IdTCPServer 间传输问题,请指教。

sqrkim 2009-05-09 10:45:03
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, Grids, DBGrids, StdCtrls, ADODB, IdTCPServer,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient;

type
Tuser = record
s_id:array[0..50] of string;
s_name:array[0..50] of string;
s_age:array[0..50] of string;
s_text:array[0..50] of string;
dt_date:array[0..50] of TDateTime;
end;

TForm1 = class(TForm)
ADOConnection1: TADOConnection;
Button1: TButton;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
Memo1: TMemo;
IdTCPClient1: TIdTCPClient;
IdTCPServer1: TIdTCPServer;
ADOQuery1: TADOQuery;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
strm: TMemoryStream;
//user:Tuser;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var i:integer; user:Tuser;
begin
strm:= TMemoryStream.Create;

ADOQuery1.First;
while not ADOQuery1.Eof do
begin
for i:= 1 to ADOQuery1.RecordCount do
begin
user.s_id[i-1]:= ADOQuery1.FieldByName('i_id').AsString;
user.s_name[i-1]:= ADOQuery1.FieldByName('vc_name').AsString;
user.s_age[i-1]:= ADOQuery1.FieldByName('vc_age').AsString;
user.s_text[i-1]:= ADOQuery1.FieldByName('vc_text').AsString;
user.dt_date[i-1]:= ADOQuery1.FieldByName('dt_date').AsDateTime;
ADOQuery1.Next;
end;
end;

strm.WriteBuffer(user,sizeof(Tuser));

self.IdTCPClient1.Host:='127.0.0.1';
self.IdTCPClient1.Port:=9999;
try
self.IdTCPClient1.Connect(5000);
//self.IdTCPClient1.Writebuffer(strm,sizeof(strm));
self.IdTCPClient1.WriteStream(strm,true,False);
self.IdTCPClient1.Disconnect;
except
on e:exception do
memo1.Lines.Add(e.Message);
end;

strm.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
with ADOQuery1 do
begin
Close;
SQL.Clear;
SQL.Add('select * from sy');
Open;
end;

self.IdTCPServer1.DefaultPort:=9999;
self.IdTCPServer1.Active:= true;
end;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
i:integer; user:TUser;
begin
strm:= TMemoryStream.Create;

with AThread.Connection do
begin
AThread.Connection.ReadStream(strm,-1,FALSE);// 跟踪:strm显示为strm(),证明数据没过来
strm.ReadBuffer(user,sizeof(TUser));
memo1.Lines.Add(user.s_id[1]); //没有看到user.s_id[1]的数据
end;
end;

end.

不知道具体应该怎么改,谢谢大家。
...全文
168 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
我试验了 如果中间不用TMemoryStream的话 client端直接用self.IdTCPClient1.Writebuffer(user,sizeof(Tuser)); server端用AThread.Connection.ReadBuffer(user,sizeof(TUser)); 这样的话数据是可以读出来的。但是老是报AV的错误,实在是头疼
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 unsigned 的回复:]
Server端ReadStream前先Read四个字节,比如使用ReadInteger之类的,然后再根据这四个字节ReadStream.接着又是Read四个字节,ReadStream...依此类推.
[/Quote]
那我是不是可以直接设置strm.Position:= 4;挪到第4个字节然后开始读,可是还是没效果。你说的用 readinteger 能再具体一些么?
或者会不会是我写的时候哪里出错了?没有写进去。
僵哥 2009-05-09
  • 打赏
  • 举报
回复
Server端ReadStream前先Read四个字节,比如使用ReadInteger之类的,然后再根据这四个字节ReadStream.接着又是Read四个字节,ReadStream...依此类推.
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
没人帮忙?
ks_reny 2009-05-09
  • 打赏
  • 举报
回复
學習
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
自己顶下
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
哈哈 哦了 结贴。
SuperTitan001 2009-05-09
  • 打赏
  • 举报
回复
这个就要你自己处理了,如果有大的就定义大点,呵呵
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
那如果长度不够100 或者超出100了呢?程序会做何处理?
SuperTitan001 2009-05-09
  • 打赏
  • 举报
回复
固定长度100,这样sizeof(user)就有了准确的值了
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
啊 明白了 原来是这样。谢谢SuperTitan001,最后一个问题s_name:array[0..50] of string[100]中 string[100]的意思是说 s_name的最大字符长度为100么?
SuperTitan001 2009-05-09
  • 打赏
  • 举报
回复
刚刚试了试
客户端:

procedure TForm1.BitBtn1Click(Sender: TObject);
var
i:integer;
user:Tuser;
begin
IdTCPClient1.Host:='127.0.0.1';
IdTCPClient1.Port:=9999;
try
self.IdTCPClient1.Connect(5000);
for i:= 0 to 50 do
begin
user.s_id[i]:= inttostr(i);
user.s_name[i]:= 'name'+inttostr(i);
user.s_age[i]:= 'age'+inttostr(i);
user.s_text[i]:= 'text'+inttostr(i);

end;
IdTCPClient1.WriteBuffer(user,sizeof(user),true);
except
on e:exception do
memo1.Lines.Add(e.Message);
end;
IdTCPClient1.Disconnect;
end;

服务端:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
user:Tuser;
i:integer;
begin
AThread.Connection.ReadBuffer(user,sizeof(user));
for i:=0 to 50 do
memo1.Lines.Add(user.s_name[i]);
end;


关键是服务端要知道数据包的大小
SuperTitan001 2009-05-09
  • 打赏
  • 举报
回复
type
Tuser = record
s_id:array[0..50] of string;
s_name:array[0..50] of string;
s_age:array[0..50] of string;
s_text:array[0..50] of string;
end;

改成
Tuser = record
s_id:array[0..50] of string[100];
s_name:array[0..50] of string[100];
s_age:array[0..50] of string[100];
s_text:array[0..50] of string[100];

中间就不要用什么MemoryStream了
发送就是IdTCPClient1.WriteBuffer(user,sizeof(user),true);

接收用AThread.Connection.ReadBuffer(user,sizeof(user));

我试过是可以的
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
还有个问题 我在跟踪的时候发现cmd:= Readln();//接收到client端传来的流长度 为0, 是不是strm.WriteBuffer(user,sizeof(Tuser));根本就没有写入进去啊?
sqrkim 2009-05-09
  • 打赏
  • 举报
回复
谢谢unsigned 和 supertitan001 的帮助和指点。我理解后编译如下代码,不知是否正确 还请指点。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, Grids, DBGrids, StdCtrls, ADODB, IdTCPServer,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient;

type
Tuser = record
s_id:array[0..50] of string;
s_name:array[0..50] of string;
s_age:array[0..50] of string;
s_text:array[0..50] of string;
//dt_date:array[0..50] of TDateTime;
end;

TForm1 = class(TForm)
ADOConnection1: TADOConnection;
Button1: TButton;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
Memo1: TMemo;
IdTCPClient1: TIdTCPClient;
IdTCPServer1: TIdTCPServer;
ADOQuery1: TADOQuery;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
strm: TMemoryStream;
//user:Tuser;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var i:integer; user:Tuser; cmd:string;
begin
ADOQuery1.First;

//赋值
while not ADOQuery1.Eof do
begin
for i:= 1 to ADOQuery1.RecordCount do
begin
user.s_id[i-1]:= ADOQuery1.FieldByName('i_id').AsString;
user.s_name[i-1]:= ADOQuery1.FieldByName('vc_name').AsString;
user.s_age[i-1]:= ADOQuery1.FieldByName('vc_age').AsString;
user.s_text[i-1]:= ADOQuery1.FieldByName('vc_text').AsString;
//user.dt_date[i-1]:= ADOQuery1.FieldByName('dt_date').AsDateTime;
ADOQuery1.Next;
end;
end;
strm:= TMemoryStream.Create;
strm.WriteBuffer(user,sizeof(Tuser));

IdTCPClient1.Host:='127.0.0.1';
IdTCPClient1.Port:=9999;
try
self.IdTCPClient1.Connect(5000);
IdTCPClient1.WriteLn(IntToStr(strm.Size)); //向服务器发送流的大小,此时跳到服务器端
cmd:= IdTCPClient1.ReadLn; //接受服务器端的命令字符,在这里我暂时设置为空
IdTCPClient1.OpenWriteBuffer; //准备发送缓冲
IdTCPClient1.WriteStream(strm,true,true,strm.Size);
IdTCPClient1.CloseWriteBuffer; //结束发送缓冲

IdTCPClient1.Disconnect;
except
on e:exception do
memo1.Lines.Add(e.Message);
end;

strm.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
with ADOQuery1 do
begin
Close;
SQL.Clear;
SQL.Add('select * from sy');
Open;
end;

self.IdTCPServer1.DefaultPort:=9999;
self.IdTCPServer1.Active:= true;
end;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
i:integer; user:TUser; cmd:string;
begin
strm:= TMemoryStream.Create;

with AThread.Connection do
begin
cmd:= Readln();//接收到client端传来的流长度
strm.Size:= StrToInt(cmd);//设置要接收的流的大小
WriteLn('BEGIN');//返回client端开始传输数据,此时跳转到client端

ReadStream(strm,-1,True); //这里报错 AV错误

strm.ReadBuffer(user,sizeof(TUser));
memo1.Lines.Add(user.s_id[1]);
end;
end;

end.
SuperTitan001 2009-05-09
  • 打赏
  • 举报
回复
你在传送string的时候,服务器端无法知道你传送的总字节数是多少。
楼上说的是一个办法。
另外可以将string改为固定长度,比如string[200]。另外Tdatetime也是传不了的,也用字符串传送吧
僵哥 2009-05-09
  • 打赏
  • 举报
回复
当然不是.
WriteStream(Stream,true,true (*注意这个参数,它实际了先发送四个字节作为后续Stream的实际长度,就形成了一个简单的网络协议*))

因此,你只需要在接收端先接收4个字节作为长度,然后再根据这个长度来接收后续数据即可.

个人建议楼主加深点网络编程方面的基础知识,先了解什么是TCP流协议.

1,593

社区成员

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

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