欢迎光临
我们一直在努力

一步步搭建自己的web服务器

IIS或者其他Web服务器究竟做了哪些工作,让浏览器请求一个URL地址后显示一个漂亮的网页?要想弄清这个疑问,我想我们可以自己写一个简单的web服务器。

思路:

  1. 创建socket监听浏览器请求。
  2. 连接成功,接受浏览器的请求数据。
  3. 响应浏览器的请求。(我们只响应静态文件) 。

好的,思路很清晰。下面就跟着我动手一步步用代码实现。

1、创建socket监听浏览器请求。

  当浏览器请求域名,DNS将域名解析为IP地址。带着缺省的端口80访问我们的服务器。所以Socket的监听,也需要绑定IP和端口。

代码:

// 窗体加载 private void Form1_Load(object sender, EventArgs e) { // 关闭线程操作检测 Control.CheckForIllegalCrossThreadCalls = false; Log("服务器启动中 >>>"); IPAddress ip = IPAddress.Parse(txtIP.Text); IPEndPoint p = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); socketWatch.Bind(p); socketWatch.Listen(10); // 为了不阻塞主线程,开启后台线程,监听浏览器请求。 Thread t = new Thread(Listen); t.IsBackground = true; t.Start(); Log("服务器启动成功。"); }

Listen 进程只需要无限循环接受浏览器发送过来的请求。

private void Listen()

{

while (true)

{

Socket sender=socketWatch.Accept();

Log(sender.RemoteEndPoint.Tostring() + "连接成功");

       }

}

辅助方法,显示提示信息。(不重要)

// 显示提示信息 private void Log(string msg) { txtMsg.AppendText(msg + "\r\n"); }

执行结果

打开我们的winform程序。

打开浏览器访问: http://192.168.95.1:3099/  地址

就能看到以下结果:

 一步步搭建自己的web服务器

 

2、连接成功,接受浏览器的请求数据。

分析:

浏览器的请求到达服务器后,我们要接待他,就要问他是过来干嘛的。

要和浏览器交流,那么服务器和浏览器就应该说一种语言。这个语言就是http协议。

(对HTTP协议还不太了解的园友可以参考:http协议知识整理

根据HTTP协议,浏览器请求服务器的格式都是规定好的,

所以我们只需要根据格式进行拆分,获取我们感兴趣的数据。

这件事情,已经不属于form1.cs这个类的能力范围了。所以我们找一个管家HttpManager来帮我们处理。至于怎么处理form1不用管,他只需要告诉管家就可以了。

代码:

在form1.cs的Listen中创建管家

HttpManager manager = new HttpManager(sender, Log);

管家接收基础的数据后,再交给专门处理请求数据的对象HttpRequest。

class HttpManager { private Socket socket; private Action<string> Log; public HttpManager(Socket socket, Action<string> act) { this.socket = socket; this.Log = act; // 接收消息 string msg = ReceiveMsg(); Log(msg); try { // 去找请求对象,拿到我们需要的数据。 HttpRequest request = new HttpRequest(msg); } catch (Exception ex) { Log(ex.Message); } } // 接受浏览器请求报文 private string ReceiveMsg() { byte[] date = new byte[1024 * 1024]; int count = socket.Receive(date); return Encoding.UTF8.GetString(date, 0, count); } }

 

核心的HttpResponse:

class HttpRequest { // 请求原始地址 /xxx/xxx.xxxx public string RawUrl { get; set; } // 请求类型 Post/Get public string Method; public HttpRequest(string msg) { string[] lines = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); string[] arrays = lines[0].Split(' '); Method = arrays[0]; RawUrl = arrays[1]; } }

 

3、响应浏览器的请求。

 

  响应过程其实也非常简单,IO读取浏览器需要请求的数据,拼接响应报文返回给浏览器。

在HttpManager类接收完数据后,调用响应对象。来帮我们处理响应。

// 构造响应报文,响应请求。 HttpResponse response = new HttpResponse(request.RawUrl, socket, Log);

核心HttpResponse代码

一步步搭建自己的web服务器一步步搭建自己的web服务器

 1 class HttpResponse  2 {  3 public int StatusCode { get; set; }  4 public Dictionary<int, string> Dic = new Dictionary<int, string>();  5 public string ContentType { get; set; }  6 public int ContentLength { get; set; }  7 public byte[] Data { get; set; }  8 public Socket Socket { get; set; }  9 public Action<string> Log { get; set; }  10 public HttpResponse(string rawUrl, Socket socket, Action<string> log)  11  {  12 this.Socket = socket;  13  FillDic();  14 this.Log = log;  15 // 判断是否为静态资源  16 if (Judge(rawUrl))  17  {  18 // IO 读取资源  19 Data = ReadFile(rawUrl);  20 if (Data == null)  21  {  22 // 404 没有这个文件  23 StatusCode = 404;  24 string msg = "没有找到这个文件。";  25 ContentType = "text/html";  26 Data = Encoding.Default.GetBytes(msg);  27 ContentLength = Data.Length;  28 ContentType = "text/html";  29  SendData();  30  }  31 else  32  {  33 ContentLength = Data.Length;  34 StatusCode = 200;  35 ContentType = GetContentType(rawUrl);  36 // 响应请求  37  SendData();  38  }  39  }  40 else  41  {  42 // 不处理  43 string msg = "只处理静态文件。";  44 Data = Encoding.Default.GetBytes(msg);  45 ContentLength = Data.Length;  46 StatusCode = 500;  47 ContentType = "text/html";  48 // 响应请求  49  SendData();  50  }  51  52  53  }  54  55  56 // 判断文件是否为静态资源  57 private bool Judge(string rawUrl)  58  {  59 bool isStatic = false;  60 string ext = Path.GetExtension(rawUrl);  61 switch (ext)  62  {  63 case ".html":  64 case ".htm":  65 case ".jpg":  66 case ".png":  67 case ".gif":  68 case ".js":  69 case ".css":  70 case ".ico": isStatic = true; break;  71  }  72 return isStatic;  73  }  74  75 // 响应  76 private void SendData()  77  {  78 StringBuilder sb = new System.Text.StringBuilder();  79 sb.Append("HTTP/1.1 " + StatusCode + " " + Dic[StatusCode] + "\r\n");  80 sb.Append("Server: ksn/1.0\r\n");  81 sb.Append("Content-Type: " + ContentType + "\r\n");  82 sb.Append("Content-Length: " + ContentLength + "\r\n");  83 sb.Append("\r\n");  84 Log("响应报文: \r\n" + sb.ToString());  85  86 byte[] head = Encoding.UTF8.GetBytes(sb.ToString());  87 List<byte> res = new List<byte>();  88  res.AddRange(head);  89 if (this.Data != null)  90  {  91 res.AddRange(this.Data);  92  }  93  Socket.Send(res.ToArray());  94  95  }  96  97  98 private string GetContentType(string rawUrl)  99  { 100 string ct = "text/html"; 101 string ext = Path.GetExtension(rawUrl); 102 switch (ext) 103  { 104 case ".js": ct = "text/javascript"; 105 break; 106 case ".css": ct = "text/css"; 107 break; 108 case ".jpg": ct = "image/jpeg"; 109 break; 110 case ".png": ct = "image/png"; 111 break; 112 case ".gif": ct = "image/gif"; 113 break; 114 case ".ico": ct = "image/x-ico"; 115 break; 116  } 117 return ct; 118  } 119 120 // 初始化状态码 121 private void FillDic() 122  { 123 Dic.Add(200, "Ok"); 124 Dic.Add(404, "Not Found"); 125 Dic.Add(500, "Internal Error"); 126  } 127 128 // 读取请求的资源 129 private byte[] ReadFile(string rawUrl) 130  { 131 string path = AppDomain.CurrentDomain.BaseDirectory; 132 path = path + "web" + rawUrl; 133 if (File.Exists(path)) 134  { 135 using (FileStream fs = new FileStream(path, FileMode.Open)) 136  { 137 byte[] data = new byte[fs.Length]; 138 fs.Read(data, 0, data.Length); 139 return data; 140  } 141  } 142 else 143  { 144 return null; 145  } 146 147 148  } 149 }

View Code

 

  这个类的代码比较多,但是不难,都是按照顺序一步步往下走的。其实web服务器大致就做了这些事情。只不过封装的东西更多,功能更完善。

资源:

项目源码下载地址

  • 海报
海报图正在生成中...
赞(0) 打赏
声明:
1、本博客不从事任何主机及服务器租赁业务,不参与任何交易,也绝非中介。博客内容仅记录博主个人感兴趣的服务器测评结果及一些服务器相关的优惠活动,信息均摘自网络或来自服务商主动提供;所以对本博客提及的内容不作直接、间接、法定、约定的保证,博客内容也不具备任何参考价值及引导作用,访问者需自行甄别。
2、访问本博客请务必遵守有关互联网的相关法律、规定与规则;不能利用本博客所提及的内容从事任何违法、违规操作;否则造成的一切后果由访问者自行承担。
3、未成年人及不能独立承担法律责任的个人及群体请勿访问本博客。
4、一旦您访问本博客,即表示您已经知晓并接受了以上声明通告。
文章名称:《一步步搭建自己的web服务器》
文章链接:https://www.456zj.com/536.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址