使用基本類庫(kù)
為了更好地理解C#與C++的區(qū)別和解決問(wèn)題方式的變化,我們先來(lái)看一個(gè)比較簡(jiǎn)單的例子。我們將創(chuàng)建一個(gè)讀取文本文件的類,并在屏幕上顯示其內(nèi)容。我將把它做成多線程程序,以便在從磁盤(pán)上讀取數(shù)據(jù)時(shí)還可以做其他的工作。
在C++中,我們可能會(huì)創(chuàng)建一個(gè)讀文件的線程和另一個(gè)做其他工作的線程,這二個(gè)線程將各自獨(dú)立地運(yùn)行,但可能會(huì)需要對(duì)它們進(jìn)行同步。在C#中,我們也可以完成同樣的工作,由于.NET框架提供了功能強(qiáng)大的異步I/O機(jī)制,在編寫(xiě)線程時(shí),我們會(huì)節(jié)省不少的時(shí)間。
異步I/O支持是內(nèi)置在CLR中的,而且?guī)缀跖c使用正常的I/O流類一樣簡(jiǎn)單。在程序的開(kāi)始,我們首先通知編譯器,我們將在程序中使用許多名字空間中的對(duì)象:
usingSystem; usingSystem.IO; usingSystem.Text;
在程序中包含System,并不會(huì)自動(dòng)地包含其所有的子名字空間,必須使用using關(guān)健字明確地包含每個(gè)子名字空間。我們?cè)诶又袝?huì)用到I/O流類,因此需要包含System.IO名字空間,我們還需要System.Text名字空間支持字節(jié)流的ASCII編碼。
由于.NET架構(gòu)為完成了大部分的工作,編寫(xiě)這一程序所需的步驟相當(dāng)簡(jiǎn)單。我們將用到Stream類的BeginRead方法,它提供異步I/O功能,將數(shù)據(jù)讀入到一個(gè)緩沖區(qū)中,當(dāng)緩沖區(qū)可以處理時(shí)調(diào)用相應(yīng)的處理程序。
我們需要使用一個(gè)字節(jié)數(shù)組作為緩沖區(qū)和回叫方法的代理,并將這二者定義為驅(qū)動(dòng)程序類的private成員變量。
publicclassAsynchIOTester { privateStreaminputStream; privatebyte[]buffer; privateAsyncCallbackmyCallBack;
inputStream是一個(gè)Stream類型的變量,我們將對(duì)它調(diào)用BeginRead方法。代理與成員函數(shù)的指針?lè)浅O嗨啤4硎荂#的第一類元素。
當(dāng)緩沖區(qū)被磁盤(pán)上的文件填滿時(shí),.NET將調(diào)用被代理的方法對(duì)數(shù)據(jù)進(jìn)行處理。在等待讀取數(shù)據(jù)期間,我們可以讓計(jì)算機(jī)完成其他的工作。(在本例中是將1個(gè)整型變量由1增加到50000,但在實(shí)際的應(yīng)用程序中,我們可以讓計(jì)算機(jī)與用戶進(jìn)行交互或作其他有意義的工作。)
本例中的代理被定義為AsyncCallback類型的過(guò)程,這是Stream的BeginRead方法所需要的。System空間中AsyncCallback類型代理的定義如下所示:
publicdelegatevoidAsyncCallback(IAsyncResultar);
這一代理可以是與任何返回void類型值、將IAsyncResult界面作為參數(shù)的方法相關(guān)聯(lián)的。在該方法被調(diào)用時(shí),CLR可以在運(yùn)行時(shí)傳遞IAsyncResult界面對(duì)象作為參數(shù)。我們需要如下所示的形式定義該方法:
voidOnCompletedRead(IAsyncResultasyncResult)
然后在構(gòu)造器中與代理連接起來(lái):
AsynchIOTester() { ??? myCallBack=newAsyncCallback(this.OnCompletedRead); }
上面的代碼將代理的實(shí)例賦給成員變量myCallback。下面是全部程序的詳細(xì)工作原理。在Main函數(shù)中,創(chuàng)建了一個(gè)類的實(shí)例,并讓它開(kāi)始運(yùn)行:
publicstaticvoidMain() { AsynchIOTestertheApp=newAsynchIOTester(); theApp.Run(); }
new關(guān)健字能夠啟動(dòng)構(gòu)造器。在構(gòu)造器中我們打開(kāi)一個(gè)文件,并得到一個(gè)Stream對(duì)象。然后在緩沖中分配空間并與回調(diào)機(jī)制聯(lián)結(jié)起來(lái)。
AsynchIOTester() { inputStream=File.OpenRead(@"C:\MSDN\fromCppToCS.txt"); buffer=newbyte[BUFFER_SIZE]; myCallBack=newAsyncCallback(this.OnCompletedRead); }
在Run方法中,我們調(diào)用了BeginRead,它將以異步的方式讀取文件。
inputStream.BeginRead( buffer,//存放結(jié)果 0,//偏移量 buffer.Length,//緩沖區(qū)中有多少字節(jié) myCallBack,//回調(diào)代理 null);//本地對(duì)象
這時(shí),我們可以完成其他的工作。
for(longi=0;i<50000;i++) { if(i%1000==0) { Console.WriteLine("i:{0}",i); } }
文件讀取操作結(jié)束后,CLR將調(diào)用回調(diào)方法。
voidOnCompletedRead(IAsyncResultasyncResult) {
在OnCompletedRead中要做的第一件事就是通過(guò)調(diào)用Stream對(duì)象的EndRead方法找出讀取了多少字節(jié):
intbytesRead=inputStream.EndRead(asyncResult);
對(duì)EndRead的調(diào)用將返回讀取的字節(jié)數(shù)。如果返回的數(shù)字比0大,則將緩沖區(qū)轉(zhuǎn)換為一個(gè)字符串,然后將它寫(xiě)到控制臺(tái)上,然后再次調(diào)用BeginRead,開(kāi)始另一次異步讀的過(guò)程。
if(bytesRead>0) { Strings=Encoding.ASCII.GetString(buffer,0,bytesRead); Console.WriteLine(s); inputStream.BeginRead(buffer,0,buffer.Length, myCallBack,null); }
現(xiàn)在,在讀取文件的過(guò)程中就可以作別的工作了(在本例中是從1數(shù)到50000),但我們可以在每次緩沖區(qū)滿了時(shí)對(duì)讀取的數(shù)據(jù)進(jìn)行處理(在本例中是向控制臺(tái)輸出緩沖區(qū)中的數(shù)據(jù))。有興趣的讀者可以點(diǎn)擊此處下載完整的源代碼。
異步I/O的管理完全是由CLR提供的,這樣,在網(wǎng)絡(luò)上讀取文件時(shí),會(huì)更好些。
在網(wǎng)絡(luò)上讀取文件
在C++中,在網(wǎng)絡(luò)上讀取文件需要有相當(dāng)?shù)木幊碳记桑?NET對(duì)此提供了廣泛的支持。事實(shí)上,在網(wǎng)絡(luò)上讀取文件僅僅是基礎(chǔ)類庫(kù)中Stream類的另一種應(yīng)用。
首先,為了對(duì)TCP/IP端口(在本例中是65000)進(jìn)行監(jiān)聽(tīng),我們需要?jiǎng)?chuàng)建一個(gè)TCPListener類的實(shí)例。
TCPListenertcpListener=newTCPListener(65000);
一旦創(chuàng)建后,就讓它開(kāi)始進(jìn)行監(jiān)聽(tīng)。
tcpListener.Start();
現(xiàn)在就要等待客戶連接的要求了。
SocketsocketForClient=tcpListener.Accept();
TCPListener對(duì)象的Accept方法返回一個(gè)Socket對(duì)象,Accept是一個(gè)同步的方法,除非接收到一個(gè)連接請(qǐng)求它才會(huì)返回。如果連接成功,就可以開(kāi)始向客戶發(fā)送文件了。
if(socketForClient.Connected) { ???
接下來(lái),我們需要?jiǎng)?chuàng)建一個(gè)NetworkStream類,將報(bào)路傳遞給constructor:
NetworkStreamnetworkStream=newNetworkStream(socketForClient);
然后創(chuàng)建一個(gè)StreamWriter對(duì)象,只是這次不是在文件上而是在剛才創(chuàng)建的NetworkStream類上創(chuàng)建該對(duì)象:
System.IO.StreamWriterstreamWriter= newSystem.IO.StreamWriter(networkStream);
當(dāng)向該流寫(xiě)內(nèi)容時(shí),流就通過(guò)網(wǎng)絡(luò)被傳輸給客戶端。
客戶端的創(chuàng)建
客戶端軟件就是一個(gè)TCPClient類的具體例子,TCPClient類代表連向主機(jī)的一個(gè)TCP/IP連接。
TCPClientsocketForServer; socketForServer=newTCPClient("localHost",65000);
有了TCPClient對(duì)象后,我們就可以創(chuàng)建NetworkStream對(duì)象了,然后在其上創(chuàng)建StreamReader類:
NetworkStreamnetworkStream=socketForServer.GetStream(); System.IO.StreamReaderstreamReader= newSystem.IO.StreamReader(networkStream);
現(xiàn)在,只要其中有數(shù)據(jù)就讀取該流,并將結(jié)果輸出到控制臺(tái)上。
do { outputString=streamReader.ReadLine();
if(outputString!=null) { Console.WriteLine(outputString); } } while(outputString!=null);
為了對(duì)這一段代碼進(jìn)行測(cè)試,可以創(chuàng)建如下一個(gè)測(cè)試用的文件:
Thisislineone Thisislinetwo Thisislinethree Thisislinefour
這是來(lái)自服務(wù)器的輸出:
Output(Server) Clientconnected SendingThisislineone SendingThisislinetwo SendingThisislinethree SendingThisislinefour Disconnectingfromclient... Exiting...
下面是來(lái)自客戶端的輸出:
Thisislineone Thisislinetwo Thisislinethree Thisislinefour
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!