好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

自己写Web服务器(续)

自己写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/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于自己写Web服务器(续)的详细内容...

  阅读:41次