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內存消耗和時間消耗問題。
測試看看吧。