C#如何解析http報文
下面通過一段內(nèi)容有文字說明有代碼分析,并附有展示圖供大家學習。
要解析HTTP報文,需要實現(xiàn)以下操作:
讀取HTTP報頭提供的各種屬性
分析屬性值,從中獲取內(nèi)容編碼和字符集編碼
將報頭數(shù)據(jù)和內(nèi)容進行分離
判斷內(nèi)容是否文本還是二進制,如果是二進制的則不進行處理
如果內(nèi)容是文本,按報頭中提供的內(nèi)容編碼和字符集編碼進行解壓縮和解碼
目前沒有找到.Net框架內(nèi)置的解析方法,理論上HttpClient等類在內(nèi)部應該已經(jīng)實現(xiàn)了解析,但不知為何沒有公開這些處理方法。(亦或是我沒找到)
那么只能自己來解析這些數(shù)據(jù)了。
我們先來看看這個經(jīng)過gzip壓縮的文本內(nèi)容的HTTP報文:
這里提供一個老外寫的簡陋的解析類(已經(jīng)過修改,原代碼中存在一些嚴重BUG):
public enum HTTPHeaderField { Accept = 0, Accept_Charset = 1, Accept_Encoding = 2, Accept_Language = 3, Accept_Ranges = 4, Authorization = 5, Cache_Control = 6, Connection = 7, Cookie = 8, Content_Length = 9, Content_Type = 10, Date = 11, Expect = 12, From = 13, Host = 14, If_Match = 15, If_Modified_Since = 16, If_None_Match = 17, If_Range = 18, If_Unmodified_Since = 19, Max_Forwards = 20, Pragma = 21, Proxy_Authorization = 22, Range = 23, Referer = 24, TE = 25, Upgrade = 26, User_Agent = 27, Via = 28, Warn = 29, Age = 30, Allow = 31, Content_Encoding = 32, Content_Language = 33, Content_Location = 34, Content_Disposition = 35, Content_MD5 = 36, Content_Range = 37, ETag = 38, Expires = 39, Last_Modified = 40, Location = 41, Proxy_Authenticate = 42, Refresh = 43, Retry_After = 44, Server = 45, Set_Cookie = 46, Trailer = 47, Transfer_Encoding = 48, Vary = 49, Warning = 50, WWW_Authenticate = 51 }; class HTTPHeader { #region PROPERTIES private string[] m_StrHTTPField = new string[52]; private byte[] m_byteData = new byte[4096]; public string[] HTTPField { get { return m_StrHTTPField; } set { m_StrHTTPField = value; } } public byte[] Data { get { return m_byteData; } set { m_byteData = value; } } #endregion // convertion System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); #region CONSTRUCTEUR /// <summary> /// Constructeur par défaut - non utilisé /// </summary> private HTTPHeader() { } public HTTPHeader(byte[] ByteHTTPRequest) { string HTTPRequest = encoding.GetString(ByteHTTPRequest); try { int IndexHeaderEnd; string Header; // Si la taille de requête est supérieur ou égale à 1460, alors toutes la chaine est l'entête http if (HTTPRequest.Length <= 1460) Header = HTTPRequest; else { IndexHeaderEnd = HTTPRequest.IndexOf("\r\n\r\n"); Header = HTTPRequest.Substring(0, IndexHeaderEnd); Data = ByteHTTPRequest.Skip(IndexHeaderEnd + 4).ToArray(); } HTTPHeaderParse(Header); } catch (Exception) { } } #endregion #region METHODES private void HTTPHeaderParse(string Header) { #region HTTP HEADER REQUEST & RESPONSE HTTPHeaderField HHField; string HTTPfield, buffer; int Index; foreach (int IndexHTTPfield in Enum.GetValues(typeof(HTTPHeaderField))) { HHField = (HTTPHeaderField)IndexHTTPfield; HTTPfield = "\n" + HHField.ToString().Replace('_', '-') + ": "; //Ajout de \n devant pour éviter les doublons entre cookie et set_cookie // Si le champ n'est pas présent dans la requête, on passe au champ suivant Index = Header.IndexOf(HTTPfield); if (Index == -1) continue; buffer = Header.Substring(Index + HTTPfield.Length); Index = buffer.IndexOf("\r\n"); if (Index == -1) m_StrHTTPField[IndexHTTPfield] = buffer.Trim(); else m_StrHTTPField[IndexHTTPfield] = buffer.Substring(0, Index).Trim(); //Console.WriteLine("Index = " + IndexHTTPfield + " | champ = " + HTTPfield.Substring(1) + " " + m_StrHTTPField[IndexHTTPfield]); } // Affichage de tout les champs /*for (int j = 0; j < m_StrHTTPField.Length; j++) { HHField = (HTTPHeaderField)j; Console.WriteLine("m_StrHTTPField[" + j + "]; " + HHField + " = " + m_StrHTTPField[j]); } */ #endregion } #endregion }
編寫以下代碼以實現(xiàn)解析文件:
class Program { static void Main(string[] args) { SRART: Console.WriteLine("輸入待解析的HTTP報文數(shù)據(jù)文件完整路徑:"); var filename = Console.ReadLine(); try { FileStream fs = new FileStream(filename, FileMode.Open); BinaryReader br = new BinaryReader(fs); var data = br.ReadBytes((int)fs.Length); var header = new HTTPHeader(data); var x = 0; foreach (var f in header.HTTPField) { if (!String.IsNullOrEmpty(f)) { Console.WriteLine($"[{x:00}] - {(HTTPHeaderField) x} : {f}"); } x++; } Console.WriteLine($"總數(shù)據(jù)尺寸{fs.Length}字節(jié),實際數(shù)據(jù)尺寸{header.Data.Length}字節(jié)"); Console.WriteLine(Encoding.UTF8.GetString(header.Data)); Console.WriteLine(); br.Close(); fs.Close(); } catch (Exception e) { Console.WriteLine(e); } goto SRART; } }
這里還未實現(xiàn)gzip解壓縮和字符解碼,直接用UTF8解碼輸出的。(需要時再寫吧,都是體力活兒~)
效果圖展示:
下面的圖是沒有經(jīng)過gzip壓縮過的數(shù)據(jù)。
以上就是用C#如何解析http報文的全部內(nèi)容,哪位大俠還有好的方法歡迎提出寶貴意見,喜歡大家喜歡以上內(nèi)容所述。
- c#使用Socket發(fā)送HTTP/HTTPS請求的實現(xiàn)代碼
- C#使用HttpPost請求調(diào)用WebService的方法
- C#使用Http Post方式傳遞Json數(shù)據(jù)字符串調(diào)用Web Service
- C#請求http向網(wǎng)頁發(fā)送接收數(shù)據(jù)的方法
- C#根據(jù)http和ftp圖片地址獲取對應圖片
- C#模擬http 發(fā)送post或get請求的簡單實例
- C#基于socket模擬http請求的方法
- C#使用IHttpModule接口修改http輸出的方法
- C#實現(xiàn)發(fā)送簡單HTTP請求的方法
- C#實現(xiàn)簡單的Http請求實例
- 實例詳解C#實現(xiàn)http不同方法的請求
相關文章
理解C#編程中的靜態(tài)類和靜態(tài)成員以及密封類
這篇文章主要介紹了理解C#編程中的靜態(tài)類和靜態(tài)成員以及密封類,注意類成員的相關訪問限制和類的繼承問題,需要的朋友可以參考下2016-01-01C#中DataSet、DataTable、DataRow數(shù)據(jù)的復制方法
這篇文章介紹了C#中DataSet、DataTable、DataRow數(shù)據(jù)的復制方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07