C#如何解析http报文

 更新时间:2020年6月25日 11:27  点击:2294

下面通过一段内容有文字说明有代码分析,并附有展示图供大家学习。

要解析HTTP报文,需要实现以下操作:

读取HTTP报头提供的各种属性
分析属性值,从中获取内容编码和字符集编码
将报头数据和内容进行分离
判断内容是否文本还是二进制,如果是二进制的则不进行处理
如果内容是文本,按报头中提供的内容编码和字符集编码进行解压缩和解码
目前没有找到.Net框架内置的解析方法,理论上HttpClient等类在内部应该已经实现了解析,但不知为何没有公开这些处理方法。(亦或是我没找到)

那么只能自己来解析这些数据了。

我们先来看看这个经过gzip压缩的文本内容的HTTP报文:

这里提供一个老外写的简陋的解析类(已经过修改,原代码中存在一些严重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
}

编写以下代码以实现解析文件:

class Program
{
 static void Main(string[] args)
 {
  SRART: Console.WriteLine("输入待解析的HTTP报文数据文件完整路径:");
  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($"总数据尺寸{fs.Length}字节,实际数据尺寸{header.Data.Length}字节");
   Console.WriteLine(Encoding.UTF8.GetString(header.Data));
   Console.WriteLine();
   br.Close();
   fs.Close();  
  }
  catch (Exception e)
  {
   Console.WriteLine(e);
  }
  goto SRART;
 }
}

这里还未实现gzip解压缩和字符解码,直接用UTF8解码输出的。(需要时再写吧,都是体力活儿~)

效果图展示:

下面的图是没有经过gzip压缩过的数据。

以上就是用C#如何解析http报文的全部内容,哪位大侠还有好的方法欢迎提出宝贵意见,喜欢大家喜欢以上内容所述。

[!--infotagslink--]

相关文章

  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • Java如何发起http请求的实现(GET/POST)

    这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
  • JavaScript预解析,对象详解

    这篇文章主要介绍了JavaScript预解析,对象的的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助...2021-11-10
  • php常量详细解析

    一、常量常量是一个简单值的标识符(名字)。如同其名称所暗示的,在脚本执行期间该值不能改变(除了所谓的魔术常量,它们其实不是常量)。常量默认为大小写敏感。按照惯例常量标识符总是大写的。 常量名和其它任何 PHP 标签遵循...2015-10-30
  • C#实现HTTP下载文件的方法

    这篇文章主要介绍了C#实现HTTP下载文件的方法,包括了HTTP通信的创建、本地文件的写入等,非常具有实用价值,需要的朋友可以参考下...2020-06-25
  • C#使用Http Post方式传递Json数据字符串调用Web Service

    这篇文章主要为大家详细介绍了C#使用Http Post方式传递Json数据字符串调用Web Service,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
  • 解决Feign切换client到okhttp无法生效的坑(出现原因说明)

    这篇文章主要介绍了解决Feign切换client到okhttp无法生效的坑(出现原因说明),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25
  • 完美解决SpringCloud-OpenFeign使用okhttp替换不生效问题

    这篇文章主要介绍了完美解决SpringCloud-OpenFeign使用okhttp替换不生效问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25
  • 解决Java处理HTTP请求超时的问题

    这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
  • HTTP 415错误-Unsupported media type详解

    这篇文章主要介绍了HTTP 415错误-Unsupported media type详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-16
  • Angular利用HTTP POST下载流文件的步骤记录

    这篇文章主要给大家介绍了关于Angular利用HTTP POST下载流文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用Angular具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-07-26
  • 详解nginx同一端口监听多个域名和同时监听http与https

    这篇文章主要介绍了详解nginx同一端口监听多个域名和同时监听http与https的相关资料,需要的朋友可以参考下...2017-07-06
  • JS跨浏览器解析XML应用过程详解

    这篇文章主要介绍了JS跨浏览器解析XML应用过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-10-16
  • Node.js之http模块的用法

    这篇文章主要介绍了Node.js之http模块的用法,对Node.js感兴趣的同学,可以参考下...2021-04-25
  • 微信小程序封装http访问网络库实例代码

    这篇文章主要介绍了微信小程序封装http访问网络库实例代码的相关资料,需要的朋友可以参考下...2017-05-27
  • JS中Eval解析JSON字符串的一个小问题

    JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧,下面通过本文给大家介绍JS中Eval解析JSON字符串的一个小问题,需要的朋友参考下吧...2016-02-23
  • C# 如何解析获取Url参数值

    这篇文章主要介绍了C# 如何解析获取Url参数值,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2020-09-01
  • JavaScript实现解析INI文件内容的方法

    这篇文章主要介绍了JavaScript实现解析INI文件内容的方法,结合实例形式分析了javascript通过自定义函数实现针对ini文件解析操作的相关处理技巧,需要的朋友可以参考下...2016-11-22
  • error LNK2019: 无法解析的外部符号 问题的解决办法

    error LNK2019: 无法解析的外部符号 问题的解决办法,需要的朋友可以参考一下...2020-04-25
  • 快速学习AngularJs HTTP响应拦截器

    任何时候,如果我们想要为请求添加全局功能,例如身份认证、错误处理等,在请求发送给服务器之前或服务器返回时对其进行拦截,是比较好的实现手段...2016-01-05