自己写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编程
原文地址:https://www.cnblogs.com/Leo_wl/p/3053084.html