用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,得到结果如下:

image

posted on 2009-11-11 23:04  亚历山大同志  阅读(2089)  评论(11编辑  收藏  举报

导航