自己写的Web服务器

  自己写一个使用Http协议的服务器。在谷歌搜了一下,发现其实.NET Framework里面本身提供了HttpListener类,看别人的博文介绍是它是对Socket的简单封装,也有一些人没有用这个类,还是直接用Socekt写了服务器。说是Socket的扩展性反而比较好。HttpListener毕竟是微软封装好的,安全性应该一般会比用Socket写的要高,如果大牛写的就不同了,像我这等水货,其实还是用HttpListener要好一些。但也是个尝试,也是学习,我尝试用Socket写。虽然说是基于Socket,但实际上用的Socket的连接池。连接池的实现细节在上一篇博文《Socket连接池》有介绍。

  写之前肯定看过别人的博文,看了这篇《C#开发自己的Web服务器》,是翻译老外的博文的。既然是用到Http协议,那肯定要对它有一定的了解,至少懂得看他它的请求头和响应头吧。本人了解的不多,但知道的都能在写这个程序里用得上。

  用谷歌浏览器随便获取了请求和响应的消息结构,列出来简单看一下

1 GET /page/130970/ HTTP/1.1
2 Host: kb.cnblogs.com
3 Connection: keep-alive
4 Cache-Control: max-age=0
5 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
6 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
7 Accept-Encoding: gzip,deflate,sdch
8 Accept-Language: zh-CN,zh;q=0.8
9 Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3

  这个有些部分被我删了,因为篇幅太长了。这个是GET的请求结构,写这个服务器要用到的信息只是第一行请求行而已。由于是GET请求,请求的方法是GET,请求要获取的资源就是“/page/130970”这段。暂时用到的信息就之后这两个而已。

  下面这个则是POST的请求消息结构

 1 POST /ws/SideRightList.asmx/GetList HTTP/1.1
 2 Host: kb.cnblogs.com
 3 Connection: keep-alive
 4 Content-Length: 35
 5 Accept: application/json, text/javascript, */*; q=0.01
 6 Origin: http://kb.cnblogs.com
 7 X-Requested-With: XMLHttpRequest
 8 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
 9 Content-Type: application/json; charset=UTF-8
10 Referer: http://kb.cnblogs.com/page/130970/
11 Accept-Encoding: gzip,deflate,sdch
12 Accept-Language: zh-CN,zh;q=0.8
13 Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3

  同样以POST这个请求方法作为开头,请求的资源就同样的跟在后面,但是请求的参数就跟在请求头的下两行,可是这里的没有带参数,参数的长度可以在请求头的Content-Length中获得,如这里的参数长度就是35。

 1 HTTP/1.1 200 OK
 2 Server: Tengine
 3 Date: Fri, 26 Apr 2013 05:50:11 GMT
 4 Content-Type: text/html; charset=utf-8
 5 Transfer-Encoding: chunked
 6 Connection: keep-alive
 7 Vary: Accept-Encoding
 8 Cache-Control: public, max-age=300
 9 Expires: Fri, 26 Apr 2013 05:54:43 GMT
10 Last-Modified: Fri, 26 Apr 2013 05:49:43 GMT
11 X-AspNet-Version: 4.0.30319
12 X-Powered-By: ASP.NET
13 X-UA-Compatible: IE=edge
14 Content-Encoding: gzip

当服务器接收到浏览器发送的请求处理完之后,要对浏览器进行响应,响应的消息结构如上所示,这里要提供的参数不是很多,首先是响应的状态码,下面则是状态码的类别

  • 1XX  提示信息 - 表示请求已被成功接收,继续处理
  • 2XX  成功 - 表示请求已被成功接收,理解,接受
  • 3XX  重定向 - 要完成请求必须进行更进一步的处理
  • 4XX  客户端错误 -  请求有语法错误或请求无法实现
  • 5XX  服务器端错误 -   服务器未能实现合法的请求

这里用到的状态码有三个 200 OK,404 Not Found,501 Not Implemented。

Server则是服务器的名称,Content-Length则是响应内容的长度,Content-Type是服务器定它们的响应中的内容的类型,也称为MIME,我在以往常见的是这几个

  • text/html
  • text/xml
  • text/plain
  • text/css
  • text/javascript

  HTTP的内容不讲太多了,知道这些足以写这个服务器了。在博客园的知识库了发现一篇文章讲HTTP协议的挺不错的,叫《HTTP 协议详解》。

  吹完Http协议的,说回程序了,既然请求头都是一堆字符串,那现在可以把服务器的职能一份为二,一部分是面向网络的,是Socket那部分,刚好不久前完成了Socket的连接池,在这里可以用得上了;另一部分则是对接收到的请求进行处理,字符串处理为主,充其量就加上文件的IO处理。Socket那部分完成了,只要对连接池配置,使用就行了,剩下是对请求的处理和作出响应。

  这里我也是习惯性的定义了一些实体类,好让信息存取方便些。

  第一个则是服务器的配置信息类,配置过IIS的都知道配置服务器需要IP,端口,虚拟目录,有时候需要控制并发量,起始页。这里就简单地存放了这些信息。

 

1     public class ServerConfigEntity
2     {
3         public string IP { get; set; }//IP地址
4         public int Port { get; set; }//端口号
5         public int MaxConnect { get; set; }//最大并发量
6         public string VirtualPath { get; set; }//虚拟目录
7         public string DefaultPage { get; set; }//起始页
8     }

 

  接着便是请求消息和响应消息了

 1     class RequestHeader
 2     {
 3         public string ActionName { get; set; }
 4         public string URL { get; set; }
 5         public string Host { get; set; }
 6         public string Accept { get; set; }
 7         public string Connection { get; set; }
 8         public string Accept_Language { get; set; }
 9         public string User_Agent { get; set; }
10         public string Accept_Encoding { get; set; }
11 
12         public string Form { get; set; }
13         public int Content_Length { get; set; }
14         public string Referer { get; set; }
15         public string Content_Type { get; set; }
16 
17         public static RequestHeader ConvertRequestHander(string headerStr)
18         {
19             string regActionName = "GET|POST";
20             string regURL = "(?<=GET|POST).*?(?=HTTP/1.1)";
21             string regHost = @"(?<=Host\:\s).*(?=\r\n)";
22             string regAccept = @"(?<=Accept\:\s).*(?=\r\n)";
23             string regConnection = @"(?<=Connection\:\s).*(?=\r\n)";
24             string regAcceptLanguage = @"(?<=Accept-Language\:\s).*(?=\r\n)";
25             string regUserAgent = @"(?<=User-Agent\:\s).*(?=\r\n)";
26             string regAcceptEncoding = @"(?<=Accept-Encoding\:\s).*(?=\r\n)";
27 
28             string regForm = @"(?<=\r\n\r\n).*";
29             string regConntenLength = @"(?<=Connten-Length\:\s).*(?=\r\n)";
30             string regRefere = @"(?<=Refere\:\s).*(?=\r\n)";
31             string regContentType = @"(?<=Content-Type\:\s).*(?=\r\n)";
32 
33             RequestHeader hander = new RequestHeader();
34             hander.ActionName = Regex.Match(headerStr, regActionName).Value;
35             hander.URL = Regex.Match(headerStr, regURL).Value;
36             hander.Host = Regex.Match(headerStr, regHost).Value;
37             hander.Accept = Regex.Match(headerStr, regAccept).Value;
38             hander.Connection = Regex.Match(headerStr, regConnection).Value;
39             hander.Accept_Language = Regex.Match(headerStr, regAcceptLanguage).Value;
40             hander.Accept_Encoding = Regex.Match(headerStr, regAcceptEncoding).Value;
41             hander.User_Agent = Regex.Match(headerStr, regUserAgent).Value;
42             string tempStr = Regex.Match(headerStr, regConntenLength).Value;
43             hander.Content_Length = Convert.ToInt32(tempStr == "" ? "0" : tempStr);
44             hander.Referer = Regex.Match(headerStr, regRefere).Value;
45             hander.Content_Type = Regex.Match(headerStr, regContentType).Value;
46             hander.Form = Regex.Match(headerStr, regForm).Value;
47             return hander;
48         }
49     }

  这里用到了正则去提取请求消息的相应内容,虽然前面介绍时只是提到用到一点点信息,但是以后扩展开来说不能用到其他信息的,于是一概提取了。

 1     class ResponseHeader
 2     {
 3         public string ResponseCode { get; set; }
 4         public string Server { get; set; }
 5         public int Content_Length { get; set; }
 6         public string Connection { get; set; }
 7         public string Content_Type { get; set; }
 8 
 9         public override string ToString()
10         {
11             string result = string.Empty;
12             result += "HTTP/1.1 " + this.ResponseCode + "\r\n";
13             result += "Server: "+this.Server+"\r\n";
14             result += "Content-Length: " + this.Content_Length + "\r\n";
15             result += "Connection: "+this.Connection+"\r\n";
16             result += "Content-Type: " + this.Content_Type + "\r\n\r\n";
17             return result;
18         }
19 
20         public string CreateErrorHtml()
21         {
22             string html = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html;charset=utf-8""></head><body>{0}</body></html>";
23             html = html.Replace("{0}", "<h2>My Web Server</h2><div>{0}</div>");
24             html = string.Format(html, this.ResponseCode);
25             return html;
26         }
27     }

  响应消息这里重写了基类的ToString()方法,能比较方便的根据当前响应的信息转成字符串。还提供了一个生成错误页面内容的方法CreateErrorHtml()。

       接着到服务器的类了,类里面有以下的私有字段

1         private SocketPoolController _pool;//Socket连接池
2         private Dictionary<string, string> _supportExtension;//支持的资源后缀
3         private ServerConfigEntity config;//服务器的配置信息
4         private bool _runFlag;//服务器启动标识

  类的构造函数,构造一个连接池,给支持的附上支持的后缀以及相对应的MIME。

 1         public HttpProtocolServer(ServerConfigEntity config)
 2         {
 3             this.config = config;
 4             _pool = new SocketPoolController(32768, config.MaxConnect);
 5             _supportExtension = new Dictionary<string, string>() 
 6             {
 7                 { "htm", "text/html" },
 8                 { "html", "text/html" },
 9                 { "xml", "text/xml" },
10                 { "txt", "text/plain" },
11                 { "css", "text/css" },
12                 { "png", "image/png" },
13                 { "gif", "image/gif" },
14                 { "jpg", "image/jpg" },
15                 { "jpeg", "image/jpeg" },
16                 { "zip", "application/zip"},
17                 {"js","text/javascript"},
18                 { "dll", "text/plain" }
19                 //{"aspx","text/html"}
20             };
21             _runFlag = false;
22         }

外放的方法有两个,分别是启动服务器RunServer()和停止服务器StopServer()

  启动服务器要把连接池运行起来,同时给注册一个接收事件,以便在接收到浏览器的请求时做出响应。

1         public void RunServer()
2         {
3             if (_runFlag) return;
4             _pool.OnReceive += new SocketPoolController.RecevieHandler(HandleRequest);
5             _pool.RunPool(config.IP, config.Port);
6             _runFlag = true;
7         }

       关闭服务器只需要把连接池关闭了就行了

1         public void StopServer()
2         {
3             _pool.StopPool();
4             _pool = null;
5             _runFlag = false;
6         }

接收到浏览器请求后处理的方法(也就是跟连接池注册的方法)如下

 1         private void HandleRequest(string uid, string header)
 2         {
 3             RequestHeader request = RequestHeader.ConvertRequestHander(header);
 4             ResponseHeader response = new ResponseHeader();
 5             response.Server = "My Test WebSite";
 6             response.Connection = "close";
 7 
 8             //暂时只支持POST和GET的请求,其他的请求就视为未实现,发501响应
 9             if (request.ActionName != "GET" && request.ActionName != "POST")
10             {
11                 response.ResponseCode = "501 Not Implemented";
12                 response.Content_Type = "text/html";
13                 SendErrorResponse(uid, response);
14                 return;
15             }
16 
17             //对请求资源名称经行处理。主要是去除GET时带的参数,还有把斜杠换过来
18             string fullURL = config.VirtualPath + request.URL;
19             string fileName =(fullURL.Contains('?')? Regex.Match(fullURL, @".*(?=\?)").Value:fullURL).Replace('/','\\');
20 
21             //如果请求的只是一个斜杠的,那证明请求的是默认页面
22             if (fileName == fullURL + "\\")
23             {
24                 //如果配置中有默认页的,发200的响应
25                 string defaultFile = Path.Combine(config.VirtualPath, config.DefaultPage);
26                 if (File.Exists(defaultFile))
27                 {
28                     response.Content_Type = "text/html";
29                     response.ResponseCode = "200 OK";
30                     SendResponse(uid, File.ReadAllText(defaultFile), response);
31                     return;
32                 }
33                 //如果不存在的,当404处理了
34                 else
35                 {
36                     response.ResponseCode = "404 Not Found";
37                     response.Content_Type = "text/html";
38                     SendErrorResponse(uid, response);
39                     return;
40                 }
41             }
42 
43             //如果请求的资源不存在的,那就发送404
44             FileInfo fileInfo = new FileInfo(fileName);
45             if (!fileInfo.Exists)
46             {
47                 response.ResponseCode = "404 Not Found";
48                 response.Content_Type = "text/html";
49                 SendErrorResponse(uid, response);
50                 return;
51             }
52 
53             //如果请求的资源不在支持的范围内,也当作404了,感觉不是404的,貌似是403的
54             string extension = fileInfo.Extension.TrimStart('.');
55             if (string.IsNullOrEmpty(extension) || !_supportExtension.ContainsKey(extension))
56             {
57                 response.ResponseCode = "404 Not Found";
58                 response.Content_Type = "text/html";
59                 SendErrorResponse(uid, response);
60                 return;
61             }
62 
63             //既然也不是请求起始页的,也没发生上面列的错误的,就正常响应
64             response.Content_Type = _supportExtension[extension];
65             response.ResponseCode = "200 OK";
66             FileStream fs =null;
67             try
68             {
69                 fs = File.OpenRead(fileInfo.FullName);
70                 byte[] datas = new byte[fileInfo.Length];
71                 fs.Read(datas, 0, datas.Length);
72                 SendResponse(uid, datas, response);
73             }
74             finally
75             {
76                 fs.Close();
77                 fs.Dispose();
78                 fs = null;
79             }
80             return;
81         }

发送消息的方法有三个,都是内部方法

  • SendResponse(string uid,string content,ResponseHeader header)是原始版的,发送普通的响应。
  • SendResponse(string uid, byte[] content, ResponseHeader header)是重载过的,专门发送内容已经是byte[]的响应。
  • SendErrorResponse(string uid,ResponseHeader header)用于专门发送错误响应的,其实内部也是调用了SendResponse(string uid,string content,ResponseHeader header)方法。从发送消息的情况来看,总共利用Socket发了两次数据,第一次是发响应消息,第二次才是发响应内容。
 1         private void SendErrorResponse(string uid,ResponseHeader header)
 2         {
 3             string errorPageContent = header.CreateErrorHtml();
 4             header.Content_Length = errorPageContent.Length;
 5             SendResponse(uid, errorPageContent, header);
 6         }
 7 
 8         private void SendResponse(string uid,string content,ResponseHeader header)
 9         {
10             header.Content_Length = content.Length;
11             _pool.SendMessage(uid, header.ToString());
12             _pool.SendMessage(uid, content);
13         }
14 
15         private void SendResponse(string uid, byte[] content, ResponseHeader header)
16         {
17             header.Content_Length = content.Length;
18             _pool.SendMessage(uid, header.ToString());
19             _pool.SendMessage(uid, content);
20         }

 

  这个简单的Web服务器就完成了,测试了一下,发现效率比不上IIS,普通浏览一个页面察觉不出来,当下载几个文件时就有差别了,IIS的用了接近2秒的时间,而这个服务器去用了接近四秒的时间。不知是哪里慢了,可能是连接池处理得不好。下面提供了源码,要用到连接池的,连接池的代码这里不提供了,需的话可以在上一篇博文《Socket连接池》里获取好了,做这个Web服务器是为了抛砖引玉,不足的,错的,遗漏的东西还很多,希望各位园友多多指点,谢谢!

整份源码
  1    class RequestHeader
  2     {
  3         public string ActionName { get; set; }
  4         public string URL { get; set; }
  5         public string Host { get; set; }
  6         public string Accept { get; set; }
  7         public string Connection { get; set; }
  8         public string Accept_Language { get; set; }
  9         public string User_Agent { get; set; }
 10         public string Accept_Encoding { get; set; }
 11 
 12         public string Form { get; set; }
 13         public int Content_Length { get; set; }
 14         public string Referer { get; set; }
 15         public string Content_Type { get; set; }
 16 
 17         public static RequestHeader ConvertRequestHander(string headerStr)
 18         {
 19             string regActionName = "GET|POST";
 20             string regURL = "(?<=GET|POST).*?(?=HTTP/1.1)";
 21             string regHost = @"(?<=Host\:\s).*(?=\r\n)";
 22             string regAccept = @"(?<=Accept\:\s).*(?=\r\n)";
 23             string regConnection = @"(?<=Connection\:\s).*(?=\r\n)";
 24             string regAcceptLanguage = @"(?<=Accept-Language\:\s).*(?=\r\n)";
 25             string regUserAgent = @"(?<=User-Agent\:\s).*(?=\r\n)";
 26             string regAcceptEncoding = @"(?<=Accept-Encoding\:\s).*(?=\r\n)";
 27 
 28             string regForm = @"(?<=\r\n\r\n).*";
 29             string regConntenLength = @"(?<=Connten-Length\:\s).*(?=\r\n)";
 30             string regRefere = @"(?<=Refere\:\s).*(?=\r\n)";
 31             string regContentType = @"(?<=Content-Type\:\s).*(?=\r\n)";
 32 
 33             RequestHeader hander = new RequestHeader();
 34             hander.ActionName = Regex.Match(headerStr, regActionName).Value;
 35             hander.URL = Regex.Match(headerStr, regURL).Value;
 36             hander.Host = Regex.Match(headerStr, regHost).Value;
 37             hander.Accept = Regex.Match(headerStr, regAccept).Value;
 38             hander.Connection = Regex.Match(headerStr, regConnection).Value;
 39             hander.Accept_Language = Regex.Match(headerStr, regAcceptLanguage).Value;
 40             hander.Accept_Encoding = Regex.Match(headerStr, regAcceptEncoding).Value;
 41             hander.User_Agent = Regex.Match(headerStr, regUserAgent).Value;
 42             string tempStr = Regex.Match(headerStr, regConntenLength).Value;
 43             hander.Content_Length = Convert.ToInt32(tempStr == "" ? "0" : tempStr);
 44             hander.Referer = Regex.Match(headerStr, regRefere).Value;
 45             hander.Content_Type = Regex.Match(headerStr, regContentType).Value;
 46             hander.Form = Regex.Match(headerStr, regForm).Value;
 47             return hander;
 48         }
 49     }
 50 
 51     class ResponseHeader
 52     {
 53         public string ResponseCode { get; set; }
 54         public string Server { get; set; }
 55         public int Content_Length { get; set; }
 56         public string Connection { get; set; }
 57         public string Content_Type { get; set; }
 58 
 59         public override string ToString()
 60         {
 61             string result = string.Empty;
 62             result += "HTTP/1.1 " + this.ResponseCode + "\r\n";
 63             result += "Server: "+this.Server+"\r\n";
 64             result += "Content-Length: " + this.Content_Length + "\r\n";
 65             result += "Connection: "+this.Connection+"\r\n";
 66             result += "Content-Type: " + this.Content_Type + "\r\n\r\n";
 67             return result;
 68         }
 69 
 70         public string CreateErrorHtml()
 71         {
 72             string html = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html;charset=utf-8""></head><body>{0}</body></html>";
 73             html = html.Replace("{0}", "<h2>My Web Server</h2><div>{0}</div>");
 74             html = string.Format(html, this.ResponseCode);
 75             return html;
 76         }
 77     }
 78 
 79     public class ServerConfigEntity
 80     {
 81         public string IP { get; set; }
 82         public int Port { get; set; }
 83         public int MaxConnect { get; set; }
 84         public string VirtualPath { get; set; }
 85         public string DefaultPage { get; set; }
 86     }
 87 
 88     public class HttpProtocolServer
 89     {
 90         private SocketPoolController _pool;
 91         private Dictionary<string, string> _supportExtension;
 92         private ServerConfigEntity config;
 93         private bool _runFlag;
 94 
 95         public HttpProtocolServer(ServerConfigEntity config)
 96         {
 97             this.config = config;
 98             _pool = new SocketPoolController(32768, config.MaxConnect);
 99             _supportExtension = new Dictionary<string, string>() 
100             {
101                 { "htm", "text/html" },
102                 { "html", "text/html" },
103                 { "xml", "text/xml" },
104                 { "txt", "text/plain" },
105                 { "css", "text/css" },
106                 { "png", "image/png" },
107                 { "gif", "image/gif" },
108                 { "jpg", "image/jpg" },
109                 { "jpeg", "image/jpeg" },
110                 { "zip", "application/zip"},
111                 {"js","text/javascript"},
112                 { "dll", "text/plain" },
113                 {"aspx","text/html"}
114             };
115             _runFlag = false;
116         }
117 
118         public void RunServer()
119         {
120             if (_runFlag) return;
121             _pool.OnReceive += new SocketPoolController.RecevieHandler(HandleRequest);
122             _pool.RunPool(config.IP, config.Port);
123             _runFlag = true;
124         }
125 
126         public void StopServer()
127         {
128             _pool.StopPool();
129             _pool = null;
130             _runFlag = false;
131         }
132 
133         private void HandleRequest(string uid, string header)
134         {
135             RequestHeader request = RequestHeader.ConvertRequestHander(header);
136             ResponseHeader response = new ResponseHeader();
137             response.Server = "My Test WebSite";
138             response.Connection = "close";
139 
140             //暂时只支持POST和GET的请求,其他的请求就视为未实现,发501响应
141             if (request.ActionName != "GET" && request.ActionName != "POST")
142             {
143                 response.ResponseCode = "501 Not Implemented";
144                 response.Content_Type = "text/html";
145                 SendErrorResponse(uid, response);
146                 return;
147             }
148 
149             //对请求资源名称经行处理。主要是去除GET时带的参数,还有把斜杠换过来
150             string fullURL = config.VirtualPath + request.URL;
151             string fileName =(fullURL.Contains('?')? Regex.Match(fullURL, @".*(?=\?)").Value:fullURL).Replace('/','\\');
152 
153             //如果请求的只是一个斜杠的,那证明请求的是默认页面
154             if (fileName == fullURL + "\\")
155             {
156                 //如果配置中有默认页的,发200的响应
157                 string defaultFile = Path.Combine(config.VirtualPath, config.DefaultPage);
158                 if (File.Exists(defaultFile))
159                 {
160                     response.Content_Type = "text/html";
161                     response.ResponseCode = "200 OK";
162                     SendResponse(uid, File.ReadAllText(defaultFile), response);
163                     return;
164                 }
165                 //如果不存在的,当404处理了
166                 else
167                 {
168                     response.ResponseCode = "404 Not Found";
169                     response.Content_Type = "text/html";
170                     SendErrorResponse(uid, response);
171                     return;
172                 }
173             }
174 
175             //如果请求的资源不存在的,那就发送404
176             FileInfo fileInfo = new FileInfo(fileName);
177             if (!fileInfo.Exists)
178             {
179                 response.ResponseCode = "404 Not Found";
180                 response.Content_Type = "text/html";
181                 SendErrorResponse(uid, response);
182                 return;
183             }
184 
185             //如果请求的资源不在支持的范围内,也当作404了,感觉不是404的,貌似是403的
186             string extension = fileInfo.Extension.TrimStart('.');
187             if (string.IsNullOrEmpty(extension) || !_supportExtension.ContainsKey(extension))
188             {
189                 response.ResponseCode = "404 Not Found";
190                 response.Content_Type = "text/html";
191                 SendErrorResponse(uid, response);
192                 return;
193             }
194 
195             //既然也不是请求起始页的,也没发生上面列的错误的,就正常响应
196             response.Content_Type = _supportExtension[extension];
197             response.ResponseCode = "200 OK";
198             FileStream fs =null;
199             try
200             {
201                 fs = File.OpenRead(fileInfo.FullName);
202                 byte[] datas = new byte[fileInfo.Length];
203                 fs.Read(datas, 0, datas.Length);
204                 SendResponse(uid, datas, response);
205             }
206             finally
207             {
208                 fs.Close();
209                 fs.Dispose();
210                 fs = null;
211             }
212             return;
213         }
214 
215         private void SendErrorResponse(string uid,ResponseHeader header)
216         {
217             string errorPageContent = header.CreateErrorHtml();
218             header.Content_Length = errorPageContent.Length;
219             SendResponse(uid, errorPageContent, header);
220         }
221 
222         private void SendResponse(string uid,string content,ResponseHeader header)
223         {
224             header.Content_Length = content.Length;
225             _pool.SendMessage(uid, header.ToString());
226             _pool.SendMessage(uid, content);
227         }
228 
229         private void SendResponse(string uid, byte[] content, ResponseHeader header)
230         {
231             header.Content_Length = content.Length;
232             _pool.SendMessage(uid, header.ToString());
233             _pool.SendMessage(uid, content);
234         }
235     }

 对本服务器的更改情况,将在下一篇博文《自己写Web服务器(续)》列出

posted @ 2013-04-27 08:25  猴健居士  阅读(6687)  评论(23编辑  收藏  举报