自己写Web服务器(续)
自己写Web服务器(续)
前几天写了篇有关写Web服务器的博文,写得不好,多亏园友们的意见,给了我继续探究的动力。这篇就关于上次做的Web服务器做了些更改。
1.支持ASPX页面的访问
多亏了园友的提点,使用了ApplicationHost类,使得宿主程序能够处理ASP.NET请求。后来上网搜了一下,原来就是写一个ASP.NET的宿主程序。上MSDN看了一下还不怎么明白,终究还是找了一些博文来看才明白。
ApplicationHost属于System.Web.Hosting命名空间,要使用这个类要添加System.Web.dll引用。先上一点代码再解释吧
1 private AspxCreator _aspxHost; 2 _aspxHost = (AspxCreator)ApplicationHost.CreateApplicationHost 3 ( typeof (AspxCreator), " / " , 4 AppDomain.CurrentDomain.BaseDirectory);
ApplicationHost通过调用CreateApplication方法构造一个Object的实例,这个实例实际上是AspxCreator类型的,CreateApplication方法有三个参数,第一个是AspxCreater的Type类型的对象,第二个是虚拟目录,一般是”/”,第三个是物理路径,这个参数跟配置在IIS配置网站是一样的。当初不明白的就是第一个参数,那个Type究竟是什么东西?其实它是一个自定义的类而已,不过这个类是继承MarshalByRefObject这个类的,通过aspx生成html的方法就定义在AspxCreater里面。还是看看AspxCreator的定义吧
1 internal class AspxCreator:MarshalByRefObject
2 {
3 public byte [] CreateAspxPage( string fileName, string qs)
4 {
5 byte [] datas= null ;
6 MemoryStream ms = null ;
7 StreamWriter sw = null ;
8 try
9 {
10 ms = new MemoryStream();
11 sw = new StreamWriter(ms, Encoding.UTF8);
12 sw.AutoFlush = true ;
13 HttpWorkerRequest worker = new ChineseRequest(fileName, qs, sw);
14 HttpRuntime.ProcessRequest(worker);
15 datas = new byte [ms.Length];
16 ms.Position = 0 ;
17 sw.BaseStream.Read(datas, 0 , datas.Length);
18 }
19 catch (Exception e)
20 {
21
22 }
23 finally
24 {
25 if (sw != null )
26 {
27 sw.Close();
28 sw.Dispose();
29 sw = null ;
30 }
31 if (ms != null )
32 {
33 ms.Close();
34 ms.Dispose();
35 ms = null ;
36 }
37 }
38 return datas;
39 }
40 }
整个类就定义了AspxCreator就只定义了一个方法,而在这个方法里,核心的就是这两行
1 HttpWorkerRequest worker = new ChineseRequest(fileName, qs, sw); 2 HttpRuntime.ProcessRequest(worker);
把一个aspx页面转成html,其余的都是流的操作。这里考虑到内容里头有中文就会造成乱码,就继承了一下SimpleWorkerRequest这个类,把里面的方法重写一下,改成UTF8的编码。使得伟大的中国方体字能正确的在浏览器中显示。
1 internal class ChineseRequest:SimpleWorkerRequest
2 {
3 private TextWriter Output;
4 public ChineseRequest( string fileName, string queryString, TextWriter textWirter)
5 : base (fileName, queryString, textWirter)
6 {
7 Output = textWirter;
8 }
9 public override void SendResponseFromMemory( byte [] data, int length)
10 {
11 Output.Write(System.Text.Encoding.UTF8.GetChars(data, 0 , length));
12 }
13 }
在浏览器发了一个ASP.NET请求时,利用之前构造的_aspxHost实例,调用CreateAspxPage方法,把相关的aspx页面转成html,通过byte[]返回回来,传到浏览器中去
1 byte [] aspxHtml = _aspxHost.CreateAspxPage(requestFile, queryString); 2 SendResponse(uid, aspxHtml, response);
结果如下图
这样就可让aspx页面脱离IIS运行了。
2.对URL进行解码
这个问题是后来自己发现的。在发送请求的URL中,如果带有中文字符的时候,一律会自动转码的,转成一串又有百分号又有字母的字符串。如果服务器单纯用正则提取了请求的URL,不对其解码的话,后果不用我说都知道了吧,之前一直没考虑这方面的问题。
想改也不难,一行代码即可,调用System.Web.HttpUtility类的UrlDecode方法,就可以得到正常的中文字符了。在虚拟目录下的确有个 “我.htm”的文件
这里有几对编码/解码的方法。
UrlDecode和UrlEncode,用于URL的解码和编码
HtmlDecode和HtmlEncode用于对Html的解码和编码
而对于js,只有编码一个方法
3.稍提高了响应的速度
我之前也说过我这个Web服务器的速度要比IIS的慢,也有园友指出我的线程没处理好,对象构造了过多,但以我现时的水平我看不出有什么对象可以削减的。我最近也查阅过有关GC的文章和一些.NET性能优化的文章。其实找到要更改的地方不多,而且本人认为响应慢的原因是我的连接池的效率问题,故找问题都在连接池里找。
最后只找到了这个地方:在接收成功绑定的方法里头,每接收完一次数据之后,我都会调用两个方法
1 buffer.FreeBuffer(unit.RecArg); 2 buffer.SetBuffer(unit.RecArg);
作用分别是释放缓冲区和设置缓冲区,方法的内部机制是先清空缓冲区的数据,把缓冲区的偏移量放入空闲栈中供下次调用,然后马上又从栈把空闲的缓冲区取出来(方法的定义在在Socket连接池一文中有源码,本文底端也有源码),这样的方法不是不好,但在这里调用先得不合适,反正都是继续用回那个缓冲区,干脆直接把缓冲区的内容清空了就可以了。
1 /// <summary>
2 /// 清除缓冲区里的数据
3 /// </summary>
4 internal void RefreshBuffer(SocketAsyncEventArgs e)
5 {
6 for ( int i = e.Offset; i < e.Offset + bufferSize; i++ )
7 {
8 if (buffers[i] == 0 ) break ;
9 buffers[i] = 0 ;
10 }
11 }
然后就调用了这个方法,不知是否这里的原因,经测试这个Web服务的速度不会比IIS慢一截了,有时还比IIS快。经过本地访问的测试和局域网内测试的结果图
其实还知道Web服务器不稳定的,线程控制那里还有问题,但现时还没找到好的解决方法,我会继续探究,有新的改动会追加到本文中去。还请各位园友多给些意见,多指点一下。谢谢。下面则是Web服务器和连接池的最新源码
Socket连接池
1 /// <summary>
2 /// 连接单元
3 /// </summary>
4 class ConnectionUnit:IDisposable
5 {
6 private string _uid; // 单元的编号,默认为-1
7 private bool _state; // 单元的状态,true表示使用,false表示空闲
8 private SocketAsyncEventArgs _sendArg; // 专用于发送
9 private SocketAsyncEventArgs _recArg; // 专用于接收
10 internal Socket client { get ; set ; } // 客户端的socket实例
11 internal List< byte > tempArray { get ; set ; } // 暂存已接收的数据,避免粘包用的
12
13 public string Uid
14 {
15 get { return _uid; }
16 set { _uid = value; }
17 }
18
19 public ConnectionUnit( string UID)
20 {
21 _uid = UID;
22 tempArray = new List< byte > ();
23 }
24
25 public ConnectionUnit() : this ( " -1 " ) { }
26
27 public ConnectionUnit( int defaultSiez)
28 {
29 _uid = " -1 " ;
30 tempArray = new List< byte > (defaultSiez);
31 }
32
33 public bool State
34 {
35 get { return _state; }
36 set { _state = value; }
37 }
38
39 public SocketAsyncEventArgs SendArg
40 {
41 get { return _sendArg; }
42 set { _sendArg = value; }
43 }
44
45 public SocketAsyncEventArgs RecArg
46 {
47 get { return _recArg; }
48 set { _recArg = value; }
49 }
50
51 public void Dispose()
52 {
53 if (_sendArg != null )
54 _sendArg.Dispose();
55 if (_recArg != null )
56 _recArg.Dispose();
57
58 _sendArg = null ;
59 _recArg = null ;
60 }
61 }
62
63 class BufferManager:IDisposable
64 {
65 private byte [] buffers;
66 private int bufferSize;
67 private int allSize;
68 private int currentIndex;
69 private Stack< int > freeIndexs;
70
71 /// <summary>
72 /// 构造缓存池
73 /// </summary>
74 /// <param name="buffersSize"> 池总大小 </param>
75 /// <param name="defaultSize"> 默认单元大小 </param>
76 internal BufferManager( int buffersSize, int defaultSize)
77 {
78 this .bufferSize= defaultSize;
79 this .allSize= buffersSize;
80 currentIndex= 0 ;
81 this .buffers = new byte [allSize];
82 freeIndexs = new Stack< int >(buffersSize/ defaultSize);
83 }
84
85 /// <summary>
86 ///
87 /// </summary>
88 /// <param name="e"></param>
89 /// <param name="offSet"></param>
90 /// <returns></returns>
91 internal bool SetBuffer(SocketAsyncEventArgs e)
92 {
93 if (freeIndexs.Count > 0 )
94 {
95 e.SetBuffer(buffers, freeIndexs.Pop(), bufferSize);
96 }
97 else
98 {
99 if ((allSize - currentIndex) < bufferSize) return false ;
100 e.SetBuffer(buffers, currentIndex, bufferSize);
101 currentIndex += bufferSize;
102 }
103 return true ;
104 }
105
106 /// <summary>
107 ///
108 /// </summary>
109 /// <param name="e"></param>
110 internal void FreeBuffer(SocketAsyncEventArgs e)
111 {
112 freeIndexs.Push(e.Offset);
113 for ( int i = e.Offset; i < e.Offset + bufferSize; i++ )
114 {
115 if (buffers[i] == 0 ) break ;
116 buffers[i] = 0 ;
117 }
118 e.SetBuffer( null , 0 , 0 );
119 }
120
121 /// <summary>
122 /// 清除缓冲区里的数据
123 /// </summary>
124 /// <param name="e"></param>
125 internal void RefreshBuffer(SocketAsyncEventArgs e)
126 {
127 for ( int i = e.Offset; i < e.Offset + bufferSize; i++ )
128 {
129 if (buffers[i] == 0 ) break ;
130 buffers[i] = 0 ;
131 }
132 }
133
134 public void Dispose()
135 {
136 buffers = null ;
137 freeIndexs = null ;
138 }
139 }
140
141 class SocketAsyncEventArgsPool:IDisposable
142 {
143 private Dictionary< string , ConnectionUnit> busyCollection;
144 private Stack<ConnectionUnit> freeCollecton;
145
146 internal SocketAsyncEventArgsPool( int maxConnect)
147 {
148 busyCollection = new Dictionary< string , ConnectionUnit> (maxConnect);
149 freeCollecton = new Stack<ConnectionUnit> (maxConnect);
150 }
151
152 /// <summary>
153 /// 取出
154 /// </summary>
155 internal ConnectionUnit Pop( string uid)
156 {
157 ConnectionUnit unit = freeCollecton.Pop();
158 unit.State = true ;
159 unit.Uid = uid;
160 busyCollection.Add(uid, unit);
161 return unit;
162 }
163
164 /// <summary>
165 /// 放回
166 /// </summary>
167 internal void Push(ConnectionUnit unit)
168 {
169 if (! string .IsNullOrEmpty(unit.Uid) && unit.Uid != " -1 " )
170 busyCollection.Remove(unit.Uid);
171 unit.Uid = " -1 " ;
172 unit.State = false ;
173 freeCollecton.Push(unit);
174 }
175
176 /// <summary>
177 /// 获取
178 /// </summary>
179 internal ConnectionUnit GetConnectionUnitByUID( string uid)
180 {
181 if (busyCollection.ContainsKey(uid))
182 return busyCollection[uid];
183 return null ;
184 }
185
186 /// <summary>
187 ///
188 /// </summary>
189 internal string [] GetOnLineList()
190 {
191 return busyCollection.Keys.ToArray();
192 }
193
194 public void Dispose()
195 {
196 foreach (KeyValuePair< string ,ConnectionUnit> item in busyCollection)
197 item.Value.Dispose();
198
199 busyCollection.Clear();
200
201 while (freeCollecton.Count > 0 )
202 freeCollecton.Pop().Dispose();
203 }
204 }
205
206 public class SocketPoolController:IDisposable
207 {
208
209 #region 字段
210
211 /// <summary>
212 /// 初始化池的互斥体
213 /// </summary>
214 private Mutex mutex = new Mutex();
215
216 /// <summary>
217 /// Accept限制信号
218 /// </summary>
219 private Semaphore semaphoreAccept;
220
221 /// <summary>
222 /// Accept信号
223 /// </summary>
224 private static ManualResetEvent acceptLock = new ManualResetEvent( false );
225
226 /// <summary>
227 /// Send信号
228 /// </summary>
229 private static ManualResetEvent sendLock = new ManualResetEvent( false );
230
231 /// <summary>
232 /// 最大并发数(连接数)
233 /// </summary>
234 private int maxConnect;
235
236 /// <summary>
237 /// 当前连接数(并发数)
238 /// </summary>
239 private int currentConnect;
240
241 /// <summary>
242 /// 缓冲区单元大小
243 /// </summary>
244 private int defaultSize;
245
246 /// <summary>
247 /// 缓冲池
248 /// </summary>
249 private BufferManager buffer;
250
251 /// <summary>
252 /// SocketasyncEventArgs池
253 /// </summary>
254 private SocketAsyncEventArgsPool pool;
255
256 /// <summary>
257 /// 服务端Socket
258 /// </summary>
259 private Socket server;
260
261 /// <summary>
262 /// 完成接受的委托
263 /// </summary>
264 public delegate void AcceptHandler( string uid);
265
266 /// <summary>
267 /// 完成发送的委托
268 /// </summary>
269 public delegate void SendHandler( string uid, string result);
270
271 /// <summary>
272 /// 完成接收的委托
273 /// </summary>
274 public delegate void RecevieHandler( string uid, string data);
275
276 /// <summary>
277 /// 完成接受事件
278 /// </summary>
279 public event AcceptHandler OnAccept;
280
281 /// <summary>
282 /// 完成发送事件
283 /// </summary>
284 public event SendHandler OnSend;
285
286 /// <summary>
287 /// 完成接收事件
288 /// </summary>
289 public event RecevieHandler OnReceive;
290
291 #endregion
292
293 #region 构造函数
294
295 /// <summary>
296 /// 构造函数
297 /// </summary>
298 /// <param name="buffersize"> 单元缓冲区大小 </param>
299 /// <param name="maxCount"> 并发总数 </param>
300 public SocketPoolController( int buffersize, int maxCount)
301 {
302 buffer = new BufferManager(buffersize * maxCount,buffersize);
303 this .currentConnect = 0 ;
304 this .maxConnect = maxCount;
305 this .currentConnect = 0 ;
306 this .defaultSize = buffersize;
307 this .pool = new SocketAsyncEventArgsPool(maxConnect);
308 // 设置并发数信号,经试验过是并发数-1才对
309 this .semaphoreAccept = new Semaphore(maxCount- 1 , maxCount- 1 );
310 InitPool();
311 }
312
313 #endregion
314
315 #region 公共方法
316
317 /// <summary>
318 /// 启动池
319 /// </summary>
320 /// <param name="ipAddress"> 服务端的IP </param>
321 /// <param name="port"> 端口 </param>
322 public void RunPool( string ipAddress, int port)
323 {
324 IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
325 server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
326 server.Bind(endpoint);
327 server.Listen( 100 );
328
329 // 调用方法异步Accept客户端的连接
330 MyAsyncAccept();
331 // 设置信号,防止再池在已经启动的情况下再次启动
332 mutex.WaitOne();
333 }
334
335 /// <summary>
336 /// 停止池
337 /// </summary>
338 public void StopPool()
339 {
340 // 把服务端的socket关了
341 if (server != null )
342 server.Close();
343 // 释放互斥信号,等待下次启动
344 mutex.ReleaseMutex();
345 // 释放资源
346 Dispose();
347 }
348
349 /// <summary>
350 /// 发送消息
351 /// </summary>
352 /// <param name="uid"></param>
353 /// <param name="message"></param>
354 public void SendMessage( string uid, string message)
355 {
356 // sendLock.Reset();
357 // ConnectionUnit unit=pool.GetConnectionUnitByUID(uid);
358 /// /如果获取不了连接单元就不发送了,
359 // if (unit == null)
360 // {
361 // if(OnSend!=null) OnSend(uid,"100");
362 // sendLock.Set();
363 // return;
364 // }
365 // byte[] datas = Encoding.ASCII.GetBytes(message);
366 // unit.SendArg.SetBuffer(datas, 0, datas.Length);
367 // unit.client.SendAsync(unit.SendArg);
368 /// /阻塞当前线程,等到发送完成才释放
369 // sendLock.WaitOne();
370 byte [] datas = Encoding.ASCII.GetBytes(message);
371 SendMessage(uid, datas);
372 }
373
374 public void SendMessage( string uid, byte [] messageByteArray)
375 {
376 sendLock.Reset();
377 ConnectionUnit unit = pool.GetConnectionUnitByUID(uid);
378 // 如果获取不了连接单元就不发送了,
379 if (unit == null )
380 {
381 if (OnSend != null ) OnSend(uid, " 100 " );
382 sendLock.Set();
383 return ;
384 }
385 unit.SendArg.SetBuffer(messageByteArray, 0 , messageByteArray.Length);
386 unit.client.SendAsync(unit.SendArg);
387 // 阻塞当前线程,等到发送完成才释放
388 Console.WriteLine( " wait-------------- " + uid);
389 sendLock.WaitOne();
390 }
391
392 #endregion
393
394 #region 异步事件回调
395
396 void SendArg_Completed( object sender, SocketAsyncEventArgs e)
397 {
398 Socket client = sender as Socket;
399 ConnectionUnit unit = e.UserToken as ConnectionUnit;
400 // 这里的消息码有三个,2字头的是成功的,1字头是不成功的
401 // 101是未知错误,100是客户端不在线
402 if (e.SocketError == SocketError.Success)
403 {
404 if (OnSend != null ) OnSend(unit.Uid, " 200 " );
405 }
406 else if (OnSend != null ) OnSend(unit.Uid, " 101 " );
407 // 释放信号,以便下次发送消息执行
408 Console.WriteLine( " set>>>>>>> " + unit.Uid);
409 sendLock.Set();
410 }
411
412 void RecArg_Completed( object sender, SocketAsyncEventArgs e)
413 {
414 Socket client = sender as Socket;
415 ConnectionUnit unit = e.UserToken as ConnectionUnit;
416 // 这里大致与上一篇异步通信的一样,只是对缓冲区的处理有一点差异
417 if (e.SocketError == SocketError.Success)
418 {
419 int rec = e.BytesTransferred;
420 if (rec == 0 )
421 {
422 CloseSocket(unit);
423 return ;
424 }
425 if (client.Available > 0 )
426 {
427 unit.tempArray.AddRange(e.Buffer);
428 // buffer.FreeBuffer(unit.RecArg);
429 // buffer.SetBuffer(unit.RecArg);
430 buffer.RefreshBuffer(unit.RecArg);
431 client.SendAsync(unit.RecArg);
432 return ;
433 }
434 byte [] data = e.Buffer;
435 int len = rec;
436 int offset = e.Offset;
437 if (unit.tempArray.Count != 0 )
438 {
439 foreach ( byte item in data)
440 {
441 if (item == 0 ) break ;
442 unit.tempArray.Add(item);
443 }
444 data = unit.tempArray.ToArray();
445 rec = data.Length;
446 offset = 0 ;
447 unit.tempArray.Clear();
448 }
449
450 string dataStr = Encoding.ASCII.GetString(data,offset,len);
451 if (OnReceive != null )
452 OnReceive(unit.Uid, dataStr);
453
454 if (!unit.State) return ;
455 // buffer.FreeBuffer(e);
456 // buffer.SetBuffer(e);
457 buffer.RefreshBuffer(e);
458 client.ReceiveAsync(e);
459 }
460 // 这里还多个了一个关闭当前连接
461 else
462 {
463 CloseSocket(unit);
464 }
465 }
466
467 void Accept_Completed( object sender, SocketAsyncEventArgs e)
468 {
469 Socket client = e.AcceptSocket;
470 try
471 {
472 if (client.Connected)
473 {
474 IPEndPoint point = client.RemoteEndPoint as IPEndPoint;
475 string uid = point.Address + " : " + point.Port;
476 ConnectionUnit unit = pool.Pop(uid);
477 unit.client = client;
478 unit.State = true ;
479 unit.Uid = uid;
480 unit.RecArg.UserToken = unit;
481 unit.SendArg.UserToken = unit;
482 buffer.SetBuffer(unit.RecArg);
483
484 // 在接受成功之后就开始接收数据了
485 client.ReceiveAsync(unit.RecArg);
486 // 设置并发限制信号和增加当前连接数
487 semaphoreAccept.WaitOne();
488 Interlocked.Increment( ref currentConnect);
489
490 if (OnAccept != null ) OnAccept(uid);
491 }
492 else if (client != null )
493 {
494 client.Close();
495 client.Dispose();
496 client = null ;
497 }
498 }
499 catch (Exception ex) { Console.WriteLine(ex.ToString()); }
500 // 设置Accept信号,以便下次Accept的执行
501 acceptLock.Set();
502 e.Dispose();
503 }
504
505 #endregion
506
507 #region 内部辅助方法
508
509 /// <summary>
510 /// 初始化SocketAsyncEventArgs池
511 /// 这里主要是给空闲栈填充足够的实例
512 /// </summary>
513 private void InitPool()
514 {
515 ConnectionUnit unit = null ;
516 for ( int i = 0 ; i < maxConnect; i++ )
517 {
518 unit = new ConnectionUnit(defaultSize);
519 unit.Uid = " -1 " ;
520 unit.RecArg = new SocketAsyncEventArgs();
521 unit.RecArg.Completed += new EventHandler<SocketAsyncEventArgs> (RecArg_Completed);
522 unit.SendArg = new SocketAsyncEventArgs();
523 unit.SendArg.Completed += new EventHandler<SocketAsyncEventArgs> (SendArg_Completed);
524 this .pool.Push(unit);
525 }
526 }
527
528 /// <summary>
529 /// 异步Accept客户端的连接
530 /// </summary>
531 void MyAsyncAccept()
532 {
533 // 这里使用Action的方式异步循环接受客户端的连接
534 // 模仿同事的做法没开线程,不知这种方式是好是坏
535 Action callback = new Action( delegate ()
536 {
537 while ( true )
538 {
539 // 每次接受都要新开一个SocketAsyncEventArgs,否则会报错
540 // 其实我也想重复利用的
541 SocketAsyncEventArgs e = new SocketAsyncEventArgs();
542 e.Completed += new EventHandler<SocketAsyncEventArgs> (Accept_Completed);
543
544 acceptLock.Reset();
545 server.AcceptAsync(e);
546 // 在异步接受完成之前阻塞当前线程
547 acceptLock.WaitOne();
548 }
549 });
550 callback.BeginInvoke( null , null );
551 }
552
553 /// <summary>
554 /// 关闭一个连接单元
555 /// </summary>
556 private void CloseSocket( ConnectionUnit unit )
557 {
558 // 关闭并释放客户端socket的字眼
559 if (unit.client != null )
560 {
561 unit.client.Shutdown(SocketShutdown.Both);
562 unit.client.Dispose();
563 unit.client = null ;
564 }
565 // Console.WriteLine(unit.Uid+" disconnect ");
566 // 把连接放回连接池
567 pool.Push(unit);
568 // 释放并发信号
569 semaphoreAccept.Release();
570 // 减少当前连接数
571 Interlocked.Decrement( ref currentConnect);
572 }
573
574 #endregion
575
576 public void Dispose()
577 {
578 if (pool != null )
579 {
580 pool.Dispose();
581 pool = null ;
582 }
583 if (buffer != null )
584 {
585 buffer.Dispose();
586 buffer = null ;
587 }
588 if (server != null )
589 {
590 server.Dispose();
591 server = null ;
592 }
593
594 }
595 }
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.URL = System.Web.HttpUtility.UrlDecode(hander.URL).Trim();
37 hander.Host = Regex.Match(headerStr, regHost).Value;
38 hander.Accept = Regex.Match(headerStr, regAccept).Value;
39 hander.Connection = Regex.Match(headerStr, regConnection).Value;
40 hander.Accept_Language = Regex.Match(headerStr, regAcceptLanguage).Value;
41 hander.Accept_Encoding = Regex.Match(headerStr, regAcceptEncoding).Value;
42 hander.User_Agent = Regex.Match(headerStr, regUserAgent).Value;
43 string tempStr = Regex.Match(headerStr, regConntenLength).Value;
44 hander.Content_Length = Convert.ToInt32(tempStr == "" ? " 0 " : tempStr);
45 hander.Referer = Regex.Match(headerStr, regRefere).Value;
46 hander.Content_Type = Regex.Match(headerStr, regContentType).Value;
47 hander.Form = Regex.Match(headerStr, regForm).Value;
48 return hander;
49 }
50 }
51
52 class ResponseHeader
53 {
54 public string ResponseCode { get ; set ; }
55 public string Server { get ; set ; }
56 public int Content_Length { get ; set ; }
57 public string Connection { get ; set ; }
58 public string Content_Type { get ; set ; }
59
60 public override string ToString()
61 {
62 string result = string .Empty;
63 result += " HTTP/1.1 " + this .ResponseCode + " \r\n " ;
64 result += " Server: " + this .Server+ " \r\n " ;
65 result += " Content-Length: " + this .Content_Length + " \r\n " ;
66 result += " Connection: " + this .Connection+ " \r\n " ;
67 result += " Content-Type: " + this .Content_Type + " \r\n\r\n " ;
68 return result;
69 }
70
71 public string CreateErrorHtml()
72 {
73 string html = @" <html><head><meta http-equiv=""Content-Type"" content=""text/html;charset=utf-8""></head><body>{0}</body></html> " ;
74 html = html.Replace( " {0} " , " <h2>My Web Server</h2><div>{0}</div> " );
75 html = string .Format(html, this .ResponseCode);
76 return html;
77 }
78 }
79
80 public class ServerConfigEntity
81 {
82 public string IP { get ; set ; }
83 public int Port { get ; set ; }
84 public int MaxConnect { get ; set ; }
85 public string VirtualPath { get ; set ; }
86 public string DefaultPage { get ; set ; }
87 }
88
89 public class HttpProtocolServer
90 {
91 private SocketPoolController _pool;
92 private Dictionary< string , string > _supportExtension;
93 private ServerConfigEntity config;
94 private bool _runFlag;
95 private AspxCreator _aspxHost;
96
97 public HttpProtocolServer(ServerConfigEntity config)
98 {
99 this .config = config;
100 _pool = new SocketPoolController( 32768 , config.MaxConnect);
101 _supportExtension = new Dictionary< string , string > ()
102 {
103 { " htm " , " text/html " },
104 { " html " , " text/html " },
105 { " xml " , " text/xml " },
106 { " txt " , " text/plain " },
107 { " css " , " text/css " },
108 { " png " , " image/png " },
109 { " gif " , " image/gif " },
110 { " jpg " , " image/jpg " },
111 { " jpeg " , " image/jpeg " },
112 { " zip " , " application/zip " },
113 { " js " , " text/javascript " },
114 { " dll " , " text/plain " },
115 { " aspx " , " text/html " }
116 };
117 _aspxHost = (AspxCreator)ApplicationHost.CreateApplicationHost
118 ( typeof (AspxCreator), " / " ,
119 AppDomain.CurrentDomain.BaseDirectory);
120 _runFlag = false ;
121 }
122
123 public void RunServer()
124 {
125 if (_runFlag) return ;
126 _pool.OnReceive += new SocketPoolController.RecevieHandler(HandleRequest);
127 _pool.RunPool(config.IP, config.Port);
128 _runFlag = true ;
129 }
130
131 public void StopServer()
132 {
133 _pool.StopPool();
134 _pool = null ;
135 _runFlag = false ;
136 }
137
138 private void HandleRequest( string uid, string header)
139 {
140 RequestHeader request = RequestHeader.ConvertRequestHander(header);
141 ResponseHeader response = new ResponseHeader();
142 response.Server = " My Test WebSite " ;
143 response.Connection = " close " ;
144
145 // 暂时只支持POST和GET的请求,其他的请求就视为未实现,发501响应
146 if (request.ActionName != " GET " && request.ActionName != " POST " )
147 {
148 response.ResponseCode = " 501 Not Implemented " ;
149 response.Content_Type = " text/html " ;
150 SendErrorResponse(uid, response);
151 return ;
152 }
153
154 // 对请求资源名称经行处理。主要是去除GET时带的参数,还有把斜杠换过来
155 string fullURL = config.VirtualPath + request.URL;
156 bool containQM=fullURL.Contains( ' ? ' );
157 string fileName =(containQM? Regex.Match(fullURL, @" .*(?=\?) " ).Value:fullURL).Replace( ' / ' , ' \\ ' );
158
159 // 如果请求的只是一个斜杠的,那证明请求的是默认页面
160 if (fileName == fullURL + " \\ " )
161 {
162 // 如果配置中有默认页的,发200的响应
163 string defaultFile = Path.Combine(config.VirtualPath, config.DefaultPage);
164 if (File.Exists(defaultFile))
165 {
166 response.Content_Type = " text/html " ;
167 response.ResponseCode = " 200 OK " ;
168 SendResponse(uid, File.ReadAllText(defaultFile), response);
169 return ;
170 }
171 // 如果不存在的,当404处理了
172 else
173 {
174 response.ResponseCode = " 404 Not Found " ;
175 response.Content_Type = " text/html " ;
176 SendErrorResponse(uid, response);
177 return ;
178 }
179 }
180
181 // 如果请求的资源不存在的,那就发送404
182 FileInfo fileInfo = new FileInfo(fileName);
183 if (! fileInfo.Exists)
184 {
185 response.ResponseCode = " 404 Not Found " ;
186 response.Content_Type = " text/html " ;
187 SendErrorResponse(uid, response);
188 return ;
189 }
190
191 // 如果请求的资源不在支持的范围内,也当作404了,感觉不是404的,貌似是403的
192 string extension = fileInfo.Extension.TrimStart( ' . ' );
193 if ( string .IsNullOrEmpty(extension) || ! _supportExtension.ContainsKey(extension))
194 {
195 response.ResponseCode = " 404 Not Found " ;
196 response.Content_Type = " text/html " ;
197 SendErrorResponse(uid, response);
198 return ;
199 }
200
201 // 既然也不是请求起始页的,也没发生上面列的错误的,就正常响应
202 response.Content_Type = _supportExtension[extension];
203 response.ResponseCode = " 200 OK " ;
204
205 if ( string .Compare(extension, " aspx " ) == 0 )
206 {
207 string queryString = containQM ? Regex.Match(fullURL, @" (?<=\?).* " ).Value : string .Empty;
208 string requestFile = containQM ? Regex.Match(request.URL, @" (?<=/).*(?=\?) " ).Value : Regex.Match(request.URL, @" (?<=/).* " ).Value;
209 // byte[] aspxHtml = _aspxHost.CreateAspxPage("AJAX.aspx", string.Empty);
210
211 byte [] aspxHtml = _aspxHost.CreateAspxPage(requestFile, queryString);
212 SendResponse(uid, aspxHtml, response);
213 return ;
214 }
215
216 FileStream fs = null ;
217 try
218 {
219 fs = File.OpenRead(fileInfo.FullName);
220 byte [] datas = new byte [fileInfo.Length];
221 fs.Read(datas, 0 , datas.Length);
222 SendResponse(uid, datas, response);
223 }
224 finally
225 {
226 fs.Close();
227 fs.Dispose();
228 fs = null ;
229 }
230 return ;
231 }
232
233 private void SendErrorResponse( string uid,ResponseHeader header)
234 {
235 string errorPageContent = header.CreateErrorHtml();
236 header.Content_Length = errorPageContent.Length;
237 SendResponse(uid, errorPageContent, header);
238 }
239
240 private void SendResponse( string uid, string content,ResponseHeader header)
241 {
242 header.Content_Length = content.Length;
243 _pool.SendMessage(uid, header.ToString());
244 _pool.SendMessage(uid, content);
245 }
246
247 private void SendResponse( string uid, byte [] content, ResponseHeader header)
248 {
249 header.Content_Length = content.Length;
250 _pool.SendMessage(uid, header.ToString());
251 _pool.SendMessage(uid, content);
252 }
253 }
ASP.NET宿主
1 internal class AspxCreator:MarshalByRefObject
2 {
3 public byte [] CreateAspxPage( string fileName, string qs)
4 {
5 byte [] datas= null ;
6 MemoryStream ms = null ;
7 StreamWriter sw = null ;
8 try
9 {
10 ms = new MemoryStream();
11 sw = new StreamWriter(ms, Encoding.UTF8);
12 sw.AutoFlush = true ;
13 HttpWorkerRequest worker = new ChineseRequest(fileName, qs, sw);
14 HttpRuntime.ProcessRequest(worker);
15 datas = new byte [ms.Length];
16 ms.Position = 0 ;
17 sw.BaseStream.Read(datas, 0 , datas.Length);
18 }
19 catch (Exception e)
20 {
21
22 }
23 finally
24 {
25 if (sw != null )
26 {
27 sw.Close();
28 sw.Dispose();
29 sw = null ;
30 }
31 if (ms != null )
32 {
33 ms.Close();
34 ms.Dispose();
35 ms = null ;
36 }
37 }
38 return datas;
39 }
40 }
41
42 internal class ChineseRequest:SimpleWorkerRequest
43 {
44 private TextWriter Output;
45 public ChineseRequest( string fileName, string queryString, TextWriter textWirter)
46 : base (fileName, queryString, textWirter)
47 {
48 Output = textWirter;
49 }
50 public override void SendResponseFromMemory( byte [] data, int length)
51 {
52 Output.Write(System.Text.Encoding.UTF8.GetChars(data, 0 , length));
53 }
54 }
若要查看连接池和Web服务器的实现详情,请查看这里篇博文《 Socket连接池 》和《 自己写的Web服务器 》
分类: C# , Socket编程
标签: Socket , Web服务器 , Http , ASP.NET宿主
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息