手记

ASP.NET那点不为人知的事

阅读目录

  •  开启服务

  • 分析报文,处理请求 

  •   总结

有了以下的知识:

ASP.NET那点不为人知的事(一)

ASP.NET那点不为人知的事(二)

想必开发一个小型服务器以不是问题了,功能补复杂,能够响应客户端浏览器的请求,并根据请求文件的类型返回响应的信息,如能处理静态页面、图片、样式、脚本、动态页面等。  

回顾

由于客户端和服务端的通信是通过Socket通信,且它们通信的“语言”是基于Http1.1协议。根据这个线索,我们完全可以自己开发服务器软件,暂且叫他Melodies Server,当然这是一个很简单的样例,和真正的服务器还是有差距的,好,我们进入正题,首先需要了解以下几个知识点:

  • 客户端和服务端是由Socket进行通信,在服务器端需要有监听请求的套接字,他绑定在某个端口号上,如果发现有请求过来,socket.Accept()产生一个套接字和客户端进行通信。

  • 客户端发送的请求(报文)交给服务器软件分析,判断是否为静态页面、图片还是动态aspx文件,若是静态文件能直接返回。

  • 处理动态页面稍稍麻烦,需要反射创建页面类(原因详见ASP.NET那点不为人知的事(二))

回到顶部

 开启服务

 public partial class WebServerForm : Form    {        public WebServerForm()        {            InitializeComponent();            TextBox.CheckForIllegalCrossThreadCalls = false;        }        private Socket socketWatch;//负责监听浏览器连接请求的套接字             private Thread threadWatch;//负责循环调用Socket.Accept 监听线程           private void btnStartServer_Click(object sender, EventArgs e)        {            socketWatch=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);            IPAddress address = IPAddress.Parse(txtIPAddress.Text.Trim());            IPEndPoint endPoint=new IPEndPoint(address,int.Parse(txtPort.Text.Trim()));            socketWatch.Bind(endPoint);            socketWatch.Listen(10);            threadWatch = new Thread(WatchConnect);            threadWatch.IsBackground = true;            threadWatch.Start();        }        private bool isWatch = true;        //Dictionary<>         void WatchConnect()        {            while (isWatch)            {                Socket socketConnection=socketWatch.Accept();                ShowMsg("浏览器:"+socketConnection.RemoteEndPoint.ToString()+",连接成功*********************");                ConnectionClient connectionClient = new ConnectionClient(socketConnection, ShowMsg);            }        }        void ShowMsg(string msg)        {            txtLog.AppendText(msg+"\r\n");        }            }

回到顶部

分析报文,处理请求 

  • 在异步线程创建的与客户端通信的Socket,它的主要职责就是分析保文:

    /// <summary>    /// 与客户端连接通信类(包含一个与客户端通信的套接字和通信线程)    /// </summary>    public class ConnectionClient    {        private Socket socketMsg;//与客户端通信套接字        private Thread threadMsg;//通信线程        private DGShowMsg dgShowMsg;//负责向主窗体文本框显示消息的委托        public ConnectionClient(Socket socket,DGShowMsg dgShowMsg)        {            this.socketMsg = socket;            this.dgShowMsg = dgShowMsg;            //负责启动一个接受客户端浏览器请求报文的线程            threadMsg = new Thread(ReceiveMsg);            threadMsg.IsBackground = true;            threadMsg.Start();        }        private bool isRec = true;        void ReceiveMsg()        {            while (isRec)            {                byte[] arrMsg=new byte[1024*1024*3];                //接受对应客户端发送过来的请求报文                int length = socketMsg.Receive(arrMsg);                string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, 0, length);                dgShowMsg(strMsg);                //处理报文                string[] arrStr = strMsg.Replace("\r\n", "韘").Split('韘');                string[] firstRow=arrStr[0].Split(' ');                string requestFile = firstRow[1];                ExcuteRequest(requestFile);//todo:长连接多少时间            }        }        private void ExcuteRequest(string requestFile)        {            //获得被请求页面的后缀名            string fileExtension = System.IO.Path.GetExtension(requestFile);            if (!string.IsNullOrEmpty(fileExtension))            {                switch (fileExtension.ToLower())            {                    case ".html":                    case ".htm":                    case ".css":                    case ".js":                                       ExcuteStaticPage(requestFile,fileExtension);                    break;                    case ".jpg":                    ExcuteImg(requestFile,fileExtension);                    break;                    case ".aspx":                    ExcuteDymPage(requestFile,fileExtension);                    break;            }            }        }
  •   针对不同的请求执行不同的操作,其中静态页面、css、js、图片处理操作一样,都是属性静态文件,直接返回字节流:

/// <summary>        /// 处理静态页面,直接输出        /// </summary>        private void ExcuteStaticPage(string requestPath,string fileExtension)        {            StringBuilder sb=new StringBuilder();            //获得请求文件的文件夹的物理路径            string dataDir = AppDomain.CurrentDomain.BaseDirectory;            if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release\"))            {                dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;            }            string phyPath = dataDir + requestPath;            //读取静态页面内容            string fileContent = System.IO.File.ReadAllText(phyPath);            //获得响应体字节数组            byte[] fileArr = System.Text.Encoding.UTF8.GetBytes(fileContent);            //获得响应报文头:            string responseHeader = GetResponseHeader(fileArr.Length, fileExtension);            byte[] arrHead = System.Text.Encoding.UTF8.GetBytes(responseHeader);            //发送响应报文头回浏览器            socketMsg.Send(arrHead);            //发送响应报文体回浏览器            //todo:sleep 1分钟会怎样            socketMsg.Send(fileArr);        }        /// <summary>        /// 处理图片        /// </summary>        /// <param name="requestPath"></param>        /// <param name="extentionName"></param>        private void ExcuteImg(string requestPath, string extentionName)        {            //获得请求文件的文件夹的物理路径            string dataDir = AppDomain.CurrentDomain.BaseDirectory;            if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release"))            {                dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;            }            //获得请求文件的物理路径(绝对路径)            string phyPath = dataDir + requestPath;            int imgLength;            byte[] fileArr;            //读取图片内容            using (FileStream fs = new FileStream(phyPath, FileMode.Open))            {                fileArr = new byte[fs.Length];                imgLength = fs.Read(fileArr, 0, fileArr.Length);                //获得响应报文头                string responseHeader = GetResponseHeader(imgLength, extentionName);                byte[] arrHeader = System.Text.Encoding.UTF8.GetBytes(responseHeader);                socketMsg.Send(arrHeader);                socketMsg.Send(fileArr, imgLength, SocketFlags.None);            }        }        /// <summary>        /// 得到响应头信息        /// </summary>        /// <param name="contentLength"></param>        /// <param name="fileExtentionName"></param>        /// <returns></returns>        private string GetResponseHeader(int contentLength,string fileExtentionName)        {            StringBuilder sbHeader=new StringBuilder();            sbHeader.Append("HTTP/1.1 200 OK\r\n");            sbHeader.Append("Content-Length: "+contentLength+"\r\n");            sbHeader.Append("Content-Type:" + GetResponseHeadContentType(fileExtentionName) + ";charset=utf-8\r\n\r\n");            return sbHeader.ToString();        }        /// <summary>        /// 根据后缀名获取响应保文中的内容类型        /// </summary>        /// <param name="fileExtentionName"></param>        /// <returns></returns>        private string GetResponseHeadContentType(string fileExtentionName)        {            switch (fileExtentionName.ToLower())            {                case ".html":                case ".htm":                case ".aspx":                    return "text/html";                    break;                case ".css":                    return "text/plain";                    break;                case ".js":                    return "text/javascript";                    break;                case ".jpg":                    return "image/JPEG";                case ".gif":                    return "image/GIF";                    break;                default:                    return "text/html";                    break;            }        }
  • 同样,针对动态页面反射创建其页面类,注意记得让其实现IHttpHandler接口

  • 创建一个页面类View

 public class View:IHttpHandler    {        public string ProcessRequest()        {            string dataDir = AppDomain.CurrentDomain.BaseDirectory;            //获得模板物理路径            if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release"))            {                dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;            }            string phyPath = dataDir + "/model.htm";            string modelContent=System.IO.File.ReadAllText(phyPath);            modelContent = modelContent.Replace("@Title", "动态页面").Replace("@Content", "反射创建页面类");            return modelContent;        }    }
  • 反射View,调用其ProcessRequest方法执行服务端代码

  /// <summary>        /// 反射创建动态页面对象        /// </summary>        /// <param name="requestFile"></param>        /// <param name="extentionName"></param>        private void ExcuteDymPage(string requestFile, string extentionName)        {            string pageClassName = System.IO.Path.GetFileNameWithoutExtension(requestFile);            string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;            //获得页面类全名称            pageClassName = assemblyName + "." + pageClassName;            //通过反射创建页面类对象            object pageObj = Assembly.GetExecutingAssembly().CreateInstance(pageClassName);            IHttpHandler page = pageObj as IHttpHandler;            byte[] fileArr=null;            if (page!=null)            {                string strHtml=page.ProcessRequest();                fileArr= System.Text.Encoding.UTF8.GetBytes(strHtml);            }            //获得响应报文头            string responseHeader = GetResponseHeader(fileArr.Length, extentionName);            byte[] arrHeader = System.Text.Encoding.UTF8.GetBytes(responseHeader);            socketMsg.Send(arrHeader);            socketMsg.Send(fileArr);        }

回到顶部

  

0人推荐
随时随地看视频
慕课网APP