日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于完成端口的文件传输设计

發(fā)布時間:2025/3/14 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于完成端口的文件传输设计 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

完成端口

說到完成端口,我想很多人都不太陌生,下面是一段摘錄:
“完成端口”模型是迄今為止最為復雜的一種I/O模型。然而,假若一個應用程序同時需要管理為數(shù)眾多的套接字,那么采用這種模型,往往可以達到最佳的系統(tǒng)性能!但不幸的是,該模型只適用于Windows NT和Windows 2000操作系統(tǒng)。因其設計的復雜性,只有在你的應用程序需要同時管理數(shù)百乃至上千個套接字的時候,而且希望隨著系統(tǒng)內(nèi)安裝的CPU數(shù)量的增多,應用程序的性能也可以線性提升,才應考慮采用“完成端口”模型。要記住的一個基本準則是,假如要為Windows NT或Windows 2000開發(fā)高性能的服務器應用,同時希望為大量套接字I/O請求提供服務(Web服務器便是這方面的典型例子),那么I/O完成端口模型便是最佳選擇!(節(jié)選自《Windows網(wǎng)絡編程》第八章)

在.net中,一旦說到完成端口,我們就不得不提到SocketAsyncEventArgs這個高性能的類,其內(nèi)部是基于完成端口實現(xiàn)的,然后被微軟提供了諸多封裝,所以使用起來也比較簡單一些.由于這個類利用完成端口并且結(jié)合異步事件的方式進行設計的,所以我們可以大致的知道他的一些特點.

處理流程

關(guān)于此類,微軟的解釋很多,但是始終離不開高性能三個字.根據(jù)我的理解,如果利用此類設計一個服務端的消息傳送中心,那么其運作流程如下:

1.創(chuàng)建SocketAsyncEventArgs池,并且創(chuàng)建緩沖管理中心,負責對池中的對象分配緩沖大小.

2.創(chuàng)建服務器套接字,并處于監(jiān)聽狀態(tài).同時創(chuàng)建基于SocketAsyncEventArgs的客戶端接收對象,接收客戶端的連接.

3.如果有客戶端連接, 客戶端接收對象將會把控制權(quán)移交給數(shù)據(jù)接收對象,數(shù)據(jù)接收對象開始接收數(shù)據(jù).

這只是一個簡單的流程,從這里我們可以看出,服務端套接字只是負責監(jiān)聽,一旦有客戶端連接,就會把連接事件拋給接收對象;所以客戶端一個一個的來,服務端一個一個的拋,性能自然會好了不少.

說了這么多,我們來淺析一下SocketAsyncEventArgs對象中的一些基本的知識點.

緩沖

首先,說到緩沖,我們很容易理解為一個存儲池,里面可以放入東西,也可以拿走.在SocketAsyncEventArgs類中,我們可以利用其SetBuffer方法初始化緩沖區(qū).

比如說: receiveArgs.SetBuffer(new byte[10],0,5);他的意思就是為當前接收數(shù)據(jù)對象設置的緩沖區(qū)大小為10字節(jié),位置從0開始,并且允許接收的最大的數(shù)據(jù)長度為5字節(jié).

當數(shù)據(jù)被接收,然后寫入到緩沖區(qū)的時候,數(shù)據(jù)會按照預先設定好的緩沖規(guī)則進行放置.比如這里我輸入aaaaa,那么在緩沖區(qū)會放入如下數(shù)據(jù):

?

但是當有新的數(shù)據(jù)再進來的時候,比如這里我輸入了bbbb,那么緩沖區(qū)就變成了

看到了沒有,緩沖區(qū)中第五位依然是我們前一次插入的值,所以,在這里我提醒大家,緩沖區(qū)的東西取完之后,一定要重置一下,否則臟數(shù)據(jù)會導致數(shù)據(jù)錯誤.

講到這里,也許有人會說,假如我插入aaaaab會怎么樣,其實,這個長度已經(jīng)超過了緩沖區(qū)的長度,緩沖區(qū)將會做截斷處理,然后當作兩段放入緩存中,首先會是

,然后會是

?

所以如果這個時候你的數(shù)據(jù)沒有被及時取走的話,將會得到最終結(jié)果 

?這不,已經(jīng)發(fā)生粘包現(xiàn)象了.

?拋出方式

說完緩沖,這里我們需要說到的是服務端套接字是如何將接收的客戶端通過事件的方式拋給SocketAsyncEventArgs對象的.

在進行服務端,我們一般都會有一個用于監(jiān)聽的套接字,套接字會利用serverSocket.AcceptAsync(SocketAsyncEventArgs)異步方法注冊SocketAsyncEventArgs對象,然后一旦有客戶端連接,就會激發(fā)SocketAsyncEventArgs對象的Completed事件,然后,在這個事件中,serverSocket會通過ReceiveAsync(SocketAsyncEventArgs)異步方法將數(shù)據(jù)處理的過程交給另外一個專門負責數(shù)據(jù)處理的對象去完成,這樣,客戶端連接,服務端只負責將連接事件拋給SocketAsyncEventArgs對象即可,比起傳統(tǒng)的編程方式: 服務端既負責監(jiān)聽,又負責接收, 效率極大的提升.

同步方式

最后說到的是同步問題,因為在異步交互的系統(tǒng)中,同步問題確實很重要,尤其是當客戶端和服務器同步的時候.

客戶端里面的線程同步,我們可以利用AutoResetEvent來實現(xiàn),客戶端和服務端同步的時候,我們需要AutoResetEvent并且結(jié)合一些標記信息來進行,也就是客戶端往服務器發(fā)送的一些小標記,比如1代表可以進行,0代表取消等等.

在設計的時候,我們還需要記住的是,代碼的核心只是負責數(shù)據(jù)的傳輸,不要加過多的邏輯判斷在里面,否則會影響性能,邏輯判斷等最好移到界面處理處來進行.

代碼精解

好了,下面上代碼來解釋解釋.

首先我們先從服務端開始:

View Code private int listenClientCount;private int poolSize;private int bufferSize;private EndPoint endPoint;private Socket serverSocket;private SocketAsyncEventArgsPool readWritePool;private BufferManager bufferManager;private SocketAsyncEventArgs acceptArgs;private SocketAsyncEventArgs receiveArgs;private SocketAsyncEventArgs sendArgs;public Action<Socket> OnStarted;public Action<Socket> OnConnected;public Action<SocketAsyncEventArgs> OnReceive;public Action<SocketAsyncEventArgs> OnSent;public Action<SocketAsyncEventArgs> OnDisconnect;public Action OnStopped;

在服務端,我們會定義服務端套接字,用于連接對象的acceptArgs,用于接收數(shù)據(jù)的receiveArgs以及用于拋出事件的一些Action等等,這些Action讓外部引用進行注冊并進行操縱.

然后這里是Start函數(shù),

View Code public void Start(){if (this.serverSocket != null)throw new Exception("Server is running, can't init another one.");this.serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);try{this.serverSocket.Bind(endPoint);this.serverSocket.Listen(listenClientCount);}catch (ArgumentNullException ex){throw new Exception(ex.Message);}catch (SocketException ex){throw new Exception(ex.Message);}if (OnStarted != null)OnStarted(serverSocket);StartAccept(null);}

這個函數(shù)的作用就是開啟監(jiān)聽,并且將接收事件傳遞給acceptArgs對象.
然后,當客戶端連接過來的時候,服務端套接字開始將控制權(quán)傳遞給receiveArgs對象:

View Code private void ProcessAccept(SocketAsyncEventArgs e){if (OnConnected != null){OnConnected(e.AcceptSocket);}receiveArgs = this.readWritePool.Pop();this.bufferManager.SetBuffer(receiveArgs);receiveArgs.AcceptSocket = e.AcceptSocket;receiveArgs.Completed += IO_Completed;if (!e.AcceptSocket.ReceiveAsync(receiveArgs)){ProcessReceive(receiveArgs);}StartAccept(e);}

注意,這里有一個IO_Completed事件,表示一旦當前數(shù)據(jù)的數(shù)據(jù)接收完成,receiveArgs對象將會觸發(fā)該事件,用于進行下一步操作,一般會取lastOperation來判斷是否繼續(xù)接收.
最后需要說的就是真正的數(shù)據(jù)接收函數(shù):

View Code private void ProcessReceive(SocketAsyncEventArgs e){if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success){if (this.OnReceive != null)this.OnReceive(e);if (!e.AcceptSocket.ReceiveAsync(e)){this.ProcessReceive(e);}}else{CloseClient(e);}}

這個函數(shù)利用了循環(huán)的方式來接收數(shù)據(jù),以便保證所有的數(shù)據(jù)都能夠進來,直至數(shù)據(jù)接收完成.在這個接收函數(shù)中,不應該有任何的邏輯判斷,否則會比較影響吞吐性能.

測試結(jié)果

好了,下面就看下界面截圖吧(請注意,這里我得客戶端是在上海,服務器端在香港,真實模擬數(shù)據(jù)傳輸情況).

(圖1,這是服務端開啟時候的情景)

(圖2,這是客戶端開啟時候的情景)

(圖3,客戶端準備發(fā)送文件給服務器端的情景,請注意這里的同步方式,客戶端會等待服務端準備完成,才會開始傳送數(shù)據(jù))

(圖4,數(shù)據(jù)傳送中,請看,左邊是客戶端的緩沖區(qū)的緩沖數(shù)據(jù),右邊是服務器端的接收緩沖區(qū)的緩沖數(shù)據(jù))

(圖5,數(shù)據(jù)傳輸完畢的情景)

(圖6,請注意,即使我們服務端設置的接收緩沖區(qū)為327670,長度為32767,但是有時候數(shù)據(jù)還是不會完整的到達的,遇到這種情況,我們只需等待下一批數(shù)據(jù)到達即可.)

源碼下載

點擊這里下載

轉(zhuǎn)載于:https://www.cnblogs.com/scy251147/archive/2013/05/09/3069720.html

總結(jié)

以上是生活随笔為你收集整理的基于完成端口的文件传输设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。