认知尚浅,如有错误,愿闻其详!
最近刚接触ASP.NET,在此之前对于Web开发方面没有过多的了解,更不懂得浏览器访问网页的整个大概的过程经历了什么,整个思维模式仍停留在客户端开发C/S模式下,所以,有个比较浅显的制作简单IIS服务器例子来搞懂ASP.NET的大概处理过程来过渡,尤为重要。那我们就来学习这个例子。
首先,我们了解一下HTTP是啥?
HTTP是一种传输协议,全称(HyperText Transfer Protocol)超文本传输协,目的是为了规范请求与接收HTML页面的方法。只有根据协议包装报文数据(请求报文,响应报文),才能够正常的解析与处理响应。报文具体结构如下:
HTTP请求报文:
其结构分为请求头、状态行、空行和请求体(GET则为空)四个部分组成,上图给为请求报文>的一般格式。
HTTP响应报文:
响应报文与请求报文结构类似,如上图。
详情可转至该文:https://blog.csdn.net/lhx_ldm/article/details/80338211
IIS服务
IIS全称Internet Information Service,中文名:Internet信息服务,专用于微软操作系统平台,兼容微软的各项Web技术,尤其是ASP.NET。用于Web网页的浏览的信息处理,也就是网站服务器。其处理ASP.NET的大概过程如下图。
ASP请求编译流程图.png
详情可转至该文:https://blog.csdn.net/qq_35393869/article/details/81182237
SimpleIIS例子
这是我在学习的过程中做的思维导图,如下图,然后根据思维导图再做出项目。IIS其内部处理简单的分为三大块:请求报文解析(HttpRequest)、报文处理(HttpApplication)、响应报文回传(HttpResponse),其基本的通信是通过Socket通信来实现,
代码
由于项目文件挺多的,代码量也挺多,篇幅过大,就不一一展示
服务器端
服务器不断地侦听接收客户端发来的请求,然后进行处理
服务端参考Socket实现:https://www.cnblogs.com/memoyu/p/10764884.html
//1、初始化解析 请求报文 。
HttpContext context = new HttpContext(msgStr);
//2、处理请求报文,处理响应报文。
HttpApplicaton applicaton = new HttpApplicaton();
applicaton.ProcessRequest(context);
//3、返回响应内容
proxSocket.Send(context.httpResponse.GetHttpResponseHeader());//返回响应头
proxSocket.Send(context.httpResponse.body);
//4、关闭连接
proxSocket.Shutdown(SocketShutdown.Both);
proxSocket.Close();
处理请求信息(HttpApplication)
获取HttpRequest解析的请求头信息,然后对请求信息进行处理,本质是对HTML的解析,与动态页面的数据填充和整合,然后响应给客户端。请求可是请求静态页面和动态页面,静态页面直接将数据组合入响应体返回,动态页面则需要执行C#代码(执行的方式是通过反射,然后创建IHttpHandler对象,执行接口方法ProcessRequest方法),然后将动态数据组合入响应体返回。
/// <summary>
/// 实现接口,处理报文
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
#region 处理动态文件请求
string ext = Path.GetExtension(context.httpRequest.url);//通过请求的地址获得请求文件拓展名
if (ext == ".aspx")//如果是.aspx文件
{
//通过反射的方式去实例化对应类,执行其内部方法,实现处理动态页面
var className = Path.GetFileNameWithoutExtension(context.httpRequest.url);//获得不具有拓展名的路径
IHttpHandler obj = Assembly.Load("SimpleIIS").CreateInstance("SimpleIIS." + className) as IHttpHandler;//通过反射创建对象
obj.ProcessRequest(context);//执行动态页面处理
return;
}
#endregion
#region 静态页面处理
//获得请求路径
// context.httpRequest.url 例如:/web/sujin.html 相对路径
string basePath = AppDomain.CurrentDomain.BaseDirectory;//获取程序的bin/Debug目录,相当于网站的根目录,
string fileName = Path.Combine(basePath, context.httpRequest.url.TrimStart('/'));//拼接url 获得请求页面的绝对路径 ,源于url地址开头带有/,所以需要先去除在拼接
if (!File.Exists(fileName))//判断请求文件是否存在
{
context.httpResponse.stateCode = "404";//设置响应报文参数
context.httpResponse.stateDescribe = "Not Found";
context.httpResponse.contentType = "text/html";
string noExistHtml = Path.Combine(basePath , @"web\404.html");//拼接404页面地址
context.httpResponse.body = File.ReadAllBytes(noExistHtml);//将404.html页面加入响应体
}
else
{
context.httpResponse.stateCode = "200";//设置响应报文参数
context.httpResponse.stateDescribe = "Ok";
context.httpResponse.contentType = GetContentType(Path.GetExtension(context.httpRequest.url));//获得请求文件类型
context.httpResponse.body = File.ReadAllBytes(fileName);//将请求文件写加入响应体
}
#endregion
}
/// <summary>
/// 根扩展名获得请求的文件类型
/// </summary>
/// <param name="ext">扩展名</param>
/// <returns>响应文件类型</returns>
public string GetContentType(string ext)
{
string type = "text/html; charset=UTF-8";
switch (ext)
{
case ".aspx":
case ".html":
case ".htm":
type = "text/html; charset=UTF-8";
break;
case ".png":
type = "image/png";
break;
case ".gif":
type = "image/gif";
break;
case ".jpg":
type = "image/jpg";
break;
case ".jpeg":
type = "image/jpeg";
break;
case ".css":
type = "text/css";
break;
case ".js":
type = "application/x-javascript";
break;
default:
type = "text/plain; charset=gbk";
break;
}
return type;
}
请求头的处理(HttpRequest)
/// <summary>
/// 请求解析构造函数,解析请求,对应赋值属性
/// </summary>
/// <param name="requestStr"></param>
/*
* 请求体:
GET / HTTP/1.1
Host: 192.168.0.4:45000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
*/
public HttpRequest(string requestStr)
{
string[] lines = requestStr.Replace("\r\n" , "\r").Split('\r'); //源于splite只能处理单个字符,所以的将\r\n替换成\r
httpMethod = lines[0].Split(' ')[0];//赋值请求的方法
url = lines[0].Split(' ')[1];//赋值请求连接
httpVersion = lines[0].Split(' ')[2];//赋值http版本号
}
响应报文处理(HttpResponse)
/// <summary>
/// 拼接响应头数据
/// </summary>
/// <returns></returns>
public byte[] GetHttpResponseHeader()
{
string strResponseHeader = string.Format(@"HTTP/1.1 {0} {1}
Content-Type: {2}
Accept-Ranges: bytes
server: Microsoft-IIS/8.5
X-Powered-By: ASP.NET
Date: Sat, 23 Nov 2019 08:43:15
Content-Length: {3}
",stateCode , stateDescribe , contentType , body.Length);
return Encoding.Default.GetBytes(strResponseHeader);
}
模拟动态页面
一般状况下,我们访问的网页都是动态的,所以,实现动态网页功能是必要的,我们不仅可以请求静态页面,动态的也需要处理,ASP.NET中动态页面为.aspx 和 .ashx,类似如下结构。
public class DynamicPage_1 : IHttpHandler
{
/// <summary>
/// 实现接口方法
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
string str = string.Format("<html><head></head><body><h3>{0}<h3></body></html>", DateTime.Now.ToString());//动态生成html内容,时间
context.httpResponse.stateCode = "200";//赋值响应头
context.httpResponse.stateDescribe = "Ok";
context.httpResponse.contentType = "text/html";
context.httpResponse.body = Encoding.Default.GetBytes(str);//赋值响应体
}
}
效果展示
我在bin/Debug/web目录下放了保存的两个静态页面.
静态页面.png SimpleIIS.gif
网友评论