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 #endregion 116 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 #endregion 135 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 #endregion 170 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 /// 輸出狀態404 185 /// </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 #endregion 194 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 ISocketPool 2 { 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 : ISocketPool 11 { 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> </div> 23 <div> </div> 24 <div> </div> 25 <div> </div> 26 <div> </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內存消耗和時間消耗問題。
測試看看吧。