自己写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服务器和连接池的最新源码

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 }

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 }

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服务器》