• 自己写的Web服务器


    自己写的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     }
     
     
    分类: C#Socket编程
    标签: SocketWeb服务器Http
  • 相关阅读:
    在Intellij idea 2017中运行tomcat 8.5
    Servlet技术之服务器的安装和配置
    Servlet&&Jsp 概述
    linux 下 tomcat 安装
    执行数据库的更新操作
    JDBC
    Mysql 命令
    hdoj2036 改革春风吹满地——叉积
    常规设置
    pytorch本地安装
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3047137.html
Copyright © 2020-2023  润新知