zoukankan      html  css  js  c++  java
  • 自己写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编程
  • 相关阅读:
    Unity The Method Signature Matching Rule
    Unity The Property Matching Rule
    Unity The Type Matching Rule
    Unity The Custom Attribute Matching Rule
    Unity The Member Name Matching Rule
    Unity No Policies
    Unity The Return Type Matching Rule
    Unity The Parameter Type Matching Rule
    Unity The Namespace Matching Rule
    关于TSQL递归查询的(转)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3053084.html
Copyright © 2011-2022 走看看