使用基本類庫 -- zt 統一教學網
2024-07-21 02:28:00
供稿:網友
使用基本類庫
為了更好地理解c#與c++的區別和解決問題方式的變化,我們先來看一個比較簡單的例子。我們將創建一個讀取文本文件的類,并在屏幕上顯示其內容。我將把它做成多線程程序,以便在從磁盤上讀取數據時還可以做其他的工作。
在c++中,我們可能會創建一個讀文件的線程和另一個做其他工作的線程,這二個線程將各自獨立地運行,但可能會需要對它們進行同步。在c#中,我們也可以完成同樣的工作,由于.net框架提供了功能強大的異步i/o機制,在編寫線程時,我們會節省不少的時間。
異步i/o支持是內置在clr中的,而且幾乎與使用正常的i/o流類一樣簡單。在程序的開始,我們首先通知編譯器,我們將在程序中使用許多名字空間中的對象:
usingsystem;
usingsystem.io;
usingsystem.text;
在程序中包含system,并不會自動地包含其所有的子名字空間,必須使用using關健字明確地包含每個子名字空間。我們在例子中會用到i/o流類,因此需要包含system.io名字空間,我們還需要system.text名字空間支持字節流的ascii編碼。
由于.net架構為完成了大部分的工作,編寫這一程序所需的步驟相當簡單。我們將用到stream類的beginread方法,它提供異步i/o功能,將數據讀入到一個緩沖區中,當緩沖區可以處理時調用相應的處理程序。
我們需要使用一個字節數組作為緩沖區和回叫方法的代理,并將這二者定義為驅動程序類的private成員變量。
publicclassasynchiotester
{
privatestreaminputstream;
privatebyte[]buffer;
privateasynccallbackmycallback;
inputstream是一個stream類型的變量,我們將對它調用beginread方法。代理與成員函數的指針非常相似。代理是c#的第一類元素。
當緩沖區被磁盤上的文件填滿時,.net將調用被代理的方法對數據進行處理。在等待讀取數據期間,我們可以讓計算機完成其他的工作。(在本例中是將1個整型變量由1增加到50000,但在實際的應用程序中,我們可以讓計算機與用戶進行交互或作其他有意義的工作。)
本例中的代理被定義為asynccallback類型的過程,這是stream的beginread方法所需要的。system空間中asynccallback類型代理的定義如下所示:
publicdelegatevoidasynccallback(iasyncresultar);
這一代理可以是與任何返回void類型值、將iasyncresult界面作為參數的方法相關聯的。在該方法被調用時,clr可以在運行時傳遞iasyncresult界面對象作為參數。我們需要如下所示的形式定義該方法:
voidoncompletedread(iasyncresultasyncresult)
然后在構造器中與代理連接起來:
asynchiotester()
{
???
mycallback=newasynccallback(this.oncompletedread);
}
上面的代碼將代理的實例賦給成員變量mycallback。下面是全部程序的詳細工作原理。在main函數中,創建了一個類的實例,并讓它開始運行:
publicstaticvoidmain()
{
asynchiotestertheapp=newasynchiotester();
theapp.run();
}
new關健字能夠啟動構造器。在構造器中我們打開一個文件,并得到一個stream對象。然后在緩沖中分配空間并與回調機制聯結起來。
asynchiotester()
{
inputstream=file.openread(@"c:/msdn/fromcpptocs.txt");
buffer=newbyte[buffer_size];
mycallback=newasynccallback(this.oncompletedread);
}
在run方法中,我們調用了beginread,它將以異步的方式讀取文件。
inputstream.beginread(
buffer,//存放結果
0,//偏移量
buffer.length,//緩沖區中有多少字節
mycallback,//回調代理
null);//本地對象
這時,我們可以完成其他的工作。
for(longi=0;i<50000;i++)
{
if(i%1000==0)
{
console.writeline("i:{0}",i);
}
}
文件讀取操作結束后,clr將調用回調方法。
voidoncompletedread(iasyncresultasyncresult)
{
在oncompletedread中要做的第一件事就是通過調用stream對象的endread方法找出讀取了多少字節:
intbytesread=inputstream.endread(asyncresult);
對endread的調用將返回讀取的字節數。如果返回的數字比0大,則將緩沖區轉換為一個字符串,然后將它寫到控制臺上,然后再次調用beginread,開始另一次異步讀的過程。
if(bytesread>0)
{
strings=encoding.ascii.getstring(buffer,0,bytesread);
console.writeline(s);
inputstream.beginread(buffer,0,buffer.length,
mycallback,null);
}
現在,在讀取文件的過程中就可以作別的工作了(在本例中是從1數到50000),但我們可以在每次緩沖區滿了時對讀取的數據進行處理(在本例中是向控制臺輸出緩沖區中的數據)。有興趣的讀者可以點擊此處下載完整的源代碼。
異步i/o的管理完全是由clr提供的,這樣,在網絡上讀取文件時,會更好些。
在網絡上讀取文件
在c++中,在網絡上讀取文件需要有相當的編程技巧,.net對此提供了廣泛的支持。事實上,在網絡上讀取文件僅僅是基礎類庫中stream類的另一種應用。
首先,為了對tcp/ip端口(在本例中是65000)進行監聽,我們需要創建一個tcplistener類的實例。
tcplistenertcplistener=newtcplistener(65000);
一旦創建后,就讓它開始進行監聽。
tcplistener.start();
現在就要等待客戶連接的要求了。
socketsocketforclient=tcplistener.accept();
tcplistener對象的accept方法返回一個socket對象,accept是一個同步的方法,除非接收到一個連接請求它才會返回。如果連接成功,就可以開始向客戶發送文件了。
if(socketforclient.connected)
{
???
接下來,我們需要創建一個networkstream類,將報路傳遞給constructor:
networkstreamnetworkstream=newnetworkstream(socketforclient);
然后創建一個streamwriter對象,只是這次不是在文件上而是在剛才創建的networkstream類上創建該對象:
system.io.streamwriterstreamwriter=
newsystem.io.streamwriter(networkstream);
當向該流寫內容時,流就通過網絡被傳輸給客戶端。
客戶端的創建
客戶端軟件就是一個tcpclient類的具體例子,tcpclient類代表連向主機的一個tcp/ip連接。
tcpclientsocketforserver;
socketforserver=newtcpclient("localhost",65000);
有了tcpclient對象后,我們就可以創建networkstream對象了,然后在其上創建streamreader類:
networkstreamnetworkstream=socketforserver.getstream();
system.io.streamreaderstreamreader=
newsystem.io.streamreader(networkstream);
現在,只要其中有數據就讀取該流,并將結果輸出到控制臺上。
do
{
outputstring=streamreader.readline();
if(outputstring!=null)
{
console.writeline(outputstring);
}
}
while(outputstring!=null);
為了對這一段代碼進行測試,可以創建如下一個測試用的文件:
thisislineone
thisislinetwo
thisislinethree
thisislinefour
這是來自服務器的輸出:
output(server)
clientconnected
sendingthisislineone
sendingthisislinetwo
sendingthisislinethree
sendingthisislinefour
disconnectingfromclient...
exiting...
下面是來自客戶端的輸出:
thisislineone
thisislinetwo
thisislinethree
thisislinefour