日韩欧美人妻无码精品白浆,www.大香蕉久久网,狠狠的日狠狠的操,日本好好热在线观看

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#開發(fā)輕量級(jí) 高性能HTTP服務(wù)器

admin
2025年7月2日 15:37 本文熱度 88

前言 


http協(xié)議是互聯(lián)網(wǎng)上使用最廣泛的通訊協(xié)議了。Web通訊也是基于http協(xié)議;對(duì)應(yīng)c#開發(fā)者來說ASP.NET Core是最新的開發(fā)Web應(yīng)用平臺(tái)。


由于最近要開發(fā)一套人臉識(shí)別系統(tǒng),對(duì)通訊效率的要求很高。雖然.NET Core對(duì)http處理很優(yōu)化了,但是我決定開發(fā)一個(gè)輕量級(jí)http服務(wù)器;不求功能多強(qiáng)大,只求能滿足需求,性能優(yōu)越。本文以c#開發(fā)windows下http服務(wù)器為例。


經(jīng)過多年的完善、優(yōu)化,我積累了一個(gè)非常高效的網(wǎng)絡(luò)庫《.NET中高性能、高可用性Socket通訊庫》以此庫為基礎(chǔ),開發(fā)一套輕量級(jí)的http服務(wù)器難度并不大。花了兩天的時(shí)間完成http服務(wù)器開發(fā),并做了測(cè)試。


同時(shí)與ASP.NET Core處理效率做了對(duì)比,結(jié)果出乎意料。我的服務(wù)器性能是ASP.NET Core的10倍。對(duì)于此結(jié)果一開始我也是不相信,經(jīng)過多次反復(fù)測(cè)試,事實(shí)卻是如此。此結(jié)果并不能說明我寫的服務(wù)器優(yōu)于ASP.NET Core,只是說明一個(gè)道理:合適的就是最好,高大上的東西并不是最好的。


1、HTTP協(xié)議特點(diǎn)




HTTP協(xié)議是基于TCP/IP之上的文本交換協(xié)議。對(duì)于開發(fā)者而言,也屬于socket通訊處理范疇。只是http協(xié)議是請(qǐng)求應(yīng)答模式,一次請(qǐng)求處理完成,則立即斷開。http這種特點(diǎn)對(duì)sokcet通訊提出幾個(gè)要求:


a)、能迅速接受TCP連接請(qǐng)求。TCP是面向連接的,在建立連接時(shí),需要三次握手。這就要求socket處理accept事件要迅速,要能短時(shí)間處理大量連接請(qǐng)求。


b)、服務(wù)端必須采用異步通訊模式。對(duì)windows而言,底層通訊就要采取IOCP,這樣才能應(yīng)付成千上萬的socket請(qǐng)求。


c)、快速的處理讀取數(shù)據(jù)。tcp是流傳輸協(xié)議,而http傳輸?shù)氖俏谋緟f(xié)議;客戶端向服務(wù)端發(fā)送的數(shù)據(jù),服務(wù)端可能需要讀取多次,服務(wù)端需要快速判斷數(shù)據(jù)是否讀取完畢。


以上幾點(diǎn)只是處理http必須要考慮的問題,如果需要進(jìn)一步優(yōu)化,必須根據(jù)自身的業(yè)務(wù)特點(diǎn)來處理。


2、快速接受客戶端的連接請(qǐng)求


采用異步Accept接受客戶端請(qǐng)求。這樣的好處是:可以同時(shí)投遞多個(gè)連接請(qǐng)求。當(dāng)有大量客戶端請(qǐng)求時(shí),能快速建立連接。


異步連接請(qǐng)求代碼如下:


public bool StartAccept()
{
   SocketAsyncEventArgs acceptEventArgs = new SocketAsyncEventArgs();
   acceptEventArgs.Completed += AcceptEventArg_Completed;
   bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArgs);
   Interlocked.Increment(ref _acceptAsyncCount);
   if (!willRaiseEvent)
   {
       Interlocked.Decrement(ref _acceptAsyncCount);
       _acceptEvent.Set();
       acceptEventArgs.Completed -= AcceptEventArg_Completed;
       ProcessAccept(acceptEventArgs);
   }
   return true;
}


可以設(shè)置同時(shí)投遞的個(gè)數(shù),比如此值為10。當(dāng)異步連接投遞個(gè)數(shù)小于10時(shí),立馬再次增加投遞。有一個(gè)線程專門負(fù)責(zé)投遞。


_acceptAsyncCount記錄當(dāng)前正在投遞的個(gè)數(shù),MaxAcceptInPool表示同時(shí)投遞的個(gè)數(shù);一旦_acceptAsyncCount小于MaxAcceptInPool,立即增加一次投遞。

private void DealNewAccept()
{
   try
   {
       if (_acceptAsyncCount <= MaxAcceptInPool)
       {
           StartAccept();
       }          
   }
   catch (Exception ex)
   {
       _log.LogException(0, "DealNewAccept 異常", ex);
   }
}


3、快速分析從客戶端收到的數(shù)據(jù)


比如客戶端發(fā)送1M數(shù)據(jù)到服務(wù)端,服務(wù)端收到1M數(shù)據(jù),需要讀取的次數(shù)是不確定的。怎么樣才能知道數(shù)據(jù)是否讀取完?


這個(gè)細(xì)節(jié)處理不好,會(huì)嚴(yán)重影響服務(wù)器的性能。畢竟服務(wù)器要對(duì)大量這樣的數(shù)據(jù)進(jìn)行分析。


http包頭舉例


POST / HTTP/1.1
Accept: */*
Content-Type: application/x-www-from-urlencoded
Host: www.163.com
Content-Length: 7
Connection: Keep-Alive
body


分析讀取數(shù)據(jù),常規(guī)、直觀的處理方式如下:


1) 、將收到的多個(gè)buffer合并成一個(gè)buffer。如果讀取10次才完成,則需要合并9次。


2) 、將buffer數(shù)據(jù)轉(zhuǎn)成文本。


3) 、找到文本中的http包頭結(jié)束標(biāo)識(shí)("\r\n\r\n") 。


4) 、找到Content-Length,根據(jù)此值判斷是否接收完成。


采用上述處理方法,將嚴(yán)重影響處理性能。必須另辟蹊徑,采用更優(yōu)化的處理方法。


優(yōu)化后的處理思路


1、多緩沖處理


基本思路是:收到所有的buffer之前,不進(jìn)行buffer合并。將緩沖存放在List<byte[]> listBuffer中。通過遍歷listBuffer來查找http包頭結(jié)束標(biāo)識(shí),來判斷是否接收完成。


類BufferManage負(fù)責(zé)管理buffer。


public class BufferManage
{
   List<byte[]> _listBuffer = new List<byte[]>();
   public void AddBuffer(byte[] buffer)
   
{
       _listBuffer.Add(buffer);
   }
   public bool FindBuffer(byte[] destBuffer, out int index)
   
{
       index = -1;
       int flagIndex = 0;
       int count = 0;
       foreach (byte[] buffer in _listBuffer)
       {
           foreach (byte ch in buffer)
           {
               count++;
               if (ch == destBuffer[flagIndex])
               {
                   flagIndex++;
               }
               else
               {
                   flagIndex = 0;
               }

               if (flagIndex >= destBuffer.Length)
               {
                   index = count;
                   return true;
               }
           }
       }
       return false;
   }
   public int TotalByteLength
   {
       get
       {
           int count = 0;
           foreach (byte[] item in _listBuffer)
           {
               count += item.Length;
           }
           return count;
       }
   }

   public byte[] GetAllByte()
   
{
       if (_listBuffer.Count == 0)
           return new byte[0];
       if (_listBuffer.Count == 1)
           return _listBuffer[0];

       int byteLen = 0;
       _listBuffer.ForEach(o => byteLen += o.Length);
       byte[] result = new byte[byteLen];

       int index = 0;
       foreach (byte[] item in _listBuffer)
       {
           Buffer.BlockCopy(item, 0, result, index, item.Length);
           index += item.Length;
       }
       return result;
   }
   public byte[] GetSubBuffer(int start, int countTotal)
   
{
       if (countTotal == 0)
           return new byte[0];
       byte[] result = new byte[countTotal];
       int countCopyed = 0;
       int indexOfBufferPool = 0;
       foreach (byte[] buffer in _listBuffer)
       {
           //找到起始復(fù)制點(diǎn)
           int indexOfItem = 0;
           if (indexOfBufferPool < start)
           {
               int left = start - indexOfBufferPool;
               if (buffer.Length <= left)
               {
                   indexOfBufferPool += buffer.Length;
                   continue;
               }
               else
               {
                   indexOfItem = left;
                   indexOfBufferPool = start;
               }
           }

           //復(fù)制數(shù)據(jù)
           int dataLeft = buffer.Length - indexOfItem;
           int dataNeed = countTotal - countCopyed;
           if (dataNeed >= dataLeft)
           {
               Buffer.BlockCopy(buffer, indexOfItem, result, countCopyed, dataLeft);
               countCopyed += dataLeft;
           }
           else
           {
               Buffer.BlockCopy(buffer, indexOfItem, result, countCopyed, dataNeed);
               countCopyed += dataNeed;
           }
           if (countCopyed >= countTotal)
           {
               Debug.Assert(countCopyed == countTotal);
               return result;
           }
       }
       throw new Exception("沒有足夠的數(shù)據(jù)!");
       // return result;
   }
}


類HttpReadParse借助BufferManage類,實(shí)現(xiàn)對(duì)http文本的解析。


public class HttpReadParse
{
   BufferManage _bufferManage = new BufferManage();
   public void AddBuffer(byte[] buffer)
   
{
       _bufferManage.AddBuffer(buffer);
   }
   public int HeaderByteCount { get; private set; } = -1;
   string _httpHeaderText = string.Empty;
   public string HttpHeaderText
   {
       get
       {
           if (_httpHeaderText != string.Empty)
               return _httpHeaderText;

           if (!IsHttpHeadOver)
               return _httpHeaderText;

           byte[] buffer = _bufferManage.GetSubBuffer(0, HeaderByteCount);
           _httpHeaderText = Encoding.UTF8.GetString(buffer);
           return _httpHeaderText;
       }
   }
   string _httpHeaderFirstLine = string.Empty;
   public string HttpHeaderFirstLine
   {
       get
       {
           if (_httpHeaderFirstLine != string.Empty)
               return _httpHeaderFirstLine;

           if (HttpHeaderText == string.Empty)
               return string.Empty;
           int index = HttpHeaderText.IndexOf(HttpConst.Flag_Return);
           if (index < 0)
               return string.Empty;

           _httpHeaderFirstLine = HttpHeaderText.Substring(0, index);
           return _httpHeaderFirstLine;
       }
   }

   public string HttpRequestUrl
   {
       get
       {
           if (HttpHeaderFirstLine == string.Empty)
               return string.Empty;

           string[] items = HttpHeaderFirstLine.Split(' ');
           if (items.Length < 2)
               return string.Empty;

           return items[1];
       }
   }
   public bool IsHttpHeadOver
   {
       get
       {
           if (HeaderByteCount > 0)
               return true;
           byte[] headOverFlag = HttpConst.Flag_DoubleReturnByte;

           if (_bufferManage.FindBuffer(headOverFlag, out int count))
           {
               HeaderByteCount = count;
               return true;
           }
           return false;
       }
   }
   int _httpContentLen = -1;
   public int HttpContentLen
   {
       get
       {
           if (_httpContentLen >= 0)
               return _httpContentLen;
           if (HttpHeaderText == string.Empty)
               return -1;
           int start = HttpHeaderText.IndexOf(HttpConst.Flag_HttpContentLenth);
           if (start < 0) //http請(qǐng)求沒有包體
               return 0;
           start += HttpConst.Flag_HttpContentLenth.Length;
           int end = HttpHeaderText.IndexOf(HttpConst.Flag_Return, start);
           if (end < 0)
               return -1;
           string intValue = HttpHeaderText.Substring(start, end - start).Trim();
           if (int.TryParse(intValue, out _httpContentLen))
               return _httpContentLen;
           return -1;
       }
   }
   public string HttpAllText
   {
       get
       {
           byte[] textBytes = _bufferManage.GetAllByte();
           string text = Encoding.UTF8.GetString(textBytes);
           return text;
       }
   }
   public int TotalByteLength => _bufferManage.TotalByteLength;
   public bool IsReadEnd
   {
       get
       {
           if (!IsHttpHeadOver)
               return false;
           if (HttpContentLen == -1)
               return false;
           int shouldLenth = HeaderByteCount + HttpContentLen;
           bool result = TotalByteLength >= shouldLenth;
           return result;
       }
   }
   public List<HttpByteValueKey> GetBodyParamBuffer()
   
{
       List<HttpByteValueKey> result = new List<HttpByteValueKey>();
       if (HttpContentLen < 0)
           return result;
       Debug.Assert(IsReadEnd);
       if (HttpContentLen == 0)
           return result;
       byte[] bodyBytes = _bufferManage.GetSubBuffer(HeaderByteCount, HttpContentLen);
       //獲取key value對(duì)應(yīng)的byte
       int start = 0;
       int current = 0;
       HttpByteValueKey item = null;
       foreach (byte b in bodyBytes)
       {
           if (item == null)
               item = new HttpByteValueKey();
           current++;
           if (b == '=')
           {
               byte[] buffer = new byte[current - start - 1];
               Buffer.BlockCopy(bodyBytes, start, buffer, 0, buffer.Length);
               item.Key = buffer;
               start = current;
           }
           else if (b == '&')
           
{
               byte[] buffer = new byte[current - start - 1];
               Buffer.BlockCopy(bodyBytes, start, buffer, 0, buffer.Length);
               item.Value = buffer;
               start = current;
               result.Add(item);
               item = null;
           }
       }
       if (item != null && item.Key != null)
       {
           byte[] buffer = new byte[bodyBytes.Length - start];
           Buffer.BlockCopy(bodyBytes, start, buffer, 0, buffer.Length);
           item.Value = buffer;
           result.Add(item);
       }
       return result;
   }
   public string HttpBodyText
   {
       get
       {
           if (HttpContentLen < 0)
               return string.Empty;
           Debug.Assert(IsReadEnd);
           if (HttpContentLen == 0)
               return string.Empty;
           byte[] bodyBytes = _bufferManage.GetSubBuffer(HeaderByteCount, HttpContentLen);
           string bodyString = Encoding.UTF8.GetString(bodyBytes);
           return bodyString;
       }
   }
}


4、性能測(cè)試


采用模擬客戶端持續(xù)發(fā)送http請(qǐng)求測(cè)試,每個(gè)http請(qǐng)求包含兩個(gè)圖片。一次http請(qǐng)求大概發(fā)送70K數(shù)據(jù)。服務(wù)端解析數(shù)據(jù)后,立即發(fā)送應(yīng)答。


注:所有測(cè)試都在本機(jī),客戶端無法模擬大量http請(qǐng)求,只能做簡(jiǎn)單壓力測(cè)試。


1)本人所寫的服務(wù)器,測(cè)試結(jié)果如下



每秒可發(fā)送300次請(qǐng)求,每秒發(fā)送數(shù)據(jù)25M,服務(wù)器cpu占有率為4%。


2)ASP.NET Core 服務(wù)器性能測(cè)試




每秒發(fā)送30次請(qǐng)求,服務(wù)器cpu占有率為12%。


測(cè)試對(duì)比


本人開發(fā)的服務(wù)端處理速度為ASP.NET Core的10倍,cpu占用為對(duì)方的三分之一。ASP.NET Core處理慢,有可能實(shí)現(xiàn)了更多的功能;只是這些隱藏的功能,對(duì)我們也沒用。


后記


如果沒有開發(fā)經(jīng)驗(yàn),沒有清晰的處理思路,開發(fā)一個(gè)高效的http服務(wù)器還有很困難的。


本人也一直以來都是采用ASP.NET Core作為http服務(wù)器。因?yàn)楣ぷ髦行枰咝У膆ttp服務(wù)器,就嘗試寫一個(gè)。


不可否認(rèn),ASP.NET Core各方面肯定優(yōu)化的很好;但是ASP.NET Core 提供的某些功能是多余的。如果化繁為簡(jiǎn),根據(jù)業(yè)務(wù)特點(diǎn)開發(fā),性能未必不能更優(yōu)。


該文章在 2025/7/2 16:58:15 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲(chǔ)管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

性小说视频| 在线看麻av| 美女黄色高潮视频网站| 99精品久久久久人妻精品| 日本亚洲欧洲无码久久久久| 操B 日韩 欧美| 一区二区三区免费版在线观 | 国产伦理视频在线播放| 日韩三级4K| 一级色情在线| 日韩欧美一线在线| 日本youjizz一区二区三区| 国产级别骚逼。| 国产真实夫妇一区二区| 国产,另类,欧美,一区二区| 日韩精品成人最新网站在线观看 | 亚洲午夜一区二区在线| 国产一区二区操美女| 日本XX少妇ⅩX久久| 日韩,亚洲欧美高清不卡| av国产香蕉| 久久黄色片视频欧美| 天天久久人人人天天天天| 亚洲天天在线啪| AV无码无播放器DVD更新| aaabbb免费在线| 在线欧美一级| 伊人影院欧美| 红桃视频国产在线视频不卡免费观看 | 日韩欧美国产成人娱乐在线观看 | 999久久久视频| 国产 日韩 欧美7月| 顶级高潮集合| 欧美精品一级免费黄片| 台湾佬中文娱乐网w一区二区| 思思久久国产精品| 一区二区三区四区视频网站| 男人的天堂电影院在线播放av| 午夜a在线试看91| 无码日韩精品一区二区不卡| 91午夜福利免费在线|