在.NET程序中实现HttpServer功能

最近在实现一个可视化数据解析工具,需要在Wpf程序中实现一个HttpServer,实现RESTfull接口,可以接收客户端(DTU或其他嵌入式设备)发送的请求。这种接口在ASP.NET中很容易实现,在Wpf程序中,需要有一个HttpServer才可以,开始考虑将Node.js签入到Wpf中,基本的功能可以实现,但是太麻烦了,程序的可移植性降低了,最好是在Wpf程序中内置这样的一个HttpServer。

从CodeProject找到两篇文章:http://www.codeproject.com/Articles/137979/Simple-HTTP-Server-in-C 和 http://www.codeproject.com/Articles/25050/Embedded-NET-HTTP-Server ,参考obJanova的文章(第二篇),对代码进行了改造,实现了自己需要的功能。

首先在Visual Studio中建一个控制台程序,添加两个文件,Sockets.cs和HTTP.cs,文件内容如下:

Sockets.cs文件内容:

// Client-server helpers for TCP/IP
// ClientInfo: wrapper for a socket which throws
//  Receive events, and allows for messaged communication (using
//  a specified character as end-of-message)
// Server: simple TCP server that throws Connect events
// ByteBuilder: utility class to manage byte arrays built up
//  in multiple transactions

// (C) Richard Smith 2005-9
//   [email protected]
// You can use this for free and give it to people as much as you like
// as long as you leave a credit to me :).

// Code to connect to a SOCKS proxy modified from
//   http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp

// changelog 1.6
//  Option for thread synchronisation on a UI control

// Define this symbol to include console output in various places
//#define DEBUG

// Define this symbol to use the old host name resolution
//#define NET_1_1

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Security.Cryptography;

using RedCorona.Cryptography;

//[assembly:System.Reflection.AssemblyVersion("1.6.2010.1228")]

namespace RedCorona.Net {
    public delegate void ConnectionRead(ClientInfo ci, String text);
    public delegate void ConnectionClosed(ClientInfo ci);
    public delegate void ConnectionReadBytes(ClientInfo ci, byte[] bytes, int len);
    public delegate void ConnectionReadMessage(ClientInfo ci, uint code, byte[] bytes, int len);
    public delegate void ConnectionReadPartialMessage(ClientInfo ci, uint code, byte[] bytes, int start, int read, int sofar, int totallength);
    public delegate void ConnectionNotify(ClientInfo ci);

    public enum ClientDirection {In, Out, Left, Right, Both};
    public enum MessageType {Unmessaged, EndMarker, Length, CodeAndLength};
    // ServerDES: The server sends an encryption key on connect
    // ServerRSAClientDES: The server sends an RSA public key, the client sends back a key
    public enum EncryptionType {None, ServerKey, ServerRSAClientKey};

    public class EncryptionUtils {
        static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        public static byte[] GetRandomBytes(int length, bool addByte){
            if(addByte && (length > 255)) throw new ArgumentException("Length must be 1 byte <256");
            byte[] random = new byte[length + (addByte ? 1 : 0)];
            rng.GetBytes(random);
            if(addByte) random[0] = (byte)length;
            return random;
        }
    }

    // OnReadBytes: catch the raw bytes as they arrive
    // OnRead: for text ended with a marker character
    // OnReadMessage: for binary info on a messaged client
    public class ClientInfo {
        internal Server server = null;
        private Socket sock;
        private String buffer;
        public event ConnectionRead OnRead;
        public event ConnectionClosed OnClose;
        public event ConnectionReadBytes OnReadBytes;
        public event ConnectionReadMessage OnReadMessage;
        public event ConnectionReadPartialMessage OnPartialMessage;
        public event ConnectionNotify OnReady;
        public MessageType MessageType;
        private ClientDirection dir;
        int id;
        bool alreadyclosed = false;
        public static int NextID = 100;
        private Exception closeException;
        private string closeReason = null;
        //private ClientThread t;
        public object Data = null;
        private Control threadSyncControl;

        // Encryption info
        EncryptionType encType;
        int encRead = 0, encStage, encExpected;
        internal bool encComplete;
        internal byte[] encKey;
        internal RSAParameters encParams;

        public EncryptionType EncryptionType {
            get { return encType; }
            set {
                if(encStage != 0) throw new ArgumentException("Key exchange has already begun");
                encType = value;
                encComplete = encType == EncryptionType.None;
                encExpected = -1;
            }
        }
        public bool EncryptionReady { get { return encComplete; } }
        internal ICryptoTransform encryptor, decryptor;
        public ICryptoTransform Encryptor { get { return encryptor; } }
        public ICryptoTransform Decryptor { get { return decryptor; } }
        public Control ThreadSyncControl { get { return threadSyncControl; } set { threadSyncControl = value; } }

        private string delim;
        public const int BUFSIZE = 1024;
        byte[] buf = new byte[BUFSIZE];
        ByteBuilder bytes = new ByteBuilder(10);

        byte[] msgheader = new byte[8];
        byte headerread = 0;
        bool wantingChecksum = true;

        bool sentReady = false;

        public string Delimiter {
            get { return delim; }
            set { delim = value; }
        }

        public ClientDirection Direction { get { return dir; } }
        public Socket Socket { get { return sock; } }
        public Server Server { get { return server; } }
        public int ID { get { return id; } }

        public bool Closed {
            get { return !sock.Connected; }
        }

        public string CloseReason { get { return closeReason; } }
        public Exception CloseException { get { return closeException; } }

        public ClientInfo(Socket cl, bool StartNow) : this(cl, null, null, ClientDirection.Both, StartNow, EncryptionType.None) {}
        //public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d) : this(cl, read, readevt, d, true, EncryptionType.None) {}
        public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow) : this(cl, read, readevt, d, StartNow, EncryptionType.None) {}
        public ClientInfo(Socket cl, ConnectionRead read, ConnectionReadBytes readevt, ClientDirection d, bool StartNow, EncryptionType encryptionType){
            sock = cl; buffer = ""; OnReadBytes = readevt; encType = encryptionType;
            encStage = 0; encComplete = encType == EncryptionType.None;
            OnRead = read;
            MessageType = MessageType.EndMarker;
            dir = d; delim = "\n";
            id = NextID; // Assign each client an application-unique ID
            unchecked{NextID++;}
            //t = new ClientThread(this);
            if(StartNow) BeginReceive();
        }

        public void BeginReceive(){
//            t.t.Start();
            if((encType == EncryptionType.None) && (!sentReady)) {
                sentReady = true;
                if(OnReady != null) OnReady(this);
            }
            sock.BeginReceive(buf, 0, BUFSIZE, 0, new AsyncCallback(ReadCallback), this);
        }

        public String Send(String text){
            byte[] ba = Encoding.UTF8.GetBytes(text);
            String s = "";
            for(int i = 0; i < ba.Length; i++) s += ba[i] + " ";
            Send(ba);
            return s;
        }

        public void SendMessage(uint code, byte[] bytes){ SendMessage(code, bytes, 0, bytes.Length); }
        public void SendMessage(uint code, byte[] bytes, byte paramType){ SendMessage(code, bytes, paramType, bytes.Length); }
        public void SendMessage(uint code, byte[] bytes, byte paramType, int len){
            if(paramType > 0){
                ByteBuilder b = new ByteBuilder(3);
                b.AddParameter(bytes, paramType);
                bytes = b.Read(0, b.Length);
                len = bytes.Length;
            } 

            lock(sock){
                byte checksum = 0; byte[] ba;
                switch(MessageType){
                    case MessageType.CodeAndLength:
                        Send(ba = UintToBytes(code));
                        for(int i = 0; i < 4; i++) checksum += ba[i];
                        Send(ba = IntToBytes(len));
                        for(int i = 0; i < 4; i++) checksum += ba[i];
                        if(encType != EncryptionType.None) Send(new byte[]{checksum});
                        break;
                    case MessageType.Length:
                        Send(ba = IntToBytes(len));
                        for(int i = 0; i < 4; i++) checksum += ba[i];
                        if(encType != EncryptionType.None) Send(new byte[]{checksum});
                        break;
                }
                Send(bytes, len);
                if(encType != EncryptionType.None){
                    checksum = 0;
                    for(int i = 0; i < len; i++) checksum += bytes[i];
                    Send(new byte[]{checksum});
                }
            }

        }
        public void Send(byte[] bytes){ Send(bytes, bytes.Length); }
        public void Send(byte[] bytes, int len){
            if(!encComplete) throw new IOException("Key exchange is not yet completed");
            if(encType != EncryptionType.None){
                byte[] outbytes = new byte[len];
                Encryptor.TransformBlock(bytes, 0, len, outbytes, 0);
                bytes = outbytes;
                //Console.Write("Encryptor IV: "); LogBytes(encryptor.Key, encryptor.IV.length);
            }
            #if DEBUG
            Console.Write(ID + " Sent: "); LogBytes(bytes, len);
            #endif
            try {
                sock.Send(bytes, len, SocketFlags.None);
            } catch(Exception e){
                closeException = e;
                closeReason = "Exception in send: "+e.Message;
                Close();
            }
        }

        public bool MessageWaiting(){
            FillBuffer(sock);
            return buffer.IndexOf(delim) >= 0;
        }

        public String Read(){
            //FillBuffer(sock);
            int p = buffer.IndexOf(delim);
            if(p >= 0){
                String res = buffer.Substring(0, p);
                buffer = buffer.Substring(p + delim.Length);
                return res;
            } else return "";
        }

        private void FillBuffer(Socket sock){
            byte[] buf = new byte[256];
            int read;
            while(sock.Available != 0){
                read = sock.Receive(buf);
                if(OnReadBytes != null) OnReadBytes(this, buf, read);
                buffer += Encoding.UTF8.GetString(buf, 0, read);
            }
        }

        void ReadCallback(IAsyncResult ar){
            try {
                int read = sock.EndReceive(ar);
                //Console.WriteLine("Socket "+ID+" read "+read+" bytes");
                if(read > 0){
                    DoRead(buf, read);
                    BeginReceive();
                } else {
                    #if DEBUG
                    Console.WriteLine(ID + " zero byte read closure");
                    #endif
                    closeReason = "Zero byte read (no data available)";
                    closeException = null;
                    Close();
                }
            } catch(SocketException e){
                #if DEBUG
                Console.WriteLine(ID + " socket exception closure: "+e);
                #endif
                closeReason = "Socket exception (" + e.Message + ")";
                closeException = e;
                Close();
            } catch(ObjectDisposedException e){
                #if DEBUG
                Console.WriteLine(ID + " disposed exception closure");
                #endif
                closeReason = "Disposed exception (socket object was disposed by the subsystem)";
                closeException = e;
                Close();
            }
        }

        internal void DoRead(byte[] buf, int read){
            if(read > 0){
                if(OnRead != null){ // Simple text mode
                    buffer += Encoding.UTF8.GetString(buf, 0, read);
                    while(buffer.IndexOf(delim) >= 0){
                        if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnRead, new object[]{this, Read()});
                        else OnRead(this, Read());
                    }
                }
            }
            Console.WriteLine(ID + " read "+read+" bytes for event handler");
            ReadInternal(buf, read, false);
        }

        public static void LogBytes(byte[] buf, int len){
            byte[] ba = new byte[len];
            Array.Copy(buf, ba, len);
            Console.WriteLine(ByteBuilder.FormatParameter(new Parameter(ba, ParameterType.Byte)));
        }

        void ReadInternal(byte[] buf, int read, bool alreadyEncrypted){            

        Console.WriteLine(ID + " read "+read+" bytes for event handler");
            if((!alreadyEncrypted) && (encType != EncryptionType.None)){
                if(encComplete){
                    #if DEBUG
                    Console.Write(ID + " Received: "); LogBytes(buf, read);
                    #endif
                    buf = decryptor.TransformFinalBlock(buf, 0, read);
                    #if DEBUG
                    Console.Write(ID + " Decrypted: "); LogBytes(buf, read);
                    #endif
                } else {
                    // Client side key exchange
                    int ofs = 0;
                    if(encExpected < 0){
                        encStage++;
                        ofs++; read--; encExpected = buf[0]; // length of key to come
                        encKey = new byte[encExpected];
                        encRead = 0;
                    }
                    if(read >= encExpected){
                        Array.Copy(buf, ofs, encKey, encRead, encExpected);
                        int togo = read - encExpected;
                        encExpected = -1;
                        #if DEBUG
                        Console.WriteLine(ID + " Read encryption key: "+ByteBuilder.FormatParameter(new Parameter(encKey, ParameterType.Byte)));
                        #endif
                        if(server == null) ClientEncryptionTransferComplete();
                        else ServerEncryptionTransferComplete();
                        if(togo > 0){
                            byte[] newbuf = new byte[togo];
                            Array.Copy(buf, read + ofs - togo, newbuf, 0, togo);
                            ReadInternal(newbuf, togo, false);
                        }
                    } else {
                        Array.Copy(buf, ofs, encKey, encRead, read);
                        encExpected -= read; encRead += read;
                    }
                    return;
                }
            }

            if((!alreadyEncrypted) && (OnReadBytes != null)){
                if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadBytes, new object[]{this, buf, read});
                else OnReadBytes(this, buf, read);
            }

            if((OnReadMessage != null) && (MessageType != MessageType.Unmessaged)){
                // Messaged mode
                int copied;
                uint code = 0;
                switch(MessageType){
                    case MessageType.CodeAndLength:
                    case MessageType.Length:
                        int length;
                        if(MessageType == MessageType.Length){
                            copied = FillHeader(ref buf, 4, read);
                            if(headerread < 4) break;
                            length = GetInt(msgheader, 0, 4);
                        } else{
                            copied = FillHeader(ref buf, 8, read);
                            if(headerread < 8) break;
                            code = (uint)GetInt(msgheader, 0, 4);
                            length = GetInt(msgheader, 4, 4);
                        }
                        if(read == copied) break;
                        // If encryption is on, the next byte is a checksum of the header
                        int ofs = 0;
                        if(wantingChecksum && (encType != EncryptionType.None)){
                            byte checksum = buf[0];
                            ofs++;
                            wantingChecksum = false;
                            byte headersum = 0;
                            for(int i = 0; i < 8; i++) headersum += msgheader[i];
                            if(checksum != headersum){
                                Close();
                                throw new IOException("Header checksum failed! (was "+checksum+", calculated "+headersum+")");
                            }
                        }
                        bytes.Add(buf, ofs, read - ofs - copied);
                        if(encType != EncryptionType.None) length++; // checksum byte

                        // Now we know we are reading into the body of the message
                        #if DEBUG
                        Console.WriteLine(ID + " Added "+(read - ofs - copied)+" bytes, have "+bytes.Length+" of "+length);
                        #endif
                        if(OnPartialMessage != null) {
                            if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnPartialMessage, new object[]{this, code, buf, ofs, read - ofs - copied, bytes.Length, length});
                            else OnPartialMessage(this, code, buf, ofs, read - ofs - copied, bytes.Length, length);
                        }

                        if(bytes.Length >= length){
                            // A message was received!
                             headerread = 0; wantingChecksum = true;
                            byte[] msg = bytes.Read(0, length);
                            if(encType != EncryptionType.None){
                                byte checksum = msg[length - 1], msgsum = 0;
                                for(int i = 0; i < length - 1; i++) msgsum += msg[i];
                                if(checksum != msgsum){
                                    Close();
                                    throw new IOException("Content checksum failed! (was "+checksum+", calculated "+msgsum+")");
                                }
                                if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadMessage, new object[]{this, code, msg, length - 1});
                                else OnReadMessage(this, code, msg, length - 1);
                            } else {
                                if(threadSyncControl != null) threadSyncControl.BeginInvoke(OnReadMessage, new object[]{this, code, msg, length });
                                else OnReadMessage(this, code, msg, length);
                            }
                            // Don‘t forget to put the rest through the mill
                            int togo = bytes.Length - length;
                            if(togo > 0){
                                byte[] whatsleft = bytes.Read(length, togo);
                                bytes.Clear();
                                ReadInternalSecondPass(whatsleft);
                            } else bytes.Clear();
                        }
                        //if(OnStatus != null) OnStatus(this, bytes.Length, length);
                        break;
                }
            }
        }

        void ReadInternalSecondPass(byte[] newbytes){
            ReadInternal(newbytes, newbytes.Length, true);
        }

        int FillHeader(ref byte[] buf, int to, int read) {
            int copied = 0;
            if(headerread < to){
                // First copy the header into the header variable.
                for(int i = 0; (i < read) && (headerread < to); i++, headerread++, copied++){
                    msgheader[headerread] = buf[i];
                }
            }
            if(copied > 0){
                // Take the header bytes off the ‘message‘ section
                byte[] newbuf = new byte[read - copied];
                for(int i = 0; i < newbuf.Length; i++) newbuf[i] = buf[i + copied];
                buf = newbuf;
            }
            return copied;
        }

        internal ICryptoTransform MakeEncryptor(){ return MakeCrypto(true); }
        internal ICryptoTransform MakeDecryptor(){ return MakeCrypto(false); }
        internal ICryptoTransform MakeCrypto(bool encrypt){
            if(encrypt) return new SimpleEncryptor(encKey);
            else return new SimpleDecryptor(encKey);
        }        

        void ServerEncryptionTransferComplete(){
            switch(encType){
                case EncryptionType.None:
                    throw new ArgumentException("Should not have key exchange for unencrypted connection!");
                case EncryptionType.ServerKey:
                    throw new ArgumentException("Should not have server-side key exchange for server keyed connection!");
                case EncryptionType.ServerRSAClientKey:
                    // Symmetric key is in RSA-encoded encKey
                    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                    rsa.ImportParameters(encParams);
                    encKey = rsa.Decrypt(encKey, false);
                    #if DEBUG
                    Console.WriteLine("Symmetric key is: "); LogBytes(encKey, encKey.Length);
                    #endif
                    MakeEncoders();
                    server.KeyExchangeComplete(this);
                    break;
            }
        }

        void ClientEncryptionTransferComplete(){
            // A part of the key exchange process has been completed, and the key is
            // in encKey
            switch(encType){
                case EncryptionType.None:
                    throw new ArgumentException("Should not have key exchange for unencrypted connection!");
                case EncryptionType.ServerKey:
                    // key for transfer is now in encKey, so all is good
                    MakeEncoders();
                    break;
                case EncryptionType.ServerRSAClientKey:
                    // Stage 1: modulus; Stage 2: exponent
                    // When the exponent arrives, create a random DES key
                    // and send it
                    switch(encStage){
                        case 1: encParams.Modulus = encKey; break;
                        case 2:
                            encParams.Exponent = encKey;
                            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                            rsa.ImportParameters(encParams);
                            encKey = EncryptionUtils.GetRandomBytes(24, false);
                            byte[] send = GetLengthEncodedVector(rsa.Encrypt(encKey, false));
                            sock.Send(send);
                            #if DEBUG
                            Console.WriteLine("Sent symmetric key: "+ByteBuilder.FormatParameter(new Parameter(send, ParameterType.Byte)));
                            #endif
                            MakeEncoders();
                            break;
                    }
                    break;
            }
        }

        internal void MakeEncoders(){
            encryptor = MakeEncryptor();
            decryptor = MakeDecryptor();
            encComplete = true;
            if(OnReady != null) OnReady(this);
        }

        public static byte[] GetLengthEncodedVector(byte[] from){
            int l = from.Length;
            if(l > 255) throw new ArgumentException("Cannot length encode more than 255");
            byte[] to = new byte[l + 1];
            to[0] = (byte)l;
            Array.Copy(from, 0, to, 1, l);
            return to;
        }

        public static int GetInt(byte[] ba, int from, int len){
            int r = 0;
            for(int i = 0; i < len; i++)
                r += ba[from + i] << ((len - i - 1) * 8);
            return r;
        }

        public static int[] GetIntArray(byte[] ba){ return GetIntArray(ba, 0, ba.Length, 4); }
        public static int[] GetIntArray(byte[] ba, int from, int len){ return GetIntArray(ba, from, len, 4); }
        public static int[] GetIntArray(byte[] ba, int from, int len, int stride){
            int[] res = new int[len / stride];
            for(int i = 0; i < res.Length; i++){
                res[i] = GetInt(ba, from + (i * stride), stride);
            }
            return res;
        }

        public static uint[] GetUintArray(byte[] ba){
            uint[] res = new uint[ba.Length / 4];
            for(int i = 0; i < res.Length; i++){
                res[i] = (uint)GetInt(ba, i * 4, 4);
            }
            return res;
        }

        public static double[] GetDoubleArray(byte[] ba){
            double[] res = new double[ba.Length / 8];
            for(int i = 0; i < res.Length; i++){
                res[i] = BitConverter.ToDouble(ba, i * 8);
            }
            return res;
        }

        public static byte[] IntToBytes(int val){ return UintToBytes((uint)val); }
        public static byte[] UintToBytes(uint val){
            byte[] res = new byte[4];
            for(int i = 3; i >= 0; i--){
                res[i] = (byte)val; val >>= 8;
            }
            return res;
        }

        public static byte[] IntArrayToBytes(int[] val){
            byte[] res = new byte[val.Length * 4];
            for(int i = 0; i < val.Length; i++){
                byte[] vb = IntToBytes(val[i]);
                res[(i * 4)] = vb[0];
                res[(i * 4) + 1] = vb[1];
                res[(i * 4) + 2] = vb[2];
                res[(i * 4) + 3] = vb[3];
            }
            return res;
        }

        public static byte[] UintArrayToBytes(uint[] val){
            byte[] res = new byte[val.Length * 4];
            for(uint i = 0; i < val.Length; i++){
                byte[] vb = IntToBytes((int)val[i]);
                res[(i * 4)] = vb[0];
                res[(i * 4) + 1] = vb[1];
                res[(i * 4) + 2] = vb[2];
                res[(i * 4) + 3] = vb[3];
            }
            return res;
        }

        public static byte[] DoubleArrayToBytes(double[] val){
            byte[] res = new byte[val.Length * 8];
            for(int i = 0; i < val.Length; i++){
                byte[] vb = BitConverter.GetBytes(val[i]);
                for(int ii = 0; ii < 8; ii++) res[(i*8)+ii] = vb[ii];
            }
            return res;
        }

        public static byte[] StringArrayToBytes(string[] val, Encoding e){
            byte[][] baa = new byte[val.Length][];
            int l = 0;
            for(int i = 0; i < val.Length; i++){ baa[i] = e.GetBytes(val[i]); l += 4 + baa[i].Length; }
            byte[] r = new byte[l + 4];
            IntToBytes(val.Length).CopyTo(r, 0);
            int ofs = 4;
            for(int i = 0; i < baa.Length; i++){
                IntToBytes(baa[i].Length).CopyTo(r, ofs); ofs += 4;
                baa[i].CopyTo(r, ofs); ofs += baa[i].Length;
            }
            return r;
        }

        public static string[] GetStringArray(byte[] ba, Encoding e){
            int l = GetInt(ba, 0, 4), ofs = 4;
            string[] r = new string[l];
            for(int i = 0; i < l; i++){
                int thislen = GetInt(ba, ofs, 4); ofs += 4;
                r[i] = e.GetString(ba, ofs, thislen); ofs += thislen;
            }
            return r;
        }

        public void Close(string reason){ if(!alreadyclosed){ closeReason = reason; Close(); } }
        public void Close(){
            if(!alreadyclosed){
                if(server != null) server.ClientClosed(this);
                if(OnClose != null) {
                    if((threadSyncControl != null) && (threadSyncControl.InvokeRequired))
                        threadSyncControl.Invoke(OnClose, new object[]{this});
                    else OnClose(this);
                }
                alreadyclosed = true;
                #if DEBUG
                Console.WriteLine("**closed client** at "+DateTime.Now.Ticks);
                #endif
            }
            sock.Close();
        }
    }

    public class Sockets {
        // Socks proxy inspired by http://www.thecodeproject.com/csharp/ZaSocks5Proxy.asp
        public static SocksProxy SocksProxy;
        public static bool UseSocks = false;

        public static Socket CreateTCPSocket(String address, int port){return CreateTCPSocket(address, port, UseSocks, SocksProxy);}
        public static Socket CreateTCPSocket(String address, int port, bool useSocks, SocksProxy proxy){
            Socket sock;
            if(useSocks) sock = ConnectToSocksProxy(proxy.host, proxy.port, address, port, proxy.username, proxy.password);
            else {
                #if NET_1_1
                IPAddress host = Dns.GetHostByName(address).AddressList[0];
                #else
                IPAddress host = Dns.GetHostEntry(address).AddressList[0];
                #endif
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sock.Connect(new IPEndPoint(host, port));
            }
            return sock;
        }

        private static string[] errorMsgs=    {
            "Operation completed successfully.",
            "General SOCKS server failure.",
            "Connection not allowed by ruleset.",
            "Network unreachable.",
            "Host unreachable.",
            "Connection refused.",
            "TTL expired.",
            "Command not supported.",
            "Address type not supported.",
            "Unknown error."
        };

        public static Socket ConnectToSocksProxy(IPAddress proxyIP, int proxyPort, String destAddress, int destPort, string userName, string password){
            byte[] request = new byte[257];
            byte[] response = new byte[257];
            ushort nIndex;

            IPAddress destIP = null;

            try{ destIP = IPAddress.Parse(destAddress); }
            catch { }

            IPEndPoint proxyEndPoint = new IPEndPoint(proxyIP, proxyPort);

            // open a TCP connection to SOCKS server...
            Socket s;
            s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
            s.Connect(proxyEndPoint);
/*            } catch(SocketException){
                throw new SocketException(0, "Could not connect to proxy server.");
            }*/

            nIndex = 0;
            request[nIndex++]=0x05; // Version 5.
            request[nIndex++]=0x02; // 2 Authentication methods are in packet...
            request[nIndex++]=0x00; // NO AUTHENTICATION REQUIRED
            request[nIndex++]=0x02; // USERNAME/PASSWORD
            // Send the authentication negotiation request...
            s.Send(request,nIndex,SocketFlags.None);

            // Receive 2 byte response...
            int nGot = s.Receive(response,2,SocketFlags.None);
            if (nGot!=2)
                throw new ConnectionException("Bad response received from proxy server.");

            if (response[1]==0xFF)
            {    // No authentication method was accepted close the socket.
                s.Close();
                throw new ConnectionException("None of the authentication method was accepted by proxy server.");
            }

            byte[] rawBytes;

            if (/*response[1]==0x02*/true)
            {//Username/Password Authentication protocol
                nIndex = 0;
                request[nIndex++]=0x05; // Version 5.

                // add user name
                request[nIndex++]=(byte)userName.Length;
                rawBytes = Encoding.Default.GetBytes(userName);
                rawBytes.CopyTo(request,nIndex);
                nIndex+=(ushort)rawBytes.Length;

                // add password
                request[nIndex++]=(byte)password.Length;
                rawBytes = Encoding.Default.GetBytes(password);
                rawBytes.CopyTo(request,nIndex);
                nIndex+=(ushort)rawBytes.Length;

                // Send the Username/Password request
                s.Send(request,nIndex,SocketFlags.None);
                // Receive 2 byte response...
                nGot = s.Receive(response,2,SocketFlags.None);
                if (nGot!=2)
                    throw new ConnectionException("Bad response received from proxy server.");
                if (response[1] != 0x00)
                    throw new ConnectionException("Bad Usernaem/Password.");
            }
            // This version only supports connect command.
            // UDP and Bind are not supported.

            // Send connect request now...
            nIndex = 0;
            request[nIndex++]=0x05;    // version 5.
            request[nIndex++]=0x01;    // command = connect.
            request[nIndex++]=0x00;    // Reserve = must be 0x00

            if (destIP != null)
            {// Destination adress in an IP.
                switch(destIP.AddressFamily)
                {
                    case AddressFamily.InterNetwork:
                        // Address is IPV4 format
                        request[nIndex++]=0x01;
                        rawBytes = destIP.GetAddressBytes();
                        rawBytes.CopyTo(request,nIndex);
                        nIndex+=(ushort)rawBytes.Length;
                        break;
                    case AddressFamily.InterNetworkV6:
                        // Address is IPV6 format
                        request[nIndex++]=0x04;
                        rawBytes = destIP.GetAddressBytes();
                        rawBytes.CopyTo(request,nIndex);
                        nIndex+=(ushort)rawBytes.Length;
                        break;
                }
            }
            else
            {// Dest. address is domain name.
                request[nIndex++]=0x03;    // Address is full-qualified domain name.
                request[nIndex++]=Convert.ToByte(destAddress.Length); // length of address.
                rawBytes = Encoding.Default.GetBytes(destAddress);
                rawBytes.CopyTo(request,nIndex);
                nIndex+=(ushort)rawBytes.Length;
            }

            // using big-edian byte order
            byte[] portBytes = BitConverter.GetBytes((ushort)destPort);
            for (int i=portBytes.Length-1;i>=0;i--)
                request[nIndex++]=portBytes[i];

            // send connect request.
            s.Send(request,nIndex,SocketFlags.None);
            s.Receive(response);    // Get variable length response...
            if (response[1]!=0x00)
                throw new ConnectionException(errorMsgs[response[1]]);
            // Success Connected...
            return s;
        }
    }

    public struct SocksProxy {
        public IPAddress host;
        public ushort port;
        public string username, password;

        public SocksProxy(String hostname, ushort port, String username, String password){
            this.port = port;
            #if NET_1_1
            host = Dns.GetHostByName(hostname).AddressList[0];
            #else
            host = Dns.GetHostEntry(hostname).AddressList[0];
            #endif
            this.username = username; this.password = password;
        }
    }

    public class ConnectionException: Exception {
        public ConnectionException(string message) : base(message) {}
    }

    // Server code cribbed from Framework Help
    public delegate bool ClientEvent(Server serv, ClientInfo new_client); // whether to accept the client
    public class Server {
        class ClientState {
            // To hold the state information about a client between transactions
            internal Socket Socket = null;
            internal const int BufferSize = 1024;
            internal byte[] buffer = new byte[BufferSize];
            internal StringBuilder sofar = new StringBuilder();  

            internal ClientState(Socket sock){
                Socket = sock;
            }
        }

      Hashtable clients = new Hashtable();
      Socket ss;

      public event ClientEvent Connect, ClientReady;
      public IEnumerable Clients {
        get { return clients.Values; }
      }

        public Socket ServerSocket {
            get { return ss; }
        }

        public ClientInfo this[int id]{
            get { return (ClientInfo)clients[id]; }
/*                foreach(ClientInfo ci in Clients)
                    if(ci.ID == id) return ci;
                return null;
            }*/
        }

        public object SyncRoot { get { return this; } }

        private EncryptionType encType;
        public EncryptionType DefaultEncryptionType {
            get { return encType; }
            set { encType = value; }
        }

        public int Port {
            get { return ((IPEndPoint)ss.LocalEndPoint).Port; }
        }

        public Server(int port) : this(port, null) {}
        public Server(int port, ClientEvent connDel) {
            Connect = connDel;

            ss = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
            ss.Bind(new IPEndPoint(IPAddress.Any, port));
            ss.Listen(100);

            // Start the accept process. When a connection is accepted, the callback
            // must do this again to accept another connection
            ss.BeginAccept(new AsyncCallback(AcceptCallback), ss);
        }

        internal void ClientClosed(ClientInfo ci){
            lock(SyncRoot) clients.Remove(ci.ID);
        }

        public void Broadcast(byte[] bytes){
            lock(SyncRoot) foreach(ClientInfo ci in Clients) ci.Send(bytes);
        }

        public void BroadcastMessage(uint code, byte[] bytes){BroadcastMessage(code, bytes, 0); }
        public void BroadcastMessage(uint code, byte[] bytes, byte paramType){
            lock(SyncRoot) foreach(ClientInfo ci in Clients) ci.SendMessage(code, bytes, paramType);
        }

        // ASYNC CALLBACK CODE
        void AcceptCallback(IAsyncResult ar) {
            try{
                Socket server = (Socket) ar.AsyncState;
                Socket cs = server.EndAccept(ar);

                // Start the thing listening again
                server.BeginAccept(new AsyncCallback(AcceptCallback), server);

                ClientInfo c = new ClientInfo(cs, null, null, ClientDirection.Both, false);
                c.server = this;
                // Allow the new client to be rejected by the application
                if(Connect != null){
                    if(!Connect(this, c)){
                        // Rejected
                        cs.Close();
                        return;
                    }
                }
                // Initiate key exchange
                c.EncryptionType = encType;
                switch(encType){
                    case EncryptionType.None: KeyExchangeComplete(c); break;
                    case EncryptionType.ServerKey:
                        c.encKey = GetSymmetricKey();
                        byte[] key = ClientInfo.GetLengthEncodedVector(c.encKey);
                        cs.Send(key);
                        #if DEBUG
                        Console.Write(c.ID + " Sent key: "); ClientInfo.LogBytes(key, key.Length);
                        #endif
                        c.MakeEncoders();
                        KeyExchangeComplete(c);
                        break;
                    case EncryptionType.ServerRSAClientKey:
                        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                        RSAParameters p = rsa.ExportParameters(true);
                        cs.Send(ClientInfo.GetLengthEncodedVector(p.Modulus));
                        cs.Send(ClientInfo.GetLengthEncodedVector(p.Exponent));
                        c.encParams = p;
                        break;
                    default: throw new ArgumentException("Unknown or unsupported encryption type "+encType);
                }
                lock(SyncRoot) clients[c.ID] = c;
                c.BeginReceive();
            } catch(ObjectDisposedException) {  }
            catch(SocketException) { }
            catch(Exception e) { Console.WriteLine(e); }
        }

        protected virtual byte[] GetSymmetricKey(){
            return EncryptionUtils.GetRandomBytes(24, false);
        }

        internal void KeyExchangeComplete(ClientInfo ci){
            // Key exchange is complete on this client. Client ready
            // handlers may still force a close of the connection
            if(ClientReady != null)
                if(!ClientReady(this, ci)) ci.Close("ClientReady callback rejected connection");
        }

        ~Server() { Close(); }
        public void Close(){
            ArrayList cl2 = new ArrayList();
            foreach(ClientInfo c in Clients) cl2.Add(c);
            foreach(ClientInfo c in cl2) c.Close("Server shutdown");

            ss.Close();
        }
    }

    public class ByteBuilder : MarshalByRefObject {
        byte[][] data;
        int packsize, used, paraminx = 0;

        public int Length {
            get {
                int len = 0;
                for(int i = 0; i < used; i++) len += data[i].Length;
                return len;
            }
        }

        public byte this[int i]{
            get { return Read(i, 1)[0]; }
        }

        public ByteBuilder() : this(10) {}
        public ByteBuilder(int packsize){
            this.packsize = packsize; used = 0;
            data = new byte[packsize][];
        }

        public ByteBuilder(byte[] data) {
            packsize = 1;
            used = 1;
            this.data = new byte[][] { data };
        }

        public ByteBuilder(byte[] data, int len) : this(data, 0, len) {}
        public ByteBuilder(byte[] data, int from, int len) : this(1) {
            Add(data, from, len);
        }
        public void Add(byte[] moredata){ Add(moredata, 0, moredata.Length); }
        public void Add(byte[] moredata, int from, int len){
//Console.WriteLine("Getting "+from+" to "+(from+len-1)+" of "+moredata.Length);
            if(used < packsize){
                data[used] = new byte[len];
                for(int j = from; j < from + len; j++)
                    data[used][j - from] = moredata[j];
                used++;
            } else {
                // Compress the existing items into the first array
                byte[] newdata = new byte[Length + len];
                int np = 0;
                for(int i = 0; i < used; i++)
                    for(int j = 0; j < data[i].Length; j++)
                        newdata[np++] = data[i][j];
                for(int j = from; j < from + len; j++)
                    newdata[np++] = moredata[j];
                data[0] = newdata;
                for(int i = 1; i < used; i++) data[i] = null;
                used = 1;
            }
        }

        public byte[] Read(int from, int len){
            if(len == 0) return new byte[0];
            byte[] res = new byte[len];
            int done = 0, start = 0;

            for(int i = 0; i < used; i++){
                if((start + data[i].Length) <= from){
                    start += data[i].Length; continue;
                }
                // Now we‘re in the data block
                for(int j = 0; j < data[i].Length; j++){
                    if((j + start) < from) continue;
                    res[done++] = data[i][j];
                    if(done == len) return res;
                }
            }

            throw new ArgumentException("Datapoints "+from+" and "+(from+len)+" must be less than "+Length);
        }

        public void Clear(){
            used = 0;
            for(int i = 0; i < used; i++) data[i] = null;
        }

        public Parameter GetNextParameter(){ return GetParameter(ref paraminx); }
        public void ResetParameterPointer(){ paraminx = 0; }

        public Parameter GetParameter(ref int index){
            paraminx = index;
            Parameter res = new Parameter();
            res.Type = Read(index++, 1)[0];
            byte[] lenba = Read(index, 4);
            index += 4;
            int len = ClientInfo.GetInt(lenba, 0, 4);
            res.content = Read(index, len);
            index += len;
            return res;
        }

        public void AddParameter(Parameter param){ AddParameter(param.content, param.Type); }
        public void AddParameter(byte[] content, byte Type){
            Add(new byte[]{Type});
            Add(ClientInfo.IntToBytes(content.Length));
            Add(content);
        }

        public void AddInt(int i){ AddParameter(ClientInfo.IntToBytes(i), ParameterType.Int); }
        public void AddIntArray(int[] ia){ AddParameter(ClientInfo.IntArrayToBytes(ia), ParameterType.Int); }
        public void AddString(string s){ AddParameter(Encoding.UTF8.GetBytes(s), ParameterType.String); }
        public void AddStringArray(string[] sa){ AddParameter(ClientInfo.StringArrayToBytes(sa, Encoding.UTF8), ParameterType.StringArray); }
        public void AddDouble(double i){ AddParameter(BitConverter.GetBytes(i), ParameterType.Double); }
        public void AddLong(long i){ AddParameter(BitConverter.GetBytes(i), ParameterType.Long); }
        public void AddDoubleArray(double[] ia){ AddParameter(ClientInfo.DoubleArrayToBytes(ia), ParameterType.Double); }

        public int GetInt(){ return ClientInfo.GetInt(GetNextParameter().content, 0, 4);}
        public int[] GetIntArray(){ return ClientInfo.GetIntArray(GetNextParameter().content);}
        public double GetDouble(){ return BitConverter.ToDouble(GetNextParameter().content, 0);}
        public double[] GetDoubleArray(){ return ClientInfo.GetDoubleArray(GetNextParameter().content);}
        public long GetLong(){ return BitConverter.ToInt64(GetNextParameter().content, 0);}
        public string GetString(){ return Encoding.UTF8.GetString(GetNextParameter().content);}
        public string[] GetStringArray(){ return ClientInfo.GetStringArray(GetNextParameter().content, Encoding.UTF8);}

        public static String FormatParameter(Parameter p){
            switch(p.Type){
                case ParameterType.Int:
                    int[] ia = ClientInfo.GetIntArray(p.content);
                    StringBuilder sb = new StringBuilder();
                    foreach(int i in ia) sb.Append(i + " ");
                    return sb.ToString();
                case ParameterType.Uint:
                    ia = ClientInfo.GetIntArray(p.content);
                    sb = new StringBuilder();
                    foreach(int i in ia) sb.Append(i.ToString("X8") + " ");
                    return sb.ToString();
                case ParameterType.Double:
                    double[] da = ClientInfo.GetDoubleArray(p.content);
                    sb = new StringBuilder();
                    foreach(double d in da) sb.Append(d + " ");
                    return sb.ToString();
                case ParameterType.String:
                    return Encoding.UTF8.GetString(p.content);
                case ParameterType.StringArray:
                    string[] sa = ClientInfo.GetStringArray(p.content, Encoding.UTF8);
                    sb = new StringBuilder();
                    foreach(string s in sa) sb.Append(s + "; ");
                    return sb.ToString();
                case ParameterType.Byte:
                    sb = new StringBuilder();
                    foreach(int b in p.content) sb.Append(b.ToString("X2") + " ");
                    return sb.ToString();
                default: return "??";
            }
        }
    }

    [Serializable]
    public struct Parameter {
        public byte Type;
        public byte[] content;

        public Parameter(byte[] content, byte type){
            this.content = content; Type = type;
        }
    }

    public struct ParameterType {
        public const byte Unparameterised = 0;
        public const byte Int = 1;
        public const byte Uint = 2;
        public const byte String = 3;
        public const byte Byte = 4;
        public const byte StringArray = 5;
        public const byte Double = 6;
        public const byte Long = 7;
    }
}

namespace RedCorona.Cryptography {
    // Cryptographic classes
    public abstract class BaseCrypto : ICryptoTransform {
        public int InputBlockSize { get { return 1; } }
        public int OutputBlockSize { get { return 1; } }
        public bool CanTransformMultipleBlocks { get { return true; } }
        public bool CanReuseTransform { get { return true; } }        

        protected byte[] key;
        protected byte currentKey;
        protected int done = 0, keyinx = 0;

        protected BaseCrypto(byte[] key){
            if(key.Length == 0) throw new ArgumentException("Must provide a key");
            this.key = key;
            currentKey = 0;
            for(int i = 0; i < key.Length; i++) currentKey += key[i];
        }

        protected abstract byte DoByte(byte b);
        public int TransformBlock(byte[] from, int frominx, int len, byte[] to, int toinx){
            #if DEBUG
            Console.WriteLine("Starting transform, key is "+currentKey);
            #endif
            for(int i = 0; i < len; i++){
                byte oldkey = currentKey;
                to[toinx + i] = DoByte(from[frominx + i]);
                #if DEBUG
                Console.WriteLine("  encrypting "+from[frominx + i]+" to "+to[toinx + i]+", key is "+oldkey);
                #endif
                BumpKey();
            }
            return len;
        }
        public byte[] TransformFinalBlock(byte[] from, int frominx, int len){
            byte[] to = new byte[len];
            TransformBlock(from, frominx, len, to, 0);
            return to;
        }
        protected void BumpKey(){
            keyinx = (keyinx + 1) % key.Length;
            currentKey = Multiply257(key[keyinx], currentKey);
        }

        protected static byte Multiply257(byte a, byte b){
             return (byte)((((a + 1) * (b + 1)) % 257) - 1);
        }

        protected static byte[] complements = {0,128,85,192,102,42,146,224,199,179,186,149,177,201,119,240,120,99,229,89,48,221,189,74,71,88,237,100,194,59,198,248,147,188,234,49,131,114,144,44,162,152,5,110,39,94,174,165,20,35,125,172,96,118,242,178,247,225,60,29,58,227,101,252,86,73,233,222,148,245,180,24,168,65,23,185,246,200,243,150,164,209,95,204,126,2,64,183,25,19,208,175,151,215,45,82,52,138,134,17,27,62,4,214,163,176,244,187,223,249,43,217,115,123,37,112,133,158,53,14,16,157,139,113,219,50,84,254,1,171,205,36,142,116,98,239,241,202,97,122,143,218,132,140,38,212,6,32,68,11,79,92,41,251,193,228,238,121,117,203,173,210,40,104,80,47,236,230,72,191,253,129,51,160,46,91,105,12,55,9,70,232,190,87,231,75,10,107,33,22,182,169,3,154,28,197,226,195,30,8,77,13,137,159,83,130,220,235,90,81,161,216,145,250,103,93,211,111,141,124,206,21,67,108,7,57,196,61,155,18,167,184,181,66,34,207,166,26,156,135,15,136,54,78,106,69,76,56,31,109,213,153,63,170,127,255};
        protected static byte Complement257(byte b){ return complements[b]; }

        public void Dispose(){} // for IDisposable
    }

    public class SimpleEncryptor : BaseCrypto {
        public SimpleEncryptor(byte[] key) : base(key) {}

        protected override byte DoByte(byte b){
            byte b2 = Multiply257((byte)(b+currentKey), currentKey);
            currentKey = Multiply257((byte)(b+b2), currentKey);
            return b2;
        }
    }
    public class SimpleDecryptor : BaseCrypto {
        public SimpleDecryptor(byte[] key) : base(key) {}

        protected override byte DoByte(byte b){
            byte b2 = (byte)(Multiply257(b, Complement257(currentKey)) - currentKey);
            currentKey = Multiply257((byte)(b+b2), currentKey);
            return b2;
        }
    }
}

HTTP.cs内容如下:

//#define DEBUG
// Embedded HTTP server, partial 1.1 support with keep-alive and session management
// REQUIRES: Sockets.dll (or Sockets.cs)

// v1.0

// HttpServer: Main class that implements a HTTP server on top of Sockets server.
//   Includes session management via cookies, keep-alive, UTF8 transfer of text
//   and simple query string parsing (via GET or POST).
// HttpRequest: Represents the request made by the user, with header fields,
//   query string and cookies pre-parsed. Typical server code will read the
//   properties of this request to determine what to do.
// HttpResponse: The response which is to be sent back to the user. A simple
//   server will set the Content property, but you can send binary information
//   via RawContent, and change the mime type or response code.
// Session: A container into which you may put state information that will be
//  available in future requests from the same user.
// IHttpHandler: You must implement this interface in order to process
//   requests.
// SubstitutingFileHandler: An implementation of IHttpHandler that reads files
//   from disk and allows substitutions of <%pseudotags> within text documents.

// (C) Richard Smith 2008
//   [email protected]
// If downloaded from CodeProject, this file is subject to the CodeProject Open Licence 1.0.
// If downloaded from elsewhere, you may freely distribute the source code, as long as this
// header is not removed or modified. You may not charge for the source code or any compiled
// library that includes this class; however you may link to it from commercial software. Please
// leave a credit to the original download location in your documentation or About box.

// Simple HTTP server
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;

namespace RedCorona.Net
{
    public class HttpServer
    {
        Server s;
        Hashtable hostmap = new Hashtable();    // Map<string, string>: Host => Home folder
        ArrayList handlers = new ArrayList();        // List<IHttpHandler>
        Hashtable sessions = new Hashtable();        // Map<string,Session>

        int sessionTimeout = 600;

        public Hashtable Hostmap { get { return hostmap; } }
        public Server Server { get { return s; } }
        public ArrayList Handlers { get { return handlers; } }
        public int SessionTimeout
        {
            get { return sessionTimeout; }
            set { sessionTimeout = value; CleanUpSessions(); }
        }

        public HttpServer(Server s)
        {
            this.s = s;
            s.Connect += new ClientEvent(ClientConnect);
            handlers.Add(new FallbackHandler());
        }

        bool ClientConnect(Server s, ClientInfo ci)
        {
            ci.Delimiter = "\r\n\r\n";
            ci.Data = new ClientData(ci);
            ci.OnRead += new ConnectionRead(ClientRead);
            ci.OnReadBytes += new ConnectionReadBytes(ClientReadBytes);
            return true;
        }

        void ClientRead(ClientInfo ci, string text)
        {
            // Read header, if in right state
            ClientData data = (ClientData)ci.Data;
            if (data.state != ClientState.Header) return; // already done; must be some text in content, which will be handled elsewhere
            text = text.Substring(data.headerskip);
            Console.WriteLine("Read header: " + text + " (skipping first " + data.headerskip + ")");
            data.headerskip = 0;
            string[] lines = text.Replace("\r\n", "\n").Split(‘\n‘);
            data.req.HeaderText = text;
            // First line: METHOD /path/url HTTP/version
            string[] firstline = lines[0].Split(‘ ‘);
            if (firstline.Length != 3) { SendResponse(ci, data.req, new HttpResponse(400, "Incorrect first header line " + lines[0]), true); return; }
            if (firstline[2].Substring(0, 4) != "HTTP") { SendResponse(ci, data.req, new HttpResponse(400, "Unknown protocol " + firstline[2]), true); return; }
            data.req.Method = firstline[0];
            data.req.Url = firstline[1];
            data.req.HttpVersion = firstline[2].Substring(5);
            int p;
            for (int i = 1; i < lines.Length; i++)
            {
                p = lines[i].IndexOf(‘:‘);
                if (p > 0) data.req.Header[lines[i].Substring(0, p)] = lines[i].Substring(p + 2);
                else Console.WriteLine("Warning, incorrect header line " + lines[i]);
            }
            // If ? in URL, split out query information
            p = firstline[1].IndexOf(‘?‘);
            if (p > 0)
            {
                data.req.Page = data.req.Url.Substring(0, p);
                data.req.QueryString = data.req.Url.Substring(p + 1);
            }
            else
            {
                data.req.Page = data.req.Url;
                data.req.QueryString = "";
            }

            if (data.req.Page.IndexOf("..") >= 0) { SendResponse(ci, data.req, new HttpResponse(400, "Invalid path"), true); return; }

            if (!data.req.Header.TryGetValue("Host", out data.req.Host)) { SendResponse(ci, data.req, new HttpResponse(400, "No Host specified"), true); return; }

            string cookieHeader;
            if (data.req.Header.TryGetValue("Cookie", out cookieHeader))
            {
                string[] cookies = cookieHeader.Split(‘;‘);
                foreach (string cookie in cookies)
                {
                    p = cookie.IndexOf(‘=‘);
                    if (p > 0)
                    {
                        data.req.Cookies[cookie.Substring(0, p).Trim()] = cookie.Substring(p + 1);
                    }
                    else
                    {
                        data.req.Cookies[cookie.Trim()] = "";
                    }
                }
            }

            string contentLengthString;
            if (data.req.Header.TryGetValue("Content-Length", out contentLengthString))
                data.req.ContentLength = Int32.Parse(contentLengthString);
            else data.req.ContentLength = 0;

            //if(data.req.ContentLength > 0){
            data.state = ClientState.PreContent;
            data.skip = text.Length + 4;
            //} else DoProcess(ci);

            //ClientReadBytes(ci, new byte[0], 0); // For content length 0 body
        }

        public string GetFilename(HttpRequest req)
        {
            string folder = (string)hostmap[req.Host];
            if (folder == null) folder = "webhome";
            if (req.Page == "/") return folder + "/index.html";
            else return folder + req.Page;
        }

        void DoProcess(ClientInfo ci)
        {
            ClientData data = (ClientData)ci.Data;
            string sessid;
            if (data.req.Cookies.TryGetValue("_sessid", out sessid))
                data.req.Session = (Session)sessions[sessid];
            bool closed = Process(ci, data.req);
            data.state = closed ? ClientState.Closed : ClientState.Header;
            data.read = 0;
            HttpRequest oldreq = data.req;
            data.req = new HttpRequest(); // Once processed, the connection will be used for a new request
            data.req.Session = oldreq.Session; // ... but session is persisted
            data.req.From = ((IPEndPoint)ci.Socket.RemoteEndPoint).Address;
        }

        void ClientReadBytes(ClientInfo ci, byte[] bytes, int len)
        {
            CleanUpSessions();
            int ofs = 0;
            ClientData data = (ClientData)ci.Data;
            Console.WriteLine("Reading " + len + " bytes of content, in state " + data.state + ", skipping " + data.skip + ", read " + data.read);
            switch (data.state)
            {
                case ClientState.Content: break;
                case ClientState.PreContent:
                    data.state = ClientState.Content;
                    if ((data.skip - data.read) > len) { data.skip -= len; return; }
                    ofs = data.skip - data.read; data.skip = 0;
                    break;
                //case ClientState.Header: data.read += len - data.headerskip; return;
                default: data.read += len; return;
            }
            data.req.Content += Encoding.Default.GetString(bytes, ofs, len - ofs);
            data.req.BytesRead += len - ofs;
            data.headerskip += len - ofs;
#if DEBUG
            Console.WriteLine("Reading " + (len - ofs) + " bytes of content. Got " + data.req.BytesRead + " of " + data.req.ContentLength);
#endif
            if (data.req.BytesRead >= data.req.ContentLength)
            {
                if (data.req.Method == "POST")
                {
                    if (data.req.QueryString == "") data.req.QueryString = data.req.Content;
                    else data.req.QueryString += "&" + data.req.Content;
                }
                ParseQuery(data.req);
                DoProcess(ci);
            }
        }

        void ParseQuery(HttpRequest req)
        {
            if (req.QueryString == "") return;
            string[] sections = req.QueryString.Split(‘&‘);
            for (int i = 0; i < sections.Length; i++)
            {
                int p = sections[i].IndexOf(‘=‘);
                if (p < 0) req.Query[sections[i]] = "";
                else req.Query[sections[i].Substring(0, p)] = URLDecode(sections[i].Substring(p + 1));
            }
        }

        public static string URLDecode(string input)
        {
            StringBuilder output = new StringBuilder();
            int p;
            while ((p = input.IndexOf(‘%‘)) >= 0)
            {
                output.Append(input.Substring(0, p));
                string hexNumber = input.Substring(p + 1, 2);
                input = input.Substring(p + 3);
                output.Append((char)int.Parse(hexNumber, System.Globalization.NumberStyles.HexNumber));
            }
            return output.Append(input).ToString();
        }

        protected virtual bool Process(ClientInfo ci, HttpRequest req)
        {
            HttpResponse resp = new HttpResponse();
            resp.Url = req.Url;
            //注意,此处从最后添加的Handler开始遍历,如果找到合适的则退出循环
            for (int i = handlers.Count - 1; i >= 0; i--)
            {
                IHttpHandler handler = (IHttpHandler)handlers[i];
                //如果handler.Process返回成功,则直接退出循环
                if (handler.Process(this, req, resp))
                {
                    //SendResponse(ci, req, resp, resp.ReturnCode != 200);
                    SendResponse(ci, req, resp, true);
                    return resp.ReturnCode != 200;
                }
            }
            return true;
        }

        enum ClientState { Closed, Header, PreContent, Content };
        class ClientData
        {
            internal HttpRequest req = new HttpRequest();
            internal ClientState state = ClientState.Header;
            internal int skip, read, headerskip;

            internal ClientData(ClientInfo ci)
            {
                req.From = ((IPEndPoint)ci.Socket.RemoteEndPoint).Address;
            }
        }

        public Session RequestSession(HttpRequest req)
        {
            if (req.Session != null)
            {
                if (sessions[req.Session.ID] == req.Session) return req.Session;
            }
            req.Session = new Session(req.From);
            sessions[req.Session.ID] = req.Session;
            return req.Session;
        }

        void CleanUpSessions()
        {
            ICollection keys = sessions.Keys;
            ArrayList toRemove = new ArrayList();
            foreach (string k in keys)
            {
                Session s = (Session)sessions[k];
                int time = (int)((DateTime.Now - s.LastTouched).TotalSeconds);
                if (time > sessionTimeout)
                {
                    toRemove.Add(k);
                    Console.WriteLine("Removed session " + k);
                }
            }
            foreach (object k in toRemove) sessions.Remove(k);
        }

        // Response stuff
        static Hashtable Responses = new Hashtable();
        static HttpServer()
        {
            Responses[200] = "OK";
            Responses[302] = "Found";
            Responses[303] = "See Other";
            Responses[400] = "Bad Request";
            Responses[404] = "Not Found";
            Responses[500] = "Misc Server Error";
            Responses[502] = "Server Busy";
        }

        void SendResponse(ClientInfo ci, HttpRequest req, HttpResponse resp, bool close)
        {
#if DEBUG
            Console.WriteLine("Response: " + resp.ReturnCode + Responses[resp.ReturnCode]);
#endif
            ByteBuilder bb = new ByteBuilder();
            bb.Add(Encoding.UTF8.GetBytes("HTTP/1.1 " + resp.ReturnCode + " " + Responses[resp.ReturnCode] +
                    "\r\nDate: " + DateTime.Now.ToString("R") +
                    "\r\nServer: RedCoronaEmbedded/1.0" +
                    "\r\nConnection: " + (close ? "close" : "Keep-Alive")));
            if (resp.RawContent == null)
                bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Encoding: utf-8" +
                    "\r\nContent-Length: " + resp.Content.Length));
            else
                bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Length: " + resp.RawContent.Length));
            if (resp.ContentType != null)
                bb.Add(Encoding.UTF8.GetBytes("\r\nContent-Type: " + resp.ContentType));
            if (req.Session != null) bb.Add(Encoding.UTF8.GetBytes("\r\nSet-Cookie: _sessid=" + req.Session.ID + "; path=/"));
            foreach (KeyValuePair<string, string> de in resp.Header) bb.Add(Encoding.UTF8.GetBytes("\r\n" + de.Key + ": " + de.Value));
            bb.Add(Encoding.UTF8.GetBytes("\r\n\r\n")); // End of header
            if (resp.RawContent != null) bb.Add(resp.RawContent);
            else bb.Add(Encoding.UTF8.GetBytes(resp.Content));
            ci.Send(bb.Read(0, bb.Length));
#if DEBUG
            Console.WriteLine("** SENDING\n" + resp.Content);
#endif
            if (close) ci.Close();
        }

        class FallbackHandler : IHttpHandler
        {
            public bool Process(HttpServer server, HttpRequest req, HttpResponse resp)
            {
#if DEBUG
                Console.WriteLine("Processing " + req);
#endif
                server.RequestSession(req);
                StringBuilder sb = new StringBuilder();
                sb.Append("<h3>Session</h3>");
                sb.Append("<p>ID: " + req.Session.ID + "<br>User: " + req.Session.User);
                sb.Append("<h3>Header</h3>");
                sb.Append("Method: " + req.Method + "; URL: ‘" + req.Url + "‘; HTTP version " + req.HttpVersion + "<p>");
                foreach (KeyValuePair<string, string> ide in req.Header) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>");
                sb.Append("<h3>Cookies</h3>");
                foreach (KeyValuePair<string, string> ide in req.Cookies) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>");
                sb.Append("<h3>Query</h3>");
                foreach (KeyValuePair<string, string> ide in req.Query) sb.Append(" " + ide.Key + ": " + ide.Value + "<br>");
                sb.Append("<h3>Content</h3>");
                sb.Append(req.Content);
                resp.Content = sb.ToString();
                return true;
            }
        }
    }

    public class HttpRequest
    {
        public bool GotHeader = false;
        public string Method, Url, Page, HttpVersion, Host, Content, HeaderText, QueryString;
        public IPAddress From;
        //public byte[] RawContent;
        public Dictionary<string, string> Query = new Dictionary<string, string>(), Header = new Dictionary<string, string>(), Cookies = new Dictionary<string, string>();

        public int ContentLength, BytesRead;
        public Session Session;
    }

    public class HttpResponse
    {
        public int ReturnCode = 200;
        public Dictionary<string, string> Header = new Dictionary<string, string>();
        public string Url, Content, ContentType = "text/html";
        public byte[] RawContent = null;

        public HttpResponse() { }
        public HttpResponse(int code, string content) { ReturnCode = code; Content = content; }

        public void MakeRedirect(string newurl)
        {
            ReturnCode = 303;
            Header["Location"] = newurl;
            Content = "This document is requesting a redirection to <a href=" + newurl + ">" + newurl + "</a>";
        }
    }

    public interface IHttpHandler
    {
        bool Process(HttpServer server, HttpRequest request, HttpResponse response);
    }

    public class Session
    {
        string id;
        IPAddress user;
        DateTime lasttouched;

        Hashtable data = new Hashtable();

        public string ID { get { return id; } }
        public DateTime LastTouched { get { return lasttouched; } }
        public IPAddress User { get { return user; } }

        public object this[object key]
        {
            get { return data[key]; }
            set { data[key] = value; Touch(); }
        }

        public Session(IPAddress user)
        {
            this.user = user;
            this.id = Guid.NewGuid().ToString();
            Touch();
        }

        public void Touch() { lasttouched = DateTime.Now; }
    }

    public class SubstitutingFileReader : IHttpHandler
    {
        // Reads a file, and substitutes <%x>
        HttpRequest req;
        bool substitute = true;

        public bool Substitute { get { return substitute; } set { substitute = value; } }

        public static Hashtable MimeTypes;

        static SubstitutingFileReader()
        {
            MimeTypes = new Hashtable();
            MimeTypes[".html"] = "text/html";
            MimeTypes[".htm"] = "text/html";
            MimeTypes[".css"] = "text/css";
            MimeTypes[".js"] = "application/x-javascript";

            MimeTypes[".png"] = "image/png";
            MimeTypes[".gif"] = "image/gif";
            MimeTypes[".jpg"] = "image/jpeg";
            MimeTypes[".jpeg"] = "image/jpeg";
        }

        public virtual bool Process(HttpServer server, HttpRequest request, HttpResponse response)
        {
            string fn = server.GetFilename(request);
            if (!File.Exists(fn))
            {
                response.ReturnCode = 404;
                response.Content = "File not found.";
                return true;
            }
            string ext = Path.GetExtension(fn);
            string mime = (string)MimeTypes[ext];
            if (mime == null) mime = "application/octet-stream";
            response.ContentType = mime;
            try
            {
                if (mime.Substring(0, 5) == "text/")
                {
                    // Mime type ‘text‘ is substituted
                    StreamReader sr = new StreamReader(fn);
                    response.Content = sr.ReadToEnd();
                    sr.Close();
                    if (substitute)
                    {
                        // Do substitutions
                        Regex regex = new Regex(@"\<\%(?<tag>[^>]+)\>");
                        lock (this)
                        {
                            req = request;
                            response.Content = regex.Replace(response.Content, new MatchEvaluator(RegexMatch));
                        }
                    }
                }
                else
                {
                    FileStream fs = File.Open(fn, FileMode.Open);
                    byte[] buf = new byte[fs.Length];
                    fs.Read(buf, 0, buf.Length);
                    fs.Close();
                    response.RawContent = buf;
                }
            }
            catch (Exception e)
            {
                response.ReturnCode = 500;
                response.Content = "Error reading file: " + e;
                return true;
            }
            return true;
        }

        public virtual string GetValue(HttpRequest req, string tag)
        {
            return "<span class=error>Unknown substitution: " + tag + "</span>";
        }

        string RegexMatch(Match m)
        {
            try
            {
                return GetValue(req, m.Groups["tag"].Value);
            }
            catch (Exception e)
            {
                return "<span class=error>Error substituting " + m.Groups["tag"].Value + "</span>";
            }
        }
    }

    public enum HMethod
    {
        GET,
        POST,
        DELETE,
        PUT
    }
    /// <summary>
    /// RESTfull处理器的接口
    /// </summary>
    public interface IRESTfulHandler
    {
        bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param);
    }
    public class RESTfulApiHandlerBase : IHttpHandler
    {
        public RESTfulApiHandlerBase(HMethod method, string sUrl, List<string> param, IRESTfulHandler handler)
        {
            m_method = method;
            m_sUrl = sUrl;
            m_listParam = param;
            m_handler = handler;
        }
        private HMethod m_method = HMethod.GET;
        private string m_sUrl = "";
        private List<string> m_listParam = new List<string>();
        private IRESTfulHandler m_handler = null;

        public bool Process(HttpServer server, HttpRequest request, HttpResponse response)
        {
            if (request.Method != m_method.ToString())
                return false;
            if (!request.Page.Equals(m_sUrl, StringComparison.CurrentCultureIgnoreCase))
                return false;

            //处理查询参数
            string sQueryString = request.QueryString;
            if (m_method == HMethod.DELETE || m_method == HMethod.PUT)
                sQueryString = request.Content;

            Dictionary<string, string> queryParam = GetParam(sQueryString);
            if (queryParam.Count != m_listParam.Count)
                return false;

            HashSet<string> set1 = new HashSet<string>(m_listParam);
            HashSet<string> set2 = new HashSet<string>(queryParam.Keys);
            //两个集合是否相同
            if (!set1.SetEquals(set2))
                return false;

            if (m_handler != null)
                return m_handler.Process(server, request, response, queryParam);
            return false;
        }

        private static Dictionary<String, String> GetParam(String sQueryString)
        {
            Dictionary<String, String> param = new Dictionary<string, string>();
            if (string.IsNullOrEmpty(sQueryString))
                return param;
            string[] ar = sQueryString.Split(new char[] { ‘&‘ });

            foreach (string item in ar)
            {
                int nFind = item.IndexOf(‘=‘);
                string sKey = item.Substring(0, nFind);
                string sValue = item.Substring(nFind + 1, item.Length - nFind - 1);
                param[sKey] = sValue;

            }

            return param;
        }
    }
}

将Program.cs中代码修改如下:

using RedCorona.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpServer http = new HttpServer(new Server(800));
            http.Handlers.Add(new SubstitutingFileReader());
            http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.GET, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientGetHander()));
            http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.POST, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientPostHander()));
            http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.DELETE, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientDeleteHander()));
            http.Handlers.Add(new RESTfulApiHandlerBase(HMethod.PUT, "/api/patientinfo", new List<string>() { "Function", "UserJID" }, new PatientPutHander()));
            Console.WriteLine("服务已启动,点击任何按键退出");
            Console.ReadKey();
        }
    }

    public class PatientGetHander : IRESTfulHandler
    {

        public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param)
        {
            Console.WriteLine("PatientGetHander 完成!");
            response.Content = "PatientGetHander 完成!";
            return true;
        }
    }

    public class PatientDeleteHander : IRESTfulHandler
    {

        public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param)
        {
            Console.WriteLine("PatientDeleteHander 完成!");
            response.Content = "PatientDeleteHander 完成!";
            return true;
        }
    }

    public class PatientPostHander : IRESTfulHandler
    {

        public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param)
        {
            Console.WriteLine("PatientPostHander 完成!");
            response.Content = "PatientPostHander 完成!";
            return true;
        }
    }

    public class PatientPutHander : IRESTfulHandler
    {

        public bool Process(HttpServer server, HttpRequest request, HttpResponse response, Dictionary<string, string> param)
        {
            Console.WriteLine("PatientPutHander 完成!");
            response.Content = "PatientPutHander 完成!";
            return true;
        }
    }
}

运行应用程序,可以发送POST/GET/PUT/DELETE命令进行测试。

时间: 2024-11-05 16:06:38

在.NET程序中实现HttpServer功能的相关文章

在程序中实现定位功能

Core Location是iOS SDK中一个提供设备位置的框架.可以使用三种技术来获取位置:GPS.蜂窝或WiFi.在这些技术中,GPS最为精准,如果有GPS硬件,Core Location将优先使用它.如果设备没有GPS硬件(如WiFi iPad)或使用GPS获取当前位置时失败,Core Location将退而求其次,选择使用蜂窝或WiFi. Core Location的大多数功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器来指定位置更新的频率和精度,以及

HOW TO: 在 Visual C# .NET 应用程序中提供文件拖放功能

本文假定您熟悉下列主题: Windows 窗体列表框控件 Windows 窗体事件处理 生成示例的步骤 列表框控件提供了您需要处理的两个拖放事件: DragEnter 和 DragDrop. 当您在控件的边界内拖动对象时,便会发生DragEnter 事件:该事件用于确定当前拖动的对象是不是您要放到控件上的对象. 在将一个或多个文件拖到控件上时,需要处理此事件. 这使得在将对象拖到控件上方时,能够根据所拖动的对象显示相应的图标. 将拖动的对象释放到控件上时,会发生DragDrop 事件. 通过处理

ios 定位新功能----在程序中实现定位功能

Core Location是iOS SDK中一个提供设备位置的框架.可以使用三种技术来获取位置:GPS.蜂窝或WiFi.在这些技术中,GPS最为精准,如果有GPS硬件,Core Location将优先使用它.如果设备没有GPS硬件(如WiFi iPad)或使用GPS获取当前位置时失败,Core Location将退而求其次,选择使用蜂窝或WiFi. Core Location的大多数功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器来指定位置更新的频率和精度,以及

微信小程序中悬浮窗功能的实现(主要探讨和解决在原生组件上的拖动)

问题场景 所谓悬浮窗就是图中微信图标的按钮,采用fixed定位,可拖动和点击. 这算是一个比较常见的实现场景了. 为什么要用cover-view做悬浮窗?原生组件出来背锅了~ 最初我做悬浮窗用的不是cover-view,而是view. 这是简化的代码结构: index.wxml: <view class="move-view" style=" top:{{top}}px;left:{{left}}px;" bindtap="goToHome"

在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能

在我上篇随笔<在DevExpress程序中使用Winform分页控件直接录入数据并保存>中介绍了在GridView以及在其封装的分页控件上做数据的直接录入的处理,介绍情况下数据的保存和校验等操作,不过还没有涉及到数据列表选择的这种方式,而这种在项目应用也是比较广泛的一种输入方式.本篇随笔继续探讨在GridView上直接录入数据,并增加字典选择列表的功能. 1.GridView直接录入数据回顾 在之前整合的数据录入案例里面,我们可以看到可以在列表里面直接录入速度的便捷性,如下所示. 1)直接在G

VC++编程中为程序加入启动画面功能

 如何为自己的程序加入启动画面 观察我们平常使用的软件,当我们双击软件的时候,会在主界面出现前,先行出现一个启动画面,由于前一阵子写了一个基于对话框的程序,亲自实验了下,今天就为大家简单的介绍下,在我们的程序中如何实现增加启动画面的功能. 在这里说明说明一下,我们平常使用的编译器是VS2010 或者是VS2012,在早一点的版本中,例如vc6.0中,加入启动画面这一功能,编译器已经为我们封装好,我们直接使用他所提供的CSplashWnd类就行了.单击[Project\Add to Project

MVC应用程序中,怎样控制与复制相同的功能

先看此篇<MVC程序实现Autocomplete功能> http://www.cnblogs.com/insus/p/3546255.html 它是实现使用jQuery实现文本框输入文字,出现下拉列表,自动完成与选择功能. 相信你的开发的MVC应用程序中,不止有唯一一个此AutoComplete功能.如果有多个时,或是不断新加时,你的Handlers目录下,肯定会出现多个ashx文件来支持.每增加一个,就会去复制然后修改它.因为这个ashx内处理的是不同类各自的数据. 因此让Insus.NET

在ASP.NET 5应用程序中的跨域请求功能详解

在ASP.NET 5应用程序中的跨域请求功能详解 浏览器安全阻止了一个网页中向另外一个域提交请求,这个限制叫做同域策咯(same-origin policy),这组织了一个恶意网站从另外一个网站读取敏感数据,但是一些特殊情况下,你需要允许另外一个站点跨域请求你的网站. 跨域资源共享(CORS:Cross Origin Resources Sharing)是一个W3C标准,它允许服务器放宽对同域策咯的限制,使用CORS,服务器可以明确的允许一些跨域的请求,并且拒绝其它的请求.CORS要比JSONP

WinForm应用程序中实现自动更新功能

WinForm应用程序中实现自动更新功能 编写人:左丘文 2015-4-20 近来在给一客户实施ECM系统,但他们使用功能并不是我们ECM制造版提供的标准功能,他们要求对系统作一些定制功能,为了避免因程序的bug而带来频繁让用户更新程序的不良影响,就想给ECM增加一个winform自动更新功能,今天在这里,我想与大家一起分享代码,在此做个小结,以供参考.有兴趣的同学,可以一同探讨与学习一下,否则就略过吧.   1. 首先我们在这里先分析一下其它程序猿的一些基本情况: 相信有许多程序猿都喜欢用Wi