zoukankan      html  css  js  c++  java
  • 在.NET程序中实现HttpServer功能

    在.NET程序中实现HttpServer功能

     

    最近在实现一个可视化数据解析工具,需要在Wpf程序中实现一个HttpServer,实现RESTfull接口,可以接收客户端(DTU或其他嵌入式设备)发送的请求。这种接口在ASP.NET中很容易实现,在Wpf程序中,需要有一个HttpServer才可以,开始考虑将Node.js签入到Wpf中,基本的功能可以实现,但是太麻烦了,程序的可移植性降低了,最好是在Wpf程序中内置这样的一个HttpServer。(当然,单纯的RESTful功能,通过WCF完全可以实现,此处的代码,给大家展示了在socket的基础上,时下基本的http协议,很有意思)

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

    首先在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
    //   bobjanova@gmail.com
    // 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 = "
    ";
                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
    //   bobjanova@gmail.com
    // 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 = "
    
    ";
                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("
    ", "
    ").Split('
    ');
                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] +
                        "
    Date: " + DateTime.Now.ToString("R") +
                        "
    Server: RedCoronaEmbedded/1.0" +
                        "
    Connection: " + (close ? "close" : "Keep-Alive")));
                if (resp.RawContent == null)
                    bb.Add(Encoding.UTF8.GetBytes("
    Content-Encoding: utf-8" +
                        "
    Content-Length: " + resp.Content.Length));
                else
                    bb.Add(Encoding.UTF8.GetBytes("
    Content-Length: " + resp.RawContent.Length));
                if (resp.ContentType != null)
                    bb.Add(Encoding.UTF8.GetBytes("
    Content-Type: " + resp.ContentType));
                if (req.Session != null) bb.Add(Encoding.UTF8.GetBytes("
    Set-Cookie: _sessid=" + req.Session.ID + "; path=/"));
                foreach (KeyValuePair<string, string> de in resp.Header) bb.Add(Encoding.UTF8.GetBytes("
    " + de.Key + ": " + de.Value));
                bb.Add(Encoding.UTF8.GetBytes("
    
    ")); // 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
    " + 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命令进行测试。

     
         
     
     
  • 相关阅读:
    SQLite存储类(数据类型)
    SQLite常用命令
    C# 跨线程操作无效
    Android打开新的Activity并同时关闭当前Activity
    SQLite实现Top功能
    Android调用Sqlite数据库时自动生成db-journal文件的原因
    C#使用SqlDataReader读取数据库数据时CommandBehavior.CloseConnection参数的作用
    Android计算时间差
    PS通道抠图总结
    Android再次激活Activity时触发事件用于列表重新读取载入
  • 原文地址:https://www.cnblogs.com/lhxsoft/p/8613519.html
Copyright © 2011-2022 走看看