用edas写一个简单的http Server,扩展http协议的处理器
为了说明一下基础tcp开发库是如何扩展来处理上层协议的,我这里用一个简单到极点的Http Server来作为例子。这个例子会处理发到特定端口的所有http请求,并返回显示Hello world的网页。
由于是一个很简单的例子,所以我们并不需要解析完整的Http协议,当然我们是能够很快构建一个http处理器,毕竟这是一个基于文本行的协议,不过即使这样还是需要很大的篇幅,所以我们只需要处理其中一小部份。
首先我们来看看Http的request。http的请求是一串文本,由多个行组成,每行都是key value形式的,第一行表示了请求的方法和路径,由空格隔开,后面的是请求的头,由冒号隔开,不过其中的内容表示什么意思对我们来说没什么意义,我们在这里也不打算处理,我们把它们存在一个hash表里,等待更高级的处理器来处理。
所以首先我们先建立一个类,实现IProcessor接口:
class HttpProcessor : IProcessor { #region IProcessor 鎴愬憳 public void Init(IWorkingSocket Sock) { throw new NotImplementedException(); } public void NextProcessor(IProcessor NextProcessor) { throw new NotImplementedException(); } public void PushData(byte[] Data, int ReadCount) { throw new NotImplementedException(); } #endregion #region IDisposable 鎴愬憳 public void Dispose() { throw new NotImplementedException(); } #endregion }
根据前面的分析我们需要在其中用一个HashTable来存储http的请求头信息。
在每一次得到一行数据的时候就分析,是头一行还是header的行。然后将其插入hashtable,如果得到的是一个空行,那么就表明请求结束了,那么我们就将一段html发送回去。
class HttpProcessor : IProcessor { private Dictionary<string, string> httpHeader; public HttpProcessor() { httpHeader = new Dictionary<string, string>(); } public event Action<IWorkingSocket,Dictionary<string, string>> OnRequest; #region IProcessor 成员 private IWorkingSocket Conn; public void Init(IWorkingSocket Sock) { Conn = Sock; } public void NextProcessor(IProcessor NextProcessor) { //如果没有下级处理器就留空 } public void PushData(byte[] Data, int ReadCount) { string line = Encoding.ASCII.GetString(Data, 0, ReadCount); if (line.Equals("\r\n")) { if (OnRequest != null) { OnRequest(Conn, httpHeader); } } string[] temp = null; if (line.Split(':').Length>1) { temp = line.Split(':'); httpHeader.Add(temp[0], temp[1]); return; } if (line.Split(' ').Length > 1) { temp = line.Split(' '); httpHeader.Add(temp[0], temp[1]); return; } } #endregion #region IDisposable 回收资源 public void Dispose() { httpHeader.Clear(); Conn = null; } #endregion }
ok,这样子后我们在处理OnRequest事件的时候就能够接收到一个Dictionary对象,里面就是所有http的头信息,如果我们不关系其中的内容就可以直接在这个事件里返回数据了。
最后的结果:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using org.Alexander.EDAS; namespace EdasServer { class Program { static void Main(string[] args) { Acceptor acp = new Acceptor(8888); acp.OnError = (ex, str) => { //TO DO:To handle listenner's error }; acp.Accepted = (sock,id,buffersize) => { AsyncSocket sk = new AsyncSocket(sock, buffersize); sk.ID = id; sk.OnError += new Action<Exception, string>(sk_OnError); sk.Disconnected += new Action<long>(sk_Disconnected); TokenProcessor Proc = new TokenProcessor(0x0A); sk.Processor = Proc; HttpProcessor httpProc = new HttpProcessor(); httpProc.OnRequest += new Action<IWorkingSocket, Dictionary<string, string>>(httpProc_OnRequest); Proc.NextProcessor(httpProc); sk.WaitReceive(); }; acp.WaitAsyncAccept(); Console.ReadKey(); } static void httpProc_OnRequest(IWorkingSocket arg1, Dictionary<string, string> arg2) { StringBuilder html=new StringBuilder(); html.Append("<HTML><HEAD><TITLE>test</TITLE></HEAD><BODY><H3>Hello world</H3></BODY></HTML>"); StringBuilder str = new StringBuilder(); str.Append("HTTP /1.1 200 OK\r\n"); str.Append("Date:" + DateTime.Now.ToLongDateString()+"\r\n"); str.Append("Server: Edas Server(win32)\r\n"); str.Append("Content-Length: " + html.Length+"\r\n"); str.Append("Content-Type: text/html\r\n"); string Data = str.ToString() + "\r\n" + html.ToString(); arg1.SendToQueue(Encoding.ASCII.GetBytes(Data)); arg1.Disconnect(); } static void sk_Disconnected(long obj) { //TO DO:To handle disconnect event } static void Proc_OnLine(IWorkingSocket arg1, byte[] arg2) { arg1.SendToQueue(arg2); } static void sk_OnError(Exception arg1, string arg2) { //TO DO:Process errors } } class HttpProcessor : IProcessor { private Dictionary<string, string> httpHeader; public HttpProcessor() { httpHeader = new Dictionary<string, string>(); } public event Action<IWorkingSocket,Dictionary<string, string>> OnRequest; #region IProcessor 鎴愬憳 private IWorkingSocket Conn; public void Init(IWorkingSocket Sock) { Conn = Sock; } public void NextProcessor(IProcessor NextProcessor) { //濡傛灉鏆傛椂娌℃湁涓嬬骇澶勭悊鍣ㄧ殑鏃跺€欑暀绌? } public void PushData(byte[] Data, int ReadCount) { string line = Encoding.ASCII.GetString(Data, 0, ReadCount); if (line.Equals("\r\n")) { if (OnRequest != null) { OnRequest(Conn, httpHeader); } } string[] temp = null; if (line.Split(':').Length>1) { temp = line.Split(':'); httpHeader.Add(temp[0], temp[1]); return; } if (line.Split(' ').Length > 1) { temp = line.Split(' '); httpHeader.Add(temp[0], temp[1]); return; } } #endregion #region IDisposable 鎴愬憳 public void Dispose() { httpHeader.Clear(); Conn = null; } #endregion } }
运行后,打开IE,输入 http://localhost:8888,得到结果如下: