当前位置:   article > 正文

模拟Web服务器

模拟web服务器
     模拟Web服务器,实现基本功能来了解Web的原理,对于学习Asp.Net当然是铺垫作用,也能更好上手Asp.Net知识点。
     首先浏览器发送请求报文给Web服务器,
     紧接着Web服务器接收浏览器发送过来的请求,并将接收到的请求内容进行数据处理,然后准备响应报文,包括响应头、响应行以及响应体,发送响应报文给浏览器。
     最后浏览器将接收到的响应报文进行解析,展示给用户,就是我们看到的网页内容了。
     不过模拟Web服务器,只是模拟Web服务器的部分功能。
     第一步——监听浏览器是否进行连接?如果连接,接收浏览器发送的请求报文数据内容:
  1. //运行服务器
  2. private void btnStart_Click(object sender, EventArgs e)
  3. {
  4. //创建连接的ip地址
  5. IPAddress ip = IPAddress.Parse(txtIp.Text);
  6. //创建连接的ip地址和端口号
  7. IPEndPoint port = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
  8. //创建一个基本的socket
  9. Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  10. try
  11. {
  12. //绑定ip和端口号
  13. socket.Bind(port);
  14. //监听的最大等待个数
  15. socket.Listen(10);
  16. ShowMsg("开始运行了...");
  17. //接收浏览器,将监听封装成一个方法
  18. Thread th = new Thread(Listen);
  19. //将监听的这个方法放到线程中,设为后台线程。
  20. th.IsBackground = true;
  21. th.Start(socket);
  22. }
  23. catch (Exception ex)
  24. {
  25. ShowMsg(ex.Message);
  26. }
  27. }
  28. //监听浏览器的连接
  29. void Listen(object o)
  30. {
  31. Socket socket = o as Socket;
  32. while (true)
  33. {
  34. //创建负责通信用的socket
  35. Socket connSocket = socket.Accept();
  36. ShowMsg(connSocket.RemoteEndPoint.ToString() + "连接成功了");
  37. //接收消息
  38. byte[] buffer = new byte[1024 * 1024];
  39. //实际接收到的字节个数
  40. int num = connSocket.Receive(buffer);
  41. string str = Encoding.UTF8.GetString(buffer, 0, num);
  42. ShowMsg(str);
  43. }
  44. }
     第二步——将显示消息交由DataConnection类来实现:
  1. //监听浏览器的连接
  2. void Listen(object o)
  3. {
  4. Socket socket = o as Socket;
  5. while (true)
  6. {
  7. //创建负责通信用的socket
  8. Socket connSocket = socket.Accept();
  9. ShowMsg(connSocket.RemoteEndPoint.ToString() + "连接成功了");
  10. //接收消息,写到一个DataConnection类中,new一个对象
  11. //通过DataConnection接收http请求和发送http响应
  12. DataConnection dc = new DataConnection(connSocket, ShowMsg);
  13. }
  14. }
  15. DataConnection类实现代码:
  16. //定义一个委托,实现展示消息方法在类与类之间进行传递
  17. delegate void DelSetText(string msg);
  18. /// <summary>
  19. /// 1、接收浏览器发送的请求
  20. /// 2、向浏览器发送响应
  21. /// </summary>
  22. class DataConnection
  23. {
  24. private Socket connSocket;
  25. //要指向窗体中给文本框赋值的方法(ShowMsg)
  26. private DelSetText del;
  27. //构造函数中把通信用的connsocket,还有委托方法传过来
  28. //获取浏览器发送过来的http请求
  29. public DataConnection(Socket connSocket, DelSetText del)
  30. {
  31. this.connSocket = connSocket;
  32. this.del = del;
  33. //2、执行接收消息的方法
  34. string msg = RecMsg();
  35. //把消息——http请求报文显示在文本框中
  36. del(msg);
  37. }
  38. //接收消息
  39. string RecMsg()
  40. {
  41. //接收消息
  42. byte[] buffer = new byte[1024 * 1024];
  43. //实际接收到的字节个数
  44. int num = connSocket.Receive(buffer);
  45. string str = Encoding.UTF8.GetString(buffer, 0, num);
  46. return str;
  47. }
     第三步——DataConnection类分析请求报文并根据请求路径返回具体页面:
  1. //定义一个委托,实现展示消息方法在类与类之间进行传递
  2. delegate void DelSetText(string msg);
  3. /// <summary>
  4. /// 1、接收浏览器发送的请求
  5. /// 2、向浏览器发送响应
  6. /// </summary>
  7. class DataConnection
  8. {
  9. private Socket connSocket;
  10. //要指向窗体中给文本框赋值的方法(ShowMsg)
  11. private DelSetText del;
  12. //构造函数中把通信用的connsocket,还有委托方法传过来
  13. //获取浏览器发送过来的http请求
  14. public DataConnection(Socket connSocket, DelSetText del)
  15. {
  16. this.connSocket = connSocket;
  17. this.del = del;
  18. //2、执行接收消息的方法
  19. string msg = RecMsg();
  20. //把消息——http请求报文显示在文本框中
  21. del(msg);
  22. //3、分析http请求报文,写到一个Request类中
  23. Request req = new Request(msg);
  24. //GET /1.html HTTP/1.1
  25. //req.Url相对路径 /1.html
  26. //4、根据请求的路径,判断是静态页面还是动态页面
  27. Judge(req.Url);
  28. }
  29. Request类中实现代码:
  30. /// <summary>
  31. /// 解析请求报文中的url
  32. /// </summary>
  33. class Request
  34. {
  35. /// <summary>
  36. /// 解析msg中的url
  37. /// </summary>
  38. /// <param name="msg">请求报文</param>
  39. public Request(string msg)
  40. {
  41. try
  42. {
  43. //以行分割请求报文
  44. string[] arrMsg = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
  45. //请求报文中的请求行
  46. string firstLine = arrMsg[0];
  47. //以空格分割请求报文中的请求行
  48. string[] arr = firstLine.Split(' ');
  49. //获得请求行中的路径
  50. url = arr[1];
  51. }
  52. catch (Exception ex)
  53. {
  54. url = "";
  55. }
  56. }
  57. private string url;
  58. /// <summary>
  59. /// 请求行中的路径
  60. /// </summary>
  61. public string Url
  62. {
  63. get { return url; }
  64. set { url = value; }
  65. }
  66. }
     第四步——DataConnection类中将生成响应头交由Response类实现:
  1. Response类实现代码:
  2. /// <summary>
  3. /// 准备响应头
  4. /// </summary>
  5. class Response
  6. {
  7. //用来存放状态码和状态描述
  8. private Dictionary<int, string> dic = new Dictionary<int, string>();
  9. //状态码
  10. private int statusCode = 200;
  11. //内容的长度
  12. private int contentLength;
  13. //内容的类型
  14. private string contentType;
  15. //调用构造方法,获得状态码、内容的长度和内容的类型。
  16. public Response(int statusCode, int contentLength, string url)
  17. {
  18. //填充字典,状态码和状态码的描述
  19. FillDic();
  20. //状态码
  21. this.statusCode = statusCode;
  22. //内容的长度
  23. this.contentLength = contentLength;
  24. //调用方法来获得内容的类型
  25. GetContentType(url);
  26. }
  27. //填充字典,状态码和状态码描述对应的键值对
  28. void FillDic()
  29. {
  30. dic.Add(200, "OK");
  31. dic.Add(404, "Not Found");
  32. }
  33. //根据后缀给contentType内容类型赋值
  34. void GetContentType(string url)
  35. {
  36. //.htm
  37. string ext = Path.GetExtension(url);
  38. switch (ext)
  39. {
  40. case ".htm":
  41. case ".html":
  42. contentType = "text/html";
  43. break;
  44. case ".css":
  45. contentType = "text/css";
  46. break;
  47. case ".js":
  48. contentType = "text/javascript";
  49. break;
  50. case ".jpg":
  51. contentType = "image/jpeg";
  52. break;
  53. default:
  54. contentType = "text/html";
  55. break;
  56. }
  57. }
  58. //生成响应头
  59. public byte[] GetResponseHeaders()
  60. {
  61. //HTTP/1.1 200 OK
  62. //Content-Length: 427380
  63. //Content-Type: image/jpeg;charset=utf-8
  64. StringBuilder sb = new StringBuilder();
  65. sb.AppendLine("HTTP/1.1 " + statusCode + " " + dic[statusCode]);
  66. sb.AppendLine("Content-Length: " + contentLength);
  67. sb.AppendLine("Content-Type: " + contentType + ";charset=utf-8\r\n");
  68. return Encoding.UTF8.GetBytes(sb.ToString());
  69. }
  70. }
     第五步——DataConnection类中接收响应头并且处理静态页面:
  1. //根据请求的路径,判断是静态页面还是动态页面
  2. void Judge(string url)
  3. {
  4. //.htm
  5. string ext = Path.GetExtension(url).TrimStart('.');
  6. switch (ext.ToLower())
  7. {
  8. case "htm":
  9. case "html":
  10. case "jpg":
  11. case "js":
  12. case "gif":
  13. case "png":
  14. //处理静态页面 并作出响应
  15. ProcessStaticPage(url);
  16. break;
  17. case "aspx": //jsp php
  18. //处理动态页面
  19. ProcessDyPage(url);
  20. break;
  21. default:
  22. break;
  23. }
  24. }
  25. /// <summary>
  26. /// 根据路径找到静态页面,并作出响应
  27. /// 1 获得响应头
  28. /// </summary>
  29. /// <param name="url"></param>
  30. void ProcessStaticPage(string url)
  31. {
  32. // /1.htm
  33. //1、根据相对路径生成绝对路径
  34. //1.1、获取exe所在文件夹的路径
  35. string exePath = AppDomain.CurrentDomain.BaseDirectory;
  36. //1.2、请求文件的绝对路径
  37. string path = exePath + "Web\\" + url;
  38. //2、判断文件是否存在,读取静态页面
  39. if (File.Exists(path))
  40. {
  41. using (FileStream fs = new FileStream(path, FileMode.Open))
  42. {
  43. //2.1、读取文件
  44. byte[] buffer = new byte[fs.Length];
  45. fs.Read(buffer, 0, buffer.Length);
  46. //2.2、生成响应头
  47. Response response = new Response(200, buffer.Length, url);
  48. //注意:send没有立即发送,会等待一个非常短的时间,等待是否还有send,如果有,一起发送过去。
  49. //2.3、发送响应头
  50. connSocket.Send(response.GetResponseHeaders());
  51. //2.4、发送响应体
  52. connSocket.Send(buffer);
  53. //2.5、关闭短连接
  54. connSocket.Close();
  55. del("关闭连接");
  56. }
  57. }
  58. else
  59. {
  60. //404文件不存在
  61. }
  62. }
     第六步——DataConnection类中处理动态页面:
  1. //处理动态页面
  2. void ProcessDyPage(string url)
  3. {
  4. NewsList list = new NewsList();
  5. //响应体
  6. byte[] buffer = list.ProcessRequest();
  7. //响应头
  8. Response res = new Response(200, buffer.Length, url);
  9. //发送响应头
  10. connSocket.Send(res.GetResponseHeaders());
  11. //发送响应体
  12. connSocket.Send(buffer);
  13. //关闭短连接
  14. connSocket.Close();
  15. del("关闭连接了");
  16. }
  17. 此方法,只要请求的的页面是以.aspx结尾的,不管什么名字,都可访问得到。
  18. 因为是根据后缀名来找到页面。

     第七步——DataConnection类中处理动态页面完善:

  1. //处理动态页面
  2. void ProcessDyPage(string url)
  3. {
  4. //文件名(类名)
  5. string fileName = Path.GetFileNameWithoutExtension(url);
  6. #region 第一种处理动态页面方式
  7. 定义一个接口
  8. //IHttpHandler handler = null;
  9. 判断
  10. //if (fileName.ToLower() == "newslist")
  11. //{
  12. // //NewsList list = new NewsList();
  13. // //去实现一个接口
  14. // handler = new NewsList();
  15. //}
  16. //else if (fileName.ToLower() == "newsdetails")
  17. //{
  18. // //NewsDetails details = new NewsDetails();
  19. // //去实现一个接口
  20. // handler = new NewsDetails();
  21. //}
  22. //if (handler != null)
  23. //{
  24. // //响应体
  25. // byte[] buffer = handler.ProcessRequest();
  26. // //响应头
  27. // Response res = new Response(200, buffer.Length, url);
  28. // //发送响应头
  29. // connSocket.Send(res.GetResponseHeaders());
  30. // //发送响应体
  31. // connSocket.Send(buffer);
  32. // //关闭短连接
  33. // connSocket.Close();
  34. // del("关闭连接了");
  35. //}
  36. //else
  37. //{
  38. // //404 找不到页面
  39. //}
  40. #endregion
  41. #region 第二种处理动态页面的方式
  42. //通过反射创建负责请求类的对象,并且放到接口中
  43. //根据字符串(类的全名称:命名空间.类名)创建对应类的对象
  44. //命名空间
  45. string nameSpace =
  46. System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;
  47. //类的全名称
  48. string fullName = nameSpace + "." + fileName;
  49. //通过反射创建类的对象
  50. IHttpHandler handler = System.
  51. Reflection.Assembly.GetExecutingAssembly().CreateInstance(fullName, true) as IHttpHandler;
  52. if (handler != null)
  53. {
  54. //生成响应体
  55. byte[] buffer = handler.ProcessRequest();
  56. //响应头
  57. Response res = new Response(200, buffer.Length, url);
  58. //发送响应头
  59. connSocket.Send(res.GetResponseHeaders());
  60. //发送响应体
  61. connSocket.Send(buffer);
  62. //关闭短连接
  63. connSocket.Close();
  64. del("关闭连接了");
  65. }
  66. else
  67. {
  68. //404文件不存在
  69. }
  70. #endregion
  71. }
  72. 准备一个接口IHttpHandler:
  73. /// <summary>
  74. /// 让实现了接口的类,能够处理浏览器的请求
  75. /// </summary>
  76. interface IHttpHandler
  77. {
  78. //处理请求并做出响应
  79. byte[] ProcessRequest();
  80. }

     附上代码整个程序

     备注:写于2013年9月25日

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/216999
推荐阅读
相关标签
  

闽ICP备14008679号