版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、還是研究 INDY控件的應(yīng)用,發(fā)現(xiàn)網(wǎng)上很少實例可以使用,于是我就一邊測一邊理解這兩個主要控件的應(yīng)用方法。網(wǎng)上有 客戶端傳大文件 >服務(wù)端 的例子但卻找不到 服務(wù)端傳大文件 >客戶端 的例子上次測試成功過一個 demo 就是服務(wù)端反傳給客戶端一個文本文件的DEMO(詳細(xì)請看:/read.php?tid=1061),于是我就在想,既然可以反傳文本文件,應(yīng)該也是可以反傳大文件,服務(wù)端傳給客戶端,應(yīng)該也是可以像客戶端傳給服務(wù)端那樣的。于是我又深入研究了一次,果然是可以的。其實 服務(wù)端和客戶端 都是客戶互相向?qū)Ψ絺魑?/p>
2、件的。但是呢,有一點不同的是,服務(wù)端是被動的,客戶端是主動的,服務(wù)端要給客戶端傳文件的話,需要客戶端向服務(wù)端發(fā)一個指令,這樣服務(wù)端才知道是向哪個客戶端發(fā)送文件流。關(guān)鍵就是 ReadBuffer 和 WriteBuffer兩個方法。先看看這兩個方法是做什么的,在 DELPHI中 查到這個函數(shù)的方法定義是這樣的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 從流中取讀一定數(shù)目(Count)字節(jié)的數(shù)據(jù)放進(jìn)一個緩存區(qū)(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.如果不能讀數(shù)據(jù)流 就會報錯很明顯 這個方法是從數(shù)據(jù)流里讀取一定長度 然后放到緩存區(qū)里的,所以 應(yīng)該不難理解procedure ReadBuffer(var Buffer; Count: Longint)的意思就是 從數(shù)據(jù)流里讀 count 長的數(shù)據(jù) 放到緩存區(qū) Buffer里。那說到這里 ,可能會有寫朋友 會問,什么是緩存區(qū)Buffer?OK! 我告訴你,這個緩存區(qū) Buffer 是 你自己定義好的一塊內(nèi)存空間。你可以定義 1M 也可以定義2M定義的方法非常簡單,就是Buffer: array0.1023 of Byte;上面就定義好
5、了一個 1024字節(jié)的緩存區(qū)。OK 這個不是討論的重點,你只需要明白 這個Buffer是你自己程序開辟的一塊內(nèi)存空間 ,就夠了??吹竭@里 你應(yīng)該明白了吧,procedure ReadBuffer(var Buffer; Count: Longint) 的作用了,就是從數(shù)據(jù)流里去讀一塊長度為 count的數(shù)據(jù),然后放到我的內(nèi)存緩存區(qū) Buffer中。這個時候你可能會又有一個疑問了,這個數(shù)據(jù)流是什么東東?很好,這個正是我要給你說的重點,數(shù)據(jù)流就是一個 socket 里面使用到的傳輸?shù)囊粋€東西,很抽象?是不是?總之,你理解成為水流會比較好理解一些,看過在線電影么?那些流媒體也是一種數(shù)據(jù)流。就像水一樣
6、流到你的電 腦,你的電腦不停接受,不停播放,下載文件也是一種數(shù)據(jù)流,只是你的電腦沒有把它播放而是保存起來了而已??傊褪且婚L串的數(shù)據(jù),有著順序的一串?dāng)?shù)據(jù),兩個連接只要有數(shù)據(jù)傳輸,就會產(chǎn)生數(shù)據(jù)流,所以說客戶端和服務(wù)端傳輸?shù)木褪沁@個所謂的數(shù)據(jù)流!那么 怎么去捕獲這些數(shù)據(jù)流呢,捕獲的是什么數(shù)據(jù)流呢?INDY控件里很多控件都有數(shù)據(jù)流的捕獲函數(shù),INDY控件里的方法就是用來捕獲相應(yīng)的數(shù)據(jù)流的。例如:在 客戶端(IdTCPClient1 是一個IdTCPClient對象)來看:IdTCPClient1.ReadBuffer(Buff, ReadCount);客戶端讀取服務(wù)端傳來的數(shù)據(jù)流,讀取ReadCou
7、nt長度的數(shù)據(jù)放到內(nèi)存的緩存區(qū)Buff中同樣道理,WriteBuffer 的作用就恰好相反,IdTCPClient1.WriteBuffer(Buff, ReadCount);客戶端從緩存區(qū)中讀ReadCount長度的數(shù)據(jù),放到朝服務(wù)端傳輸?shù)臄?shù)據(jù)流里那么在服務(wù)端來說,又是怎么和傳數(shù)據(jù)流的呢?是IdTCPserver1.ReadBuffer(Buff, ReadCount)和IdTCPserver1.WriteBuffer(Buff, ReadCount)嗎?如果你這樣想 我很高興,因為你可以舉一反三了,但如果你這樣寫入程序中,程序會報錯的。為什么?原因就是服務(wù)端對應(yīng)的是多個客戶端的連接,它本身
8、是多線程,每個線程各自對應(yīng)著一個客戶端(INDY里是這樣的)。也就是說,你的服務(wù)器里,只要有一 個服務(wù)端(IdTCPserver對象 IdTCPserver1),它就可以監(jiān)聽并且處理很多不同的客戶端傳過來的請求。所以說,如果你直接用IdTCPserver1.ReadBuffer(Buff, ReadCount)和IdTCPserver1.WriteBuffer(Buff, ReadCount)是不行的,因為你不能告訴服務(wù)端到底把這個數(shù)據(jù)傳輸給哪個和它建立連接的客戶端。讓他同時傳給所有的客戶端嗎?那服務(wù)端是不可能做得 到的,因為數(shù)據(jù)流會分散,并且會沖突。那服務(wù)端是怎么來處理和區(qū)分不同的客戶端的呢
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 就是正在進(jìn)行交流的客戶端。所以,服務(wù)端就是通過 AThread 來對不同的客戶端進(jìn)行區(qū)分和處理的。說道這里,你就應(yīng)該明白了 服務(wù)端的對數(shù)據(jù)流的讀寫是在AThread的
11、方法屬性里的如:AThread.Connection.WriteBuffer(Buf, ReadCount);就是給建立連接的客戶端發(fā)數(shù)據(jù)流,從緩存區(qū)Buf里讀ReadCount長度的數(shù)據(jù)然后寫到和客戶端AThread連接的數(shù)據(jù)流中去反過來也是一樣:AThread.Connection.ReadBuffer(Buf, ReadCount);就是從與客戶端AThread連接的數(shù)據(jù)流中讀取 ReadCount 長度的數(shù)據(jù)放到內(nèi)存緩存區(qū)Buf中說到這里,你可能就已經(jīng)都明白了吧,不過你可能還會有一個問題,那,如果是傳輸文件的話,這個數(shù)據(jù)流是怎么傳的呢?整個過程是這樣的:不管服務(wù)端給客戶端傳還是客戶端給
12、服務(wù)端傳,其實過程都是一樣:為了便于理解,這里畫一個圖,A 要傳輸一個文件文件test.rar給 BA ->B分解動作A->創(chuàng)建文件流(fstream),用文件流的方法(fstream.ReadBuffer)將test.rar一段一段讀取然后放到 Buffer中,然后用AThread.Connection.WriteBuffer將Buffer的數(shù)據(jù)寫進(jìn)要給B傳輸?shù)臄?shù)據(jù)流中|B->創(chuàng)建文件流(ftmpStream)->讀取A傳過來的數(shù)據(jù)流(IdTCPClient1.ReadBuffer),用文件流的方法(ftmpStream.WriteBuffer),將緩沖區(qū)(Buffe
13、r)中的數(shù)據(jù)寫進(jìn)本地文件總之:文件 test.rar >A的Buffer>數(shù)據(jù)流>B的Buffer>B的文件 test.rar| | | |文件流的方法 數(shù)據(jù)流的方法 數(shù)據(jù)流的方法 文件流的方法| | | | | | | | | |fstream.ReadBuffer | | | | | | | | | | | |AThread.Connection.WriteBuffer | | |IdTCPClient1.ReadBuffer |ftmpStream.WriteBuffer以我的經(jīng)驗,感覺最難理解的就是 文件流和數(shù)據(jù)流的讀寫混淆在一起,初學(xué)的時候特別費勁??吹缴厦娴?/p>
14、流程,你應(yīng)該明白了吧,其實傳輸一個文件,并不是僅僅對數(shù)據(jù)流的處理,還有的是文件流的處理。但是,文件流和數(shù)據(jù)流其實都是很類似的(都是ReadBuffer和WriteBuffer)。只要你分清是哪個對象作出的動作就可以了。好了,說到這里都是空洞的理論,那么現(xiàn)在就結(jié)合一下程序來分析一下。=發(fā)送方(本例是用服務(wù)端做發(fā)送方的)=procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);vars, sCommand: string;fStream : TFileStream; /定義一個文件流FFileName:string;aSize:in
15、teger;Buf : array0.1023 of Byte; /創(chuàng)建1M的內(nèi)存緩沖區(qū)ReadCount: integer;beginFFileName:='test.rar' ; /我們約定就發(fā)送的文件名是 test.rartrys := uppercase(AThread.Connection.ReadLn); /接受客戶端發(fā)過來的一個字符串sCommand := copy(s,1,3); /取這個字串的前3位if sCommand = 'GET' then /如果前3位字串是 “GET” 就發(fā)文件Beginmemo1.Lines.Add('接到指
16、令 GET ,開始相應(yīng)工作') ;fstream:=tfilestream.Create(ExtractFileDir(ParamStr(0) +''+FFileName,fmOpenRead+ fmShareDenyNone); /針對 test.rar創(chuàng)建文件流AThread.Connection.WriteLn(ExtractFileName(FFileName);AThread.Connection.ReadLn(#13#10, 1000) ;/這兩行作用是把文件名發(fā)送過去,ReadLn(#13#10, 1000) ;我目前也沒搞清是做什么用的,但是對方要用得到
17、的,不能去掉的aSize:= fstream.Size; /從文件流中獲取本地文件大小AThread.Connection.WriteBuffer(aSize, 4); /把這個大小數(shù)值傳過去while fstream.Position < fstream.Size do/用一個循環(huán)來設(shè)定將文件一塊一塊放到要傳輸?shù)臄?shù)據(jù)流中begin/以下這塊是判斷每次讀寫到從文件流里讀入緩存區(qū)的大小if fstream.Size - fstream.Position >= SizeOf(Buf) thenReadCount := sizeOf(Buf)else ReadCount := fstre
18、am.Size - fstream.Position;fstream.ReadBuffer(Buf, ReadCount); /從文件流中讀取數(shù)據(jù),放到緩存區(qū)AThread.Connection.WriteBuffer(Buf, ReadCount);/從緩存區(qū)把數(shù)據(jù)發(fā)到數(shù)據(jù)流中memo1.Lines.Add('寫文件 '+inttostr(ReadCount)+' 字節(jié) ') ;end;AThread.Connection.ReadLn; /這句作用不大,好像可以去掉的AThread.Connection.CloseWriteBuffer; /關(guān)閉傳輸數(shù)據(jù)流m
19、emo1.Lines.Add('關(guān)閉客戶端文件流可寫緩沖區(qū) ') ;/釋放資源FreeAndNil(fStream);memo1.Lines.Add('釋放資源 ') ;EndfinallyAThread.Connection.Disconnect;memo1.Lines.Add('斷開本次連接 ') ;end;end;=接受方(本例是用客戶端做接受方的)=客戶端 要發(fā)送一個約定好的指令給服務(wù)端,然后服務(wù)端給它傳輸文件test.rarprocedure TForm1.Button1Click(Sender: TObject);varaFileNa
20、me:string;ftmpStream : TFileStream; /定義一個文件流aFileSize:integer;ReadCount:integer;Buff: array0.1023 of Byte; /定義一個緩存區(qū)beginwith IdTCPClient1 dobegintryconnect; /連接服務(wù)端if connected thenbeginwriteln('GET'); /發(fā)送 GET指令memo1.Lines.Add('GET指令已經(jīng)發(fā)送');aFileName := IdTCPClient1.ReadLn(#13#10, 100)
21、 ; /讀取傳輸過來的文件名if aFileName = '' then Exit;/如果已經(jīng)存在這個文件,先刪掉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; /設(shè)置進(jìn)度條的顯示最大數(shù)字/創(chuàng)建文件流ftmpStream:= TFileStream.Create(ExtractFileDir(ParamStr(0) +''+aFileName, fmCreate);/用一個循環(huán),不停讀取數(shù)據(jù)流 然后利用文件流寫入文件中repeat/確定本次循環(huán)讀取的快數(shù)大小if aFileSize - ftmpStream.Size > SizeOf(Buff) thenReadCount :=
23、SizeOf(Buff)elseReadCount := aFileSize - ftmpStream.Size;IdTCPClient1.ReadBuffer(Buff, ReadCount); /將傳來的數(shù)據(jù)流讀入緩存區(qū)ftmpStream.WriteBuffer(Buff, ReadCount); /將緩存區(qū)的數(shù)據(jù)寫進(jìn)文件中memo1.Lines.Add('寫入文件 '+inttostr(ReadCount)+'字節(jié)。');ProgressBar1.Position := ftmpStream.Size; /改變進(jìn)度條的狀態(tài)Application.Proc
24、essMessages;until ftmpStream.Size >= aFileSize;memo1.Lines.Add('讀取連接流中數(shù)據(jù),寫入創(chuàng)建的文件流對象ftmpStream中。');disconnect;memo1.Lines.Add('斷開本次連接');end;finallyFreeAndNil(fTmpStream);memo1.Lines.Add('釋放文件流對象ftmpStream占用的資源。');end;end;end;好了 至此,整個流程以及核心代碼已經(jīng)介紹完了,不知道你是否有所收獲?附: 源碼客戶端: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指令已經(jīng)發(fā)送
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)+'字節(jié)。');
35、0; ProgressBar1.Position := ftmpStream.Size; Application.ProcessMessages; until ftmpStream.Size >= aFileSize;
36、; memo1.Lines.Add('讀取連接流中數(shù)據(jù),寫入創(chuàng)建的文件流對象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.服務(wù)端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 ,開始相應(yīng)工作') ; /fstream:=tfilestream.Create(Ext
44、ractFileDir(ParamStr(0) +'key.txt',fmOpenRead + fmShareDenyNone); /memo1.Lines.Add('創(chuàng)建文件流 ') ; /AThread.Connection.OpenWriteBuffer;
45、; /memo1.Lines.Add('打開客戶端文件流可寫緩沖區(qū) ') ; /改寫的程序 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)+' 字節(jié) ') ; end; AThread.Connection.ReadLn; AThread.Connection.CloseWriteBuffer; memo1.Lines.Add('關(guān)閉客戶端文件流可寫緩沖區(qū) ') ; FreeAndNil(fStream); &
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 人教版九年級上學(xué)期化學(xué)期末試卷帶答案
- 2024年私募股權(quán)投資合作協(xié)議書
- 2024年免疫分析儀器及試劑項目合作計劃書
- 2024年口腔醫(yī)療合作協(xié)議書
- 2024年新型有機(jī)酸開發(fā)與生產(chǎn)項目建議書
- 2024年LNG工藝包及裝置合作協(xié)議書
- 企業(yè)內(nèi)部審計工作規(guī)定
- 2024年井控設(shè)備裝置合作協(xié)議書
- 安徽省淮北市龍興中學(xué)2023-2024學(xué)年高一上學(xué)期期末試卷(無答案)
- 2023年醫(yī)用超聲儀器項目分析評估報告
- 人教精通版五年級英語上冊-名師教學(xué)課件-Lesson-14
- 普通高中班級心理委員工作手冊(附班級心理委員工作手冊)
- 2023學(xué)年完整公開課版《客家之歌》
- 22G101平法識圖考核試題及答案
- 制作香囊(說課稿)2022-2023學(xué)年綜合實踐活動四年級上冊 全國通用
- 陶瓷釉料配方600例
- 磁異常的解釋推斷
- 安全輸血護(hù)理質(zhì)量管理查檢表
- 中國詩歌發(fā)展史
- 分項工程報價單鋼筋工程
- 基因工程基因工程的工具酶
評論
0/150
提交評論