你的位置:首页 > ASP.net教程

[ASP.net教程]C# 利用socekt做到http监听,怎么样才能做到高性能


c#原始提供了http的监听的类HttpListener,实现了简单的http。文章地址《C# 控制台或者winform程序开启http的监听状态》

但是经过我测试,这个HttpListener提供的真的就只是简单的http监听功能,无法实现高并发处理。

不知道是我处理问题还是其他什么原因,无法实现,当上一个http请求连接尚未关闭的情况下,即便是把请求放到另外一个线程执行,都要等到处理结束,close了才能接受和处理下一次的连接请求。

也许你会说HttpListener不是提供了异步监听的嘛?异步不就可以类使用多线程实现嘛。但是经过我测试,确实没有得到我想要的实际效果。

所以另辟蹊径。http其实质就是socket的tcp封装实现的功能,单次请求,处理,关闭的socket功能。

所以这里找到了可以使用最原始的socket的来提供http监听,处理数据,关闭状态。

好了直接上代码,,一下代码部分来至于博客园,园友帖子提供,时间久远亦不知道是哪位仁兄的帖子,见谅。

 1  internal class HttpServer 2   { 3     private IPEndPoint _IP; 4     private TcpListener _Listeners; 5     private volatile bool IsInit = false; 6     HashSet<string> Names; 7  8     /// <summary> 9     /// 初始化服务器 10     /// </summary> 11     public HttpServer(string ip, int port, HashSet<string> names) 12     { 13       IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(ip), port); 14       this._IP = localEP; 15       Names = names; 16       if (Names == null) 17       { 18         Names = new HashSet<string>(); 19       } 20       try 21       { 22         foreach (var item in names) 23         { 24           Console.WriteLine(string.Format(System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff:") + "Start Listen Http Socket -> {0}:{1}{2} ", ip, port, item)); 25         } 26         this._Listeners = new TcpListener(IPAddress.Parse(ip), port); 27         this._Listeners.Start(5000); 28         IsInit = true; 29         this.AcceptAsync(); 30       } 31       catch (Exception ex) 32       { 33         Console.WriteLine(ex); 34         this.Dispose(); 35       } 36     } 37  38     private void AcceptAsync() 39     { 40       try 41       { 42         this._Listeners.BeginAcceptTcpClient(new AsyncCallback(AcceptAsync_Async), null); 43       } 44       catch (Exception) { } 45     } 46  47     private void AcceptAsync_Async(IAsyncResult iar) 48     { 49       this.AcceptAsync(); 50       try 51       { 52         TcpClient client = this._Listeners.EndAcceptTcpClient(iar); 53         var socket = new HttpClient(client); 54         Console.WriteLine(System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff:") + "Create Http Socket Remote Socket LocalEndPoint:" + client.Client.LocalEndPoint + " RemoteEndPoint:" + client.Client.RemoteEndPoint.ToString()); 55         foreach (var item in Names) 56         { 57           if (socket.http_url.StartsWith(item)) 58           { 59             try 60             { 61               socket.process(); 62               return; 63             } 64             catch { break; } 65           } 66         } 67         socket.WriteFailure(); 68         socket.Close(); 69       } 70       catch (Exception) { } 71     } 72  73     /// <summary> 74     /// 释放资源 75     /// </summary> 76     public void Dispose() 77     { 78       if (IsInit) 79       { 80         IsInit = false; 81         this.Dispose(true); 82         GC.SuppressFinalize(this); 83       } 84     } 85  86     /// <summary> 87     /// 释放所占用的资源 88     /// </summary> 89     /// <param name="flag1"></param> 90     protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1) 91     { 92       if (flag1) 93       { 94         if (_Listeners != null) 95         { 96           try 97           { 98             Console.WriteLine(string.Format("Stop Http Listener -> {0}:{1} ", this.IP.Address.ToString(), this.IP.Port)); 99             _Listeners.Stop();100             _Listeners = null;101           }102           catch { }103         }104       }105     }106 107     /// <summary>108     /// 获取绑定终结点109     /// </summary>110     public IPEndPoint IP { get { return this._IP; } }111   }

这个是实现socket监听状态

 1 public class HttpClient 2   { 3     private static int MAX_POST_SIZE = 10 * 1024 * 1024; // 10MB 4     private const int BUF_SIZE = 4096; 5     private Stream inputStream; 6     public StreamWriter OutputStream; 7     public String http_method; 8     public String http_url; 9     public String http_protocol_versionstring; 10     public Hashtable httpHeaders = new Hashtable(); 11     internal TcpClient _Socket; 12  13     /// <summary> 14     /// 这个是服务器收到有效链接初始化 15     /// </summary> 16     internal HttpClient(TcpClient client) 17     { 18       this._Socket = client; 19       inputStream = new BufferedStream(_Socket.GetStream()); 20       OutputStream = new StreamWriter(new BufferedStream(_Socket.GetStream()), UTF8Encoding.Default); 21       ParseRequest(); 22     } 23  24     internal void process() 25     { 26       try 27       { 28         if (http_method.Equals("GET")) 29         { 30           Program.Pool.ActiveHttp(this, GetRequestExec()); 31         } 32         else if (http_method.Equals("POST")) 33         { 34           Program.Pool.ActiveHttp(this, PostRequestExec()); 35         } 36       } 37       catch (Exception e) 38       { 39         Console.WriteLine("Exception: " + e.ToString()); 40         WriteFailure(); 41       } 42     } 43  44     public void Close() 45     { 46       OutputStream.Flush(); 47       inputStream.Dispose(); 48       inputStream = null; 49       OutputStream.Dispose(); 50       OutputStream = null; // bs = null;       51       this._Socket.Close(); 52     } 53  54     #region 读取流的一行 private string ReadLine() 55     /// <summary> 56     /// 读取流的一行 57     /// </summary> 58     /// <returns></returns> 59     private string ReadLine() 60     { 61       int next_char; 62       string data = ""; 63       while (true) 64       { 65         next_char = this.inputStream.ReadByte(); 66         if (next_char == '\n') { break; } 67         if (next_char == '\r') { continue; } 68         if (next_char == -1) { Thread.Sleep(1); continue; }; 69         data += Convert.ToChar(next_char); 70       } 71       return data; 72     } 73     #endregion 74  75     #region 转化出 Request private void ParseRequest() 76     /// <summary> 77     /// 转化出 Request 78     /// </summary> 79     private void ParseRequest() 80     { 81       String request = ReadLine(); 82       if (request != null) 83       { 84         string[] tokens = request.Split(' '); 85         if (tokens.Length != 3) 86         { 87           throw new Exception("invalid http request line"); 88         } 89         http_method = tokens[0].ToUpper(); 90         http_url = tokens[1]; 91         http_protocol_versionstring = tokens[2]; 92       } 93       String line; 94       while ((line = ReadLine()) != null) 95       { 96         if (line.Equals("")) 97         { 98           break; 99         }100         int separator = line.IndexOf(':');101         if (separator == -1)102         {103           throw new Exception("invalid http header line: " + line);104         }105         String name = line.Substring(0, separator);106         int pos = separator + 1;107         while ((pos < line.Length) && (line[pos] == ' '))108         {109           pos++;//过滤键值对的空格110         }111         string value = line.Substring(pos, line.Length - pos);112         httpHeaders[name] = value;113       }114     }115     #endregion116 117     #region 读取Get数据 private Dictionary<string, string> GetRequestExec()118     /// <summary>119     /// 读取Get数据120     /// </summary>121     /// <returns></returns>122     private Dictionary<string, string> GetRequestExec()123     {124       Dictionary<string, string> datas = new Dictionary<string, string>();125       int index = http_url.IndexOf("?", 0);126       if (index >= 0)127       {128         string data = http_url.Substring(index + 1);129         datas = getData(data);130       }131       WriteSuccess();132       return datas;133     }134     #endregion135 136     #region 读取提交的数据 private void handlePOSTRequest()137     /// <summary>138     /// 读取提交的数据139     /// </summary>140     private Dictionary<string, string> PostRequestExec()141     {142       int content_len = 0;143       MemoryStream ms = new MemoryStream();144       if (this.httpHeaders.ContainsKey("Content-Length"))145       {146         //内容的长度147         content_len = Convert.ToInt32(this.httpHeaders["Content-Length"]);148         if (content_len > MAX_POST_SIZE) { throw new Exception(String.Format("POST Content-Length({0}) 对于这个简单的服务器太大", content_len)); }149         byte[] buf = new byte[BUF_SIZE];150         int to_read = content_len;151         while (to_read > 0)152         {153           int numread = this.inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read));154           if (numread == 0)155           {156             if (to_read == 0) { break; }157             else { throw new Exception("client disconnected during post"); }158           }159           to_read -= numread;160           ms.Write(buf, 0, numread);161         }162         ms.Seek(0, SeekOrigin.Begin);163       }164       WriteSuccess();165       StreamReader inputData = new StreamReader(ms);166       string data = inputData.ReadToEnd();167       return getData(data);168     }169     #endregion170 171     #region 输出状态172     /// <summary>173     /// 输出200状态174     /// </summary>175     public void WriteSuccess()176     {177       OutputStream.WriteLine("HTTP/1.0 200 OK");178       OutputStream.WriteLine("Content-Type: text/html");179       OutputStream.WriteLine("Connection: close");180       OutputStream.WriteLine("");181     }182 183     /// <summary>184     /// 输出状态404185     /// </summary>186     public void WriteFailure()187     {188       OutputStream.WriteLine("HTTP/1.0 404 File not found");189       OutputStream.WriteLine("Content-Type: text/html");190       OutputStream.WriteLine("Connection: close");191       OutputStream.WriteLine("");192     }193     #endregion194 195     /// <summary>196     /// 分析http提交数据分割197     /// </summary>198     /// <param name="rawData"></param>199     /// <returns></returns>200     private static Dictionary<string, string> getData(string rawData)201     {202       var rets = new Dictionary<string, string>();203       string[] rawParams = rawData.Split('&');204       foreach (string param in rawParams)205       {206         string[] kvPair = param.Split('=');207         string key = kvPair[0];208         string value = HttpUtility.UrlDecode(kvPair[1]);209         rets[key] = value;210       }211       return rets;212     }213   }

实现了对http数据请求处理

 

1 public interface ISocketPool2   {3     /// <summary>4     /// 5     /// </summary>6     /// <param name="client"></param>7     void ActiveHttp(Fly.Network.SocketPool.Http.HttpClient client, Dictionary<string, string> parms);8   }

 

 1 public class Program 2   { 3     public static MessagePool Pool = new MessagePool(); 4     static void Main(string[] args) 5     { 6       HttpServer https = new HttpServer("127.0.0.1", 80, new HashSet<string>() {"/test/","/flie/" }); 7       Console.ReadLine(); 8     } 9   }10   class MessagePool : ISocketPool11   {12     public void ActiveHttp(HttpClient client, Dictionary<string, string> parms)13     {14       Thread.Sleep(new Random().Next(0, 3000));15       foreach (var item in parms)16       {17         Console.WriteLine(DateTime.Now.NowString() + "item.Key:" + item.Key + "; item.Value:" + item.Value);18       }19       string strHtml = @"20 <html><head></head>21 <body>22 <div>&nbsp;</div>23 <div>&nbsp;</div>24 <div>&nbsp;</div>25 <div>&nbsp;</div>26 <div>&nbsp;</div>27 {0}28 </body>29 </html>30 ";31       client.OutputStream.WriteLine(string.Format(strHtml, DateTime.Now.NowString() + "xxxxxxxxxxx"));32       client.Close();33     }34   }

程序启动过后,看到输出

2015-04-13 16:23:21:059:Start Listen Http Socket -> 127.0.0.1:80/test/2015-04-13 16:23:21:069:Start Listen Http Socket -> 127.0.0.1:80/flie/

 

接下来我们在浏览器输入 127.0.0.1/test/

 

正常收到请求,输出程序

127.0.0.1/test/

这里test1这个并不是我们监听饿目录,根本不会处理,

 

接下来我们再看看这个效果 get提交的参数 127.0.0.1/test/?bb=test

输出了get提交过来的参数信息。可能你会奇怪,为什么一次请求会收到两次连接请求。这里我查看过了其中一次请求是浏览器自带的请求页面标签的icon连接请求,

如果你拷贝了程序,你现在可以实现跑来程序,然后输入网址,按着F5不放,看看服务器的承受能力,当然这里忽律了逻辑方面对cpu内存消耗和时间消耗问题。

测试看看吧。