詳解通過.NET緩存提高TCP傳輸速度
在這里我們將介紹的是通過.NET緩存提高TCP傳輸速度,這也是提高網絡性能的方法,希望對大家有所幫助。
.NET提供了一個NetworkStream 用于TCP 的讀寫,實際使用時發現直接操作效率很低,哪怕把TCP 的發送緩存和接受緩存設置很大也沒有太大提高。后來在對 NetworkStream 讀寫前設置了緩存,性能一下子提高了很多。
從實際測試結果看設置自己的寫緩存,對性能的提升最為顯著。我分析了一下,其原因很可能是在向NetworkStream 序列化對象時,序列化程序調用了大量的Write 方法向NetworkStream寫入數據,每次向NetworkStream寫入數據,數據被首先寫入TCP的發送緩存,并且由調用線程設置一個信號通知.Net framework 內部的TCP線程發送緩沖區中已經有數據,TCP線程被激活并讀取發送緩沖區中的數據,組包并向網卡寫入數據。頻繁的調用 NetworkStream.Write 寫入小塊數據將導致調用線程和TCP線程反復切換,并大量觸發網卡中斷,導致發送效率低下。如果我們在發送前將數據緩存并按較大的數據塊發送給TCP線程,則大大減少線程切換和網卡中斷數量,從而大大提高傳輸效率。
問題到這里還沒有結束,我們發送的對象往往較大,如果我們將發送對象全部序列化到buffer中再發送,那么勢必占用大量內存,實際上我們無法忍受這種對內存無限制申請的行為,試想一個1G大小的對象,我們在發送前為它另外再開辟1個G的內存來緩存,對于系統來說簡直是無法忍受。由于我們用.net 發送數據,我們在發送時需要將對象序列化到流中,而不能像 C/C++那樣直接通過指針來讀取數據(當然你也可以用unsafe代碼,但這種方式會帶來其他問題,而且并不為大家所推薦),所以我們需要開發一個專門用 TCP 發送緩存的流來處理讀寫前的緩存。為此我開發了一個 TcpCacheStream 類,這個類被用在讀寫 NetworkStream 前先進行緩存。
調用方法很簡單
發送過程
- object msg;
- //初始化 msg 過程省略
- System.Net.Sockets.NetworkStream _ClientStream;
- //初始化 _ClientStream 過程省略
- //創建TcpCacheStream
- TcpCacheStream tcpStream = new TcpCacheStream(_ClientStream);
- //二進制序列化 msg 對象到 TcpCacheStream
- IFormatter formatter = new BinaryFormatter();
- formatter.Serialize(tcpStream, msg);
- //將緩存中最后一包的數據發送出去
- tcpStream.Flush();
接收過程
- System.Net.Sockets.NetworkStream _ClientStream;
- //初始化 _ClientStream 過程省略
- //創建TcpCacheStream
- TcpCacheStream tcpStream = new TcpCacheStream(_ClientStream);
- //從 TcpCacheStream 二進制反序列化
- IFormatter formatter = new BinaryFormatter();
- objcet result = formatter.Deserialize(tcpStream);
TcpCacheStream 類為調用者封裝了緩存的過程,這個緩存過程實際并不復雜,發送時數據先寫入TcpCacheStream的buf中,當buf滿后才向NetworkStream 寫入數據,否則只緩存。由于最后一包不能保證正好填滿buf,我們在寫入數據后一定要調用 Flush 方法,將所有數據都發送出去。接收的過程反過來,如果buf中沒有數據,就先將數據讀入到buf中,然后再COPY給調用者,如果已經有數據則直接COPY給調用者。
TcpCacheStream 的代碼如下:
- [Serializable]
- public class TcpCacheStream : Stream
- {
- #region Private fields
- const int BUF_SIZE = 4096;
- private byte[] _Buf = new byte[BUF_SIZE];
- private MemoryStream _CacheStream = new MemoryStream(BUF_SIZE);
- private NetworkStream _NetworkStream;
- private int _BufLen = 0;
- #endregion
- #region Private properties
- private MemoryStream CacheStream
- {
- get
- {
- return _CacheStream;
- }
- }
- #endregion
- #region Public properties
- ///
- /// get or set the Network Stream
- ///
- public NetworkStream NetworkStream
- {
- get
- {
- return _NetworkStream;
- }
- }
- #endregion
- public TcpCacheStream(NetworkStream networkStream)
- {
- _NetworkStream = networkStream;
- }
- #region Implement stream class
- public override bool CanRead
- {
- get
- {
- return true;
- }
- }
- public override bool CanSeek
- {
- get
- {
- return false;
- }
- }
- public override bool CanWrite
- {
- get
- {
- return true;
- }
- }
- public override void Flush()
- {
- NetworkStream.Write(_Buf, 0, _BufLen);
- NetworkStream.Flush();
- }
- public override long Length
- {
- get
- {
- throw new Exception("This stream can not seek!");
- }
- }
- public override long Position
- {
- get
- {
- throw new Exception("This stream can not seek!");
- }
- set
- {
- throw new Exception("This stream can not seek!");
- }
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- int len = 0;
- //If cache is not empty, read from cache
- if (CacheStream.Length > CacheStream.Position)
- {
- len = CacheStream.Read(buffer, offset, count);
- return len;
- }
- //Fill cache
- len = NetworkStream.Read(_Buf, 0, BUF_SIZE);
- if (len == 0)
- {
- return 0;
- }
- CacheStream.Position = 0;
- CacheStream.Write(_Buf, 0, len);
- CacheStream.SetLength(len);
- CacheStream.Position = 0;
- len = CacheStream.Read(buffer, offset, count);
- return len;
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new Exception("This stream can not seek!");
- }
- public override void SetLength(long value)
- {
- throw new Exception("This stream can not seek!");
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- if (offset + count > buffer.Length)
- {
- throw new ArgumentException("Count + offset large then buffer.Length");
- }
- int remain = count - (BUF_SIZE - _BufLen);
- if (remain < 0)
- {
- Array.Copy(buffer, offset, _Buf, _BufLen, count);
- _BufLen = BUF_SIZE + remain;
- }
- else
- {
- Array.Copy(buffer, offset, _Buf, _BufLen, BUF_SIZE - _BufLen);
- NetworkStream.Write(_Buf, 0, _Buf.Length);
- Array.Copy(buffer, offset + BUF_SIZE - _BufLen, _Buf, 0, remain);
- _BufLen = remain;
- }
- }
- #endregion
- }
原文標題:.Net 下通過緩存提高TCP傳輸速度
鏈接:http://www.cnblogs.com/eaglet/archive/2009/11/04/1595887.html
【責任編輯:彭凡 TEL:(010)68476606】