自己寫的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服務器(續)》列出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM