indy 文件传输原理_第1页
indy 文件传输原理_第2页
indy 文件传输原理_第3页
indy 文件传输原理_第4页
indy 文件传输原理_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、还是研究 INDY控件的应用,发现网上很少实例可以使用,于是我就一边测一边理解这两个主要控件的应用方法。网上有 客户端传大文件 >服务端 的例子但却找不到 服务端传大文件 >客户端 的例子上次测试成功过一个 demo 就是服务端反传给客户端一个文本文件的DEMO(详细请看:/read.php?tid=1061),于是我就在想,既然可以反传文本文件,应该也是可以反传大文件,服务端传给客户端,应该也是可以像客户端传给服务端那样的。于是我又深入研究了一次,果然是可以的。其实 服务端和客户端 都是客户互相向对方传文

2、件的。但是呢,有一点不同的是,服务端是被动的,客户端是主动的,服务端要给客户端传文件的话,需要客户端向服务端发一个指令,这样服务端才知道是向哪个客户端发送文件流。关键就是 ReadBuffer 和 WriteBuffer两个方法。先看看这两个方法是做什么的,在 DELPHI中 查到这个函数的方法定义是这样的procedure ReadBuffer(var Buffer; Count: Longint);描述:Use ReadBuffer to read Count bytes from the stream into a buffer in cases where the number ofb

3、ytes is known and fixed, for example when reading in structures. ReadBuffer is used internallyfor loading from a stream and copying from a stream.大致的意思:使用 ReadBuffer 从流中取读一定数目(Count)字节的数据放进一个缓存区(buffer)ReadBuffer calls Read to do the actual reading. If Count bytes cannot be read from the stream,an E

4、ReadError exception is raised.如果不能读数据流 就会报错很明显 这个方法是从数据流里读取一定长度 然后放到缓存区里的,所以 应该不难理解procedure ReadBuffer(var Buffer; Count: Longint)的意思就是 从数据流里读 count 长的数据 放到缓存区 Buffer里。那说到这里 ,可能会有写朋友 会问,什么是缓存区Buffer?OK! 我告诉你,这个缓存区 Buffer 是 你自己定义好的一块内存空间。你可以定义 1M 也可以定义2M定义的方法非常简单,就是Buffer: array0.1023 of Byte;上面就定义好

5、了一个 1024字节的缓存区。OK 这个不是讨论的重点,你只需要明白 这个Buffer是你自己程序开辟的一块内存空间 ,就够了。看到这里 你应该明白了吧,procedure ReadBuffer(var Buffer; Count: Longint) 的作用了,就是从数据流里去读一块长度为 count的数据,然后放到我的内存缓存区 Buffer中。这个时候你可能会又有一个疑问了,这个数据流是什么东东?很好,这个正是我要给你说的重点,数据流就是一个 socket 里面使用到的传输的一个东西,很抽象?是不是?总之,你理解成为水流会比较好理解一些,看过在线电影么?那些流媒体也是一种数据流。就像水一样

6、流到你的电 脑,你的电脑不停接受,不停播放,下载文件也是一种数据流,只是你的电脑没有把它播放而是保存起来了而已。总之就是一长串的数据,有着顺序的一串数据,两个连接只要有数据传输,就会产生数据流,所以说客户端和服务端传输的就是这个所谓的数据流!那么 怎么去捕获这些数据流呢,捕获的是什么数据流呢?INDY控件里很多控件都有数据流的捕获函数,INDY控件里的方法就是用来捕获相应的数据流的。例如:在 客户端(IdTCPClient1 是一个IdTCPClient对象)来看:IdTCPClient1.ReadBuffer(Buff, ReadCount);客户端读取服务端传来的数据流,读取ReadCou

7、nt长度的数据放到内存的缓存区Buff中同样道理,WriteBuffer 的作用就恰好相反,IdTCPClient1.WriteBuffer(Buff, ReadCount);客户端从缓存区中读ReadCount长度的数据,放到朝服务端传输的数据流里那么在服务端来说,又是怎么和传数据流的呢?是IdTCPserver1.ReadBuffer(Buff, ReadCount)和IdTCPserver1.WriteBuffer(Buff, ReadCount)吗?如果你这样想 我很高兴,因为你可以举一反三了,但如果你这样写入程序中,程序会报错的。为什么?原因就是服务端对应的是多个客户端的连接,它本身

8、是多线程,每个线程各自对应着一个客户端(INDY里是这样的)。也就是说,你的服务器里,只要有一 个服务端(IdTCPserver对象 IdTCPserver1),它就可以监听并且处理很多不同的客户端传过来的请求。所以说,如果你直接用IdTCPserver1.ReadBuffer(Buff, ReadCount)和IdTCPserver1.WriteBuffer(Buff, ReadCount)是不行的,因为你不能告诉服务端到底把这个数据传输给哪个和它建立连接的客户端。让他同时传给所有的客户端吗?那服务端是不可能做得 到的,因为数据流会分散,并且会冲突。那服务端是怎么来处理和区分不同的客户端的呢

9、?在 IdTCPserver对象的方法事件里,看看他们的方法定义:procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);procedure TForm1.IdTCPServer1Exception(AThread: TIdPeerThread; AException: Except

10、ion);看到了么,很多方法里都有一个AThread: TIdPeerThread,没错,这个就是客户端了,它代表了一个客户端。比方说TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 里的 AThread ,就是只刚和它建立连接的客户端。procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);里的AThread 就是正在进行交流的客户端。所以,服务端就是通过 AThread 来对不同的客户端进行区分和处理的。说道这里,你就应该明白了 服务端的对数据流的读写是在AThread的

11、方法属性里的如:AThread.Connection.WriteBuffer(Buf, ReadCount);就是给建立连接的客户端发数据流,从缓存区Buf里读ReadCount长度的数据然后写到和客户端AThread连接的数据流中去反过来也是一样:AThread.Connection.ReadBuffer(Buf, ReadCount);就是从与客户端AThread连接的数据流中读取 ReadCount 长度的数据放到内存缓存区Buf中说到这里,你可能就已经都明白了吧,不过你可能还会有一个问题,那,如果是传输文件的话,这个数据流是怎么传的呢?整个过程是这样的:不管服务端给客户端传还是客户端给

12、服务端传,其实过程都是一样:为了便于理解,这里画一个图,A 要传输一个文件文件test.rar给 BA ->B分解动作A->创建文件流(fstream),用文件流的方法(fstream.ReadBuffer)将test.rar一段一段读取然后放到 Buffer中,然后用AThread.Connection.WriteBuffer将Buffer的数据写进要给B传输的数据流中|B->创建文件流(ftmpStream)->读取A传过来的数据流(IdTCPClient1.ReadBuffer),用文件流的方法(ftmpStream.WriteBuffer),将缓冲区(Buffe

13、r)中的数据写进本地文件总之:文件 test.rar >A的Buffer>数据流>B的Buffer>B的文件 test.rar| | | |文件流的方法 数据流的方法 数据流的方法 文件流的方法| | | | | | | | | |fstream.ReadBuffer | | | | | | | | | | | |AThread.Connection.WriteBuffer | | |IdTCPClient1.ReadBuffer |ftmpStream.WriteBuffer以我的经验,感觉最难理解的就是 文件流和数据流的读写混淆在一起,初学的时候特别费劲。看到上面的

14、流程,你应该明白了吧,其实传输一个文件,并不是仅仅对数据流的处理,还有的是文件流的处理。但是,文件流和数据流其实都是很类似的(都是ReadBuffer和WriteBuffer)。只要你分清是哪个对象作出的动作就可以了。好了,说到这里都是空洞的理论,那么现在就结合一下程序来分析一下。=发送方(本例是用服务端做发送方的)=procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);vars, sCommand: string;fStream : TFileStream; /定义一个文件流FFileName:string;aSize:in

15、teger;Buf : array0.1023 of Byte; /创建1M的内存缓冲区ReadCount: integer;beginFFileName:='test.rar' ; /我们约定就发送的文件名是 test.rartrys := uppercase(AThread.Connection.ReadLn); /接受客户端发过来的一个字符串sCommand := copy(s,1,3); /取这个字串的前3位if sCommand = 'GET' then /如果前3位字串是 “GET” 就发文件Beginmemo1.Lines.Add('接到指

16、令 GET ,开始相应工作') ;fstream:=tfilestream.Create(ExtractFileDir(ParamStr(0) +''+FFileName,fmOpenRead+ fmShareDenyNone); /针对 test.rar创建文件流AThread.Connection.WriteLn(ExtractFileName(FFileName);AThread.Connection.ReadLn(#13#10, 1000) ;/这两行作用是把文件名发送过去,ReadLn(#13#10, 1000) ;我目前也没搞清是做什么用的,但是对方要用得到

17、的,不能去掉的aSize:= fstream.Size; /从文件流中获取本地文件大小AThread.Connection.WriteBuffer(aSize, 4); /把这个大小数值传过去while fstream.Position < fstream.Size do/用一个循环来设定将文件一块一块放到要传输的数据流中begin/以下这块是判断每次读写到从文件流里读入缓存区的大小if fstream.Size - fstream.Position >= SizeOf(Buf) thenReadCount := sizeOf(Buf)else ReadCount := fstre

18、am.Size - fstream.Position;fstream.ReadBuffer(Buf, ReadCount); /从文件流中读取数据,放到缓存区AThread.Connection.WriteBuffer(Buf, ReadCount);/从缓存区把数据发到数据流中memo1.Lines.Add('写文件 '+inttostr(ReadCount)+' 字节 ') ;end;AThread.Connection.ReadLn; /这句作用不大,好像可以去掉的AThread.Connection.CloseWriteBuffer; /关闭传输数据流m

19、emo1.Lines.Add('关闭客户端文件流可写缓冲区 ') ;/释放资源FreeAndNil(fStream);memo1.Lines.Add('释放资源 ') ;EndfinallyAThread.Connection.Disconnect;memo1.Lines.Add('断开本次连接 ') ;end;end;=接受方(本例是用客户端做接受方的)=客户端 要发送一个约定好的指令给服务端,然后服务端给它传输文件test.rarprocedure TForm1.Button1Click(Sender: TObject);varaFileNa

20、me:string;ftmpStream : TFileStream; /定义一个文件流aFileSize:integer;ReadCount:integer;Buff: array0.1023 of Byte; /定义一个缓存区beginwith IdTCPClient1 dobegintryconnect; /连接服务端if connected thenbeginwriteln('GET'); /发送 GET指令memo1.Lines.Add('GET指令已经发送');aFileName := IdTCPClient1.ReadLn(#13#10, 100)

21、 ; /读取传输过来的文件名if aFileName = '' then Exit;/如果已经存在这个文件,先删掉if FileExists(ExtractFileDir(ParamStr(0) + ''+aFileName) thenbeginmemo1.Lines.Add('本地目录存在'+aFileName+',删掉这个文件');DeleteFile(ExtractFileDir(ParamStr(0) + ''+aFileName);end;/读取传输过来的文件名IdTCPClient1.WriteLn;I

22、dTCPClient1.ReadBuffer(aFileSize, 4); /读取文件大小ProgressBar1.Max := aFileSize; /设置进度条的显示最大数字/创建文件流ftmpStream:= TFileStream.Create(ExtractFileDir(ParamStr(0) +''+aFileName, fmCreate);/用一个循环,不停读取数据流 然后利用文件流写入文件中repeat/确定本次循环读取的快数大小if aFileSize - ftmpStream.Size > SizeOf(Buff) thenReadCount :=

23、SizeOf(Buff)elseReadCount := aFileSize - ftmpStream.Size;IdTCPClient1.ReadBuffer(Buff, ReadCount); /将传来的数据流读入缓存区ftmpStream.WriteBuffer(Buff, ReadCount); /将缓存区的数据写进文件中memo1.Lines.Add('写入文件 '+inttostr(ReadCount)+'字节。');ProgressBar1.Position := ftmpStream.Size; /改变进度条的状态Application.Proc

24、essMessages;until ftmpStream.Size >= aFileSize;memo1.Lines.Add('读取连接流中数据,写入创建的文件流对象ftmpStream中。');disconnect;memo1.Lines.Add('断开本次连接');end;finallyFreeAndNil(fTmpStream);memo1.Lines.Add('释放文件流对象ftmpStream占用的资源。');end;end;end;好了 至此,整个流程以及核心代码已经介绍完了,不知道你是否有所收获?附: 源码客户端:unit c

25、lient;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,IdTCPClient, ComCtrls;typeTForm1 = class(TForm)    IdTCPClient1: TIdTCPClient;    Memo1: TMemo; &

26、#160;  Button1: TButton;    ProgressBar1: TProgressBar;    procedure Button1Click(Sender: TObject);    procedure FormCreate(Sender: TObject);private     Private declarations public     Publi

27、c declarations end;varForm1: TForm1;implementation$R *.dfmprocedure TForm1.Button1Click(Sender: TObject);varaFileName:string;ftmpStream : TFileStream;aFileSize:integer;ReadCount:integer;Buff: array0.1023 of Byte;begin    with   IdTCPClient1 do   

28、0;begin    try      connect;      if connected then      begin      writeln('GET');      memo1.Lines.Add('GET指令已经发送

29、9;);        aFileName := IdTCPClient1.ReadLn(#13#10, 100) ;        if aFileName = '' then   Exit;        if FileExists(ExtractFileDir(ParamStr(0) + ''

30、;+aFileName) then        begin        memo1.Lines.Add('本地目录存在'+aFileName+',删掉这个文件');        DeleteFile(ExtractFileDir(ParamStr(0) + ''+aFileName); 

31、0;      end;        IdTCPClient1.WriteLn;        IdTCPClient1.ReadBuffer(aFileSize, 4);        ProgressBar1.Max := aFileSize;    &

32、#160;   ftmpStream:= TFileStream.Create(ExtractFileDir(ParamStr(0) +''+aFileName, fmCreate);        repeat          if aFileSize - ftmpStream.Size > SizeOf(Buff) then   

33、         ReadCount := SizeOf(Buff)          else            ReadCount := aFileSize - ftmpStream.Size;       &#

34、160;  IdTCPClient1.ReadBuffer(Buff, ReadCount);          ftmpStream.WriteBuffer(Buff, ReadCount);          memo1.Lines.Add('写入文件 '+inttostr(ReadCount)+'字节。');  

35、0;       ProgressBar1.Position := ftmpStream.Size;          Application.ProcessMessages;         until ftmpStream.Size >= aFileSize;      

36、;  memo1.Lines.Add('读取连接流中数据,写入创建的文件流对象ftmpStream中。');        disconnect;        memo1.Lines.Add('断开本次连接');      end;    finally  

37、0;   FreeAndNil(fTmpStream);      memo1.Lines.Add('释放文件流对象ftmpStream占用的资源。');    end;    end;end;procedure TForm1.FormCreate(Sender: TObject);begin   IdTCPClient1.Host:='' ;&#

38、160;  IdTCPClient1.port:=5555 ;end;end.服务端unit serverunit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, IdBaseComponent, IdComponent, IdTCPServer,IdSocketHandle,IdResourceStrings,IdStack, IdGlobal,StdCtrls;typeTForm1 = class(TForm) &#

39、160;  Memo1: TMemo;    Button1: TButton;    Button2: TButton;    IdTCPServer1: TIdTCPServer;    procedure IdTCPServer1Execute(AThread: TIdPeerThread);    procedure Button1Click(Sender: TObje

40、ct);    procedure Button2Click(Sender: TObject);    procedure FormCreate(Sender: TObject);private     Private declarations public     Public declarations end;varForm1: TForm1;implementation$R *.dfmprocedure TForm1.IdTCPS

41、erver1Execute(AThread: TIdPeerThread);var    s, sCommand: string;    fStream : TFileStream;    FFileName:string;    aSize:integer;    Buf : array0.1023 of Byte;    ReadCount: integ

42、er;begin    FFileName:='test.rar' ;     try       s := uppercase(AThread.Connection.ReadLn);       sCommand := copy(s,1,3);       if sCommand

43、= 'GET' then         Begin            memo1.Lines.Add('接到指令 GET ,开始相应工作') ;            /fstream:=tfilestream.Create(Ext

44、ractFileDir(ParamStr(0) +'key.txt',fmOpenRead + fmShareDenyNone);            /memo1.Lines.Add('创建文件流 ') ;            /AThread.Connection.OpenWriteBuffer;  

45、;          /memo1.Lines.Add('打开客户端文件流可写缓冲区 ') ;            /改写的程序            fstream:=tfilestream.Create(ExtractFileDir(Pa

46、ramStr(0) +''+FFileName,fmOpenRead + fmShareDenyNone);            AThread.Connection.WriteLn(ExtractFileName(FFileName);            AThread.Connection.ReadLn(#13#10, 1000)

47、;            aSize:= fstream.Size;            AThread.Connection.WriteBuffer(aSize, 4);            while fstream.Positio

48、n < fstream.Size do             begin               if fstream.Size - fstream.Position >= SizeOf(Buf) then        

49、          ReadCount := sizeOf(Buf)                 else ReadCount := fstream.Size - fstream.Position;          

50、0;    fstream.ReadBuffer(Buf, ReadCount);               AThread.Connection.WriteBuffer(Buf, ReadCount);               memo1.Lines.

51、Add('写文件 '+inttostr(ReadCount)+' 字节 ') ;            end;            AThread.Connection.ReadLn;            AThread.Connection.CloseWriteBuffer;            memo1.Lines.Add('关闭客户端文件流可写缓冲区 ') ;            FreeAndNil(fStream);       &

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论