v9.7.2018.0421   支持运行时修改DAL连接字符串
大石头 编写于 2018-04-21 14:00:47
X
using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text;
using NewLife.Data;

namespace XCoder.XNet
{
    #region 分析器
    abstract class MySqlParser
    {
        public Action<MySqlPacket> OnParsedPacket;
        public Action OnParsedError;

        public DateTime LastTime { get; set; }
        public IPEndPoint LastSrcEP { get; set; }
        public IPEndPoint LastDstEP { get; set; }

        public abstract void Parse(Packet pk);

        //The Packet Header
        //Bytes                 Name
        // -----                 ----
        // 3                     Packet Length
        // 1                     Packet Number

        // Packet Length: The length, in bytes, of the packet
        //                that follows the Packet Header. There
        //                may be some special values in the most
        //                significant byte. The maximum packet 
        //                length is (2**24 -1),about 16MB.

        // Packet Number: A serial number which can be used to
        //                ensure that all packets are present
        //                and in order. The first packet of a
        //                client query will have Packet Number = 0
        //                Thus, when a new SQL statement starts, 
        //                the packet number is re-initialised.

        protected void ReadPacketHeader(Byte[] header, out Int32 packetLength, out Int32 packetNum)
        {
            if (header == null || header.Length != 4) throw new ArgumentException();

            packetLength = header[0] + (header[1] << 8) + (header[3] << 16);
            packetNum = header[3];
        }
    }

    class MySqlQueryParser : MySqlParser
    {
        private enum ParsingState
        {
            Head,
            Body
        }

        private ParsingState _state;
        private MySqlQueryPacket _packet;

        public MySqlQueryParser()
        {
            _state = ParsingState.Head;
            _packet = new MySqlQueryPacket();
        }

        public override void Parse(Packet pk)
        {
            TryParse(pk);
        }

        private void TryParse(Packet pk)
        {
            if (_state == ParsingState.Head)
            {
                if (pk.Count >= 4)
                {
                    var head = pk.ReadBytes(4);
                    ReadPacketHeader(head, out _packet.Length, out _packet.Num);
                    _state = ParsingState.Body;
                }
                else
                    return;
            }

            if (_state == ParsingState.Body)
            {
                if (pk.Count >= _packet.Length)
                {
                    var body = _packet.Body = pk.ReadBytes(_packet.Length);
                    _packet.Command = (DBCmd)body[0];
                    HandleParseOk();
                }
            }
        }

        private void HandleParseError(String msg)
        {
            _state = ParsingState.Head;
            _packet = new MySqlQueryPacket();

            OnParsedError?.Invoke();
        }

        private void HandleParseOk()
        {
            if (OnParsedPacket != null)
            {
                _packet.Time = LastTime;
                _packet.SrcEP = LastSrcEP;
                _packet.DstEP = LastDstEP;
                OnParsedPacket(_packet);
            }
            _state = ParsingState.Head;
            _packet = new MySqlQueryPacket();
        }
    }
    #endregion

    #region 数据包
    enum PacketType
    {
        Query,
        ResultSet,
    }

    class MySqlPacket
    {
        public DateTime Time { get; set; }
        public IPEndPoint SrcEP { get; set; }
        public IPEndPoint DstEP { get; set; }

        public PacketType PacketType { get; set; }
    }
    #endregion

    #region 结果包
    enum ResultSetType
    {
        OK,
        Error,
        ResultSet,
    }

    class MySqlResultSetPacket : MySqlPacket
    {
        public ResultSetType ResultSetType { get; set; }

        public MySqlResultSetPacket()
        {
            PacketType = PacketType.ResultSet;
        }
    }

    class MySqlOKResultSet : MySqlResultSetPacket
    {
        public Int32 Length;
        public Int32 Num;
        public Int32 AffectRow;
        public Int32 ServerState;
        public String Message;

        public MySqlOKResultSet()
        {
            ResultSetType = ResultSetType.OK;
        }
    }

    class MySqlErrorResultSet : MySqlResultSetPacket
    {
        public Int32 Length;
        public Int32 Num;
        public Int32 ErrorNum;
        public Int32 SqlState;
        public String Message;

        public MySqlErrorResultSet()
        {
            ResultSetType = ResultSetType.Error;
        }
    }

    class MySqlDataResultSet : MySqlResultSetPacket
    {
        public ResultSetHeadPacket HeadPacket;
        public List<ResultSetFieldPacket> FieldPacket;
        public List<ResultSetRowPacket> RowPacket;

        public MySqlDataResultSet()
        {
            ResultSetType = ResultSetType.ResultSet;
        }
    }

    public class ResultSetHeadPacket
    {
        public Int32 PacketLength;
        public Int32 PacketNum;
        public Int32 FieldNum;
    }

    public class ResultSetFieldPacket
    {
        public Int32 PacketLength;
        public Int32 PacketNum;
    }

    public class ResultSetRowPacket
    {
        public Int32 PacketLength;
        public Int32 PacketNum;
    }
    #endregion

    #region 请求包
    class MySqlQueryPacket : MySqlPacket
    {
        public Int32 Length;
        public Int32 Num;
        public Byte[] Body;
        public DBCmd Command;

        private String _commandArgs;
        public String CommandArgs
        {
            get
            {
                if (Command == DBCmd.QUERY && Body != null && Body.Length > 0)
                {
                    if (_commandArgs == null)
                        _commandArgs = Encoding.UTF8.GetString(Body, 1, Body.Length - 1);
                }

                return _commandArgs;
            }
        }

        public MySqlQueryPacket()
        {
            PacketType = PacketType.Query;
        }

        public MySqlCommandArgs ResolveCommand()
        {
            var args = new MySqlCommandArgs
            {
                CmdType = CommandType.Text
            };

            var cmd = CommandArgs;
            if (cmd != null)
            {
                if (cmd.Substring(0, 4) == "CALL")
                {
                    args.CmdType = CommandType.StoredProcedure;

                    var start = cmd.IndexOf("(");
                    if (start > 3) args.ResolveQuery = cmd.Substring(3, start - 3);
                }
                else
                {
                    //todo replace ='xx' to ={}
                }
            }

            return args;
        }
    }

    class MySqlCommandArgs
    {
        public CommandType CmdType { get; set; }
        public String ResolveQuery { get; set; }
    }

    enum DBCmd : Byte
    {
        SLEEP = 0,
        QUIT = 1,
        INIT_DB = 2,
        QUERY = 3,
        FIELD_LIST = 4,
        CREATE_DB = 5,
        DROP_DB = 6,
        RELOAD = 7,
        SHUTDOWN = 8,
        STATISTICS = 9,
        PROCESS_INFO = 10,
        CONNECT = 11,
        PROCESS_KILL = 12,
        DEBUG = 13,
        PING = 14,
        TIME = 15,
        DELAYED_INSERT = 16,
        CHANGE_USER = 17,
        BINLOG_DUMP = 18,
        TABLE_DUMP = 19,
        CONNECT_OUT = 20,
        REGISTER_SLAVE = 21,
        PREPARE = 22,
        EXECUTE = 23,
        LONG_DATA = 24,
        CLOSE_STMT = 25,
        RESET_STMT = 26,
        SET_OPTION = 27,
        FETCH = 28
    }
    #endregion
}