当前位置: 代码迷 >> C# >> C# 利用socekt做到http监听,如何样才能做到高性能
  详细解决方案

C# 利用socekt做到http监听,如何样才能做到高性能

热度:86   发布时间:2016-05-05 04:12:46.0
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内存消耗和时间消耗问题。

测试看看吧。

  相关解决方案