自己写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://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息