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编程
  • 相关阅读:
    JQuery脚本-通过禁用按钮防止表单重复提交
    获得图片的圆形头像效果
    jquery通过submit()和serialize()提交表单
    nginx location 配置阐述优先级别使用说明
    友盟(Swift)-集成、统计用户数量、具体页面访问数量、具体按钮点击数量
    点击按钮,使按钮进行左右翻转动画
    清除指定区域
    语音播报-文字转系统声音
    颜色线性渐变-CAGradientLayer
    popover带箭头弹框
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3053084.html
Copyright © 2011-2022 走看看