zoukankan      html  css  js  c++  java
  • 【转载】支持漫游用户和跨区域功能请求

    http://www.cnblogs.com/zhuweisky/archive/2006/03/27/360124.html

    http://blog.csdn.net/zhuweisky/article/details/4608945

    //-----------------------------------

     #region InstantMessageContract
        public class InstantMessageContract : IContract
        {
            #region Members
            public int MessageTextLen; //消息文本长度
            public string MessageText; //消息文本
            #endregion

            #region Ctor
            private IStringEncoder stringEncoder;
            public InstantMessageContract(IStringEncoder encoder)
            {
                this.stringEncoder = encoder;
            }
            #endregion

            #region IContract 成员

            #region FillMyself
            public void FillMyself(byte[] data, int offset)
            {
                int curOffset = offset;
                this.MessageTextLen = BitConverter.ToInt32(data, curOffset);
                curOffset += 4;
                this.MessageText = this.stringEncoder.GetStrFromStream(data, curOffset, this.MessageTextLen);
                curOffset += this.MessageTextLen;
            }
            #endregion

            #region GetStreamLength
            public int GetStreamLength()
            {
                int totalLen = 4 + this.MessageTextLen;

                return totalLen;
            }
            #endregion

            #region ToStream
            public byte[] ToStream()
            {
                int totalLen = this.GetStreamLength();
                byte[] stream = new byte[totalLen];
                int offset = 0;

                #region FillFields
                byte[] bMessageTextLen = BitConverter.GetBytes(this.MessageTextLen);
                for (int i = 0; i < bMessageTextLen.Length; i++)
                {
                    stream[offset + i] = bMessageTextLen[i];
                }
                offset += 4;

                byte[] bMessageText = this.stringEncoder.GetBytesFromStr(this.MessageText);
                for (int i = 0; i < bMessageText.Length; i++)
                {
                    stream[offset + i] = bMessageText[i];
                }
                offset += bMessageText.Length;

                #endregion
                return stream;
            }
            #endregion

            #region ToStream2
            public void ToStream(byte[] buff, int offset)
            {
                byte[] stream = this.ToStream();
                for (int i = 0; i < stream.Length; i++)
                {
                    buff[offset + i] = stream[i];
                }
            }
            #endregion
            #endregion
        }
        #endregion

    //------------------------------------

     [Serializable]
        public class ContractHelper : IContractHelper, IServiceKeyNameMatcher, ITcpPassiveHelper, IStringEncoder
        {
            private int serverType = 0x1b;

            public IMessageHeader CreateMessageHeader(string userID, int serviceKey, int bodyLen)
            {
                MessageHeader header = new MessageHeader(this);
                header.UserID = userID;
                header.UserIDLen = this.GetBytesFromStr(userID).Length;
                header.ServiceKey = serviceKey;
                header.MessageBodyLength = bodyLen;
                return header;
            }

            public byte[] GetBytesFromStr(string ss)
            {
                if (ss == null)
                {
                    return new byte[0];
                }
                return Encoding.Default.GetBytes(ss);
            }

            public string GetFailureMsg(int serviceResultType)
            {
                return ServiceResultType.MatchServiceResultType(serviceResultType);
            }

            public NetMessage GetResponseByServiceResultType(NetMessage reqMsg, int serviceResultType)
            {
                IMessageHeader header = (IMessageHeader) reqMsg.Header.Clone();
                header.MessageBodyLength = 0;
                header.Result = serviceResultType;
                return new NetMessage(header, null);
            }

            public string GetServiceName(int serviceKey)
            {
                return serviceKey.ToString();
            }

            public ServiceType GetServiceType(int serviceKey)
            {
                if ((serviceKey >= 20) && (serviceKey < 39))
                {
                    return ServiceType.Basic;
                }
                if ((serviceKey >= 40) && (serviceKey < 59))
                {
                    return ServiceType.P2PMessage;
                }
                if ((serviceKey >= 60) && (serviceKey < 89))
                {
                    return ServiceType.FriendRelation;
                }
                if ((serviceKey > 900) && (serviceKey < 999))
                {
                    return ServiceType.Function;
                }
                return ServiceType.CustomServiceType;
            }

            public string GetStrFromStream(byte[] stream, int offset, int len)
            {
                if (stream == null)
                {
                    return null;
                }
                return Encoding.Default.GetString(stream, offset, len);
            }

            public IMessageHeader ParseMessageHeader(byte[] data, int offset)
            {
                MessageHeader header = new MessageHeader(this);
                header.FillMyself(data, offset);
                return header;
            }

            public bool ValidateMessageToken(IMessageHeader header)
            {
                return (((MessageHeader) header).Token == "@@@@");
            }

            public bool VerifyFirstMessage(NetMessage msg)
            {
                return true;
            }

            public bool VerifyOtherMessage(NetMessage msg)
            {
                return true;
            }

            public int MessageHeaderLength
            {
                get
                {
                    return 96;
                }
            }

            public int ServerType
            {
                get
                {
                    return this.serverType;
                }
                set
                {
                    this.serverType = value;
                }
            }
        }

    //-------------------------------------------

    public class ContractParser : IPassiveHelper
        {
            #region property
            #region ContractHelper
            private IContractHelper contractHelper;
            public IContractHelper ContractHelper
            {
                set
                {
                    this.contractHelper = value;
                }
            }
            #endregion 
          
            #endregion

            #region ParseBody
            public IContract ParseBody(int serviceKey, byte[] data)
            {
                return this.ParseBody(serviceKey, data, 0);
            }

            public IContract ParseBody(int serviceKey, byte[] data, int offset)
            {           
                if ((data == null) || (data.Length <= offset))
                {
                    return null;
                }

                if (this.contractHelper.GetServiceType(serviceKey) == ServiceType.P2PMessage)
                {
                    IContract contract = P2PRequestType.GetEmptyContract(serviceKey, this.contractHelper);
                    if (contract != null)
                    {
                        contract.FillMyself(data, offset);
                    }
                    return contract;
                }

                if (this.contractHelper.GetServiceType(serviceKey) == ServiceType.FriendRelation)
                {
                    IContract contract = FriendRelationRequestType.GetEmptyContract(serviceKey, this.contractHelper);
                    if (contract != null)
                    {
                        contract.FillMyself(data, offset);
                    }
                    return contract;
                }

                return null;
            }
            #endregion

            #region ParseServiceFailureBody
            public IServiceFailureBody ParseServiceFailureBody(byte[] body)
            {
                return null;
            }
            #endregion    
     
            #region IContractParser Members

            public PassiveMessageType GetPassiveMessageType(IMessageHeader header)
            {
                int serviceKey = header.ServiceKey;

                if (header.P2PAck)
                {
                    return PassiveMessageType.Ack;
                }

                if (this.contractHelper.GetServiceType(serviceKey) == ServiceType.P2PMessage)
                {
                    return PassiveMessageType.P2PMessage;
                }

                if ((serviceKey == DataCenterBase.RequestTypeDefinition.LogonAgin) || ((serviceKey == FriendRelationRequestType.ChangeMyState)))
                {
                    return PassiveMessageType.Notify;
                }

                if ((this.contractHelper.GetServiceType(serviceKey) == ServiceType.Basic) || (this.contractHelper.GetServiceType(serviceKey) == ServiceType.FriendRelation) || (this.contractHelper.GetServiceType(serviceKey) == ServiceType.Function))
                {
                    return PassiveMessageType.Response;
                }

                return PassiveMessageType.Response;

            }

            #endregion
        }

     //-------------------------------------

     public class ServerProxy
        {
            #region property

            #region MyID
            private string myID = "";
            public string MyID
            {
                set
                {
                    this.myID = value;
                }
            }
            #endregion

            #region DataEntryToUserDb
            private IDataEntry dataEntryToUserDb = null;
            public IDataEntry DataEntryToUserDb
            {          
                set
                {
                    this.dataEntryToUserDb = value;
                }
            }
            #endregion

            #region ContractParser
            private ContractParser contractParser = null;
            public  ContractParser ContractParser
            {
                set
                {
                    this.contractParser = value;
                }
            }
            #endregion

            #region ContractHelper
            private IContractHelper contractHelper = null;
            public IContractHelper  ContractHelper
            {
                get
                {
                    return this.contractHelper;
                }
                set
                {
                    this.contractHelper = value;
                }
            }
            #endregion

            #region TcpServerAgent
            private ITcpServerAgent tcpServerAgent = null;
            public ITcpServerAgent TcpServerAgent
            {
                get
                {
                    return this.tcpServerAgent;
                }
                set
                {
                    this.tcpServerAgent = value;
                }
            }
            #endregion

            #region FilePackageSize
            private int filePackageSize = 2000;//bytes
            public int FilePackageSize
            {
                set
                {
                    this.filePackageSize = value;
                }
            }
            #endregion
            #endregion

            #region Friends
            public ArrayList GetAllFriends()
            {
                ArrayList list = new ArrayList();
                ClientUser[] friends = (ClientUser[])this.dataEntryToUserDb.GetObjects(typeof(ClientUser), "") ;
                if (friends != null)
                {
                    foreach (ClientUser friend in friends)
                    {
                        list.Add(friend);
                    }
                }

                return list;
            }

            public ClientUser GetUser(string userID)
            {
                return (ClientUser)this.dataEntryToUserDb.GetAObjectEspecial(typeof(ClientUser), userID);
            }

            public Hashtable GetAllOnlineFriendStatus() //ID -- status
            {
                Hashtable ht = new Hashtable();
                IMessageHeader header = this.CreateMessageHeader(FriendRelationRequestType.FriendList, 0);

                NetMessage result = this.tcpServerAgent.CommitRequest(new Message(header ,null) ,DataPriority.Common, true);
                if (result.Header.Result != ServiceResultType.ServiceSucceed)
                {
                    return ht;
                }

                FriendsStatusContract contract = (FriendsStatusContract)this.contractParser.ParseBody(result.Header.ServiceKey ,result.Body ,result.BodyOffset);
                foreach (TheStatus theStatus in contract.theTheStatuss)
                {
                    ht.Add(theStatus.FriendID, theStatus.Status);
                }

                return ht;
            }
            #endregion

            #region Basic
            #region CreateMessageHeader
            public MessageHeader CreateMessageHeader(int serviceKey, int bodyLen)
            {
                return this.CreateMessageHeader(serviceKey, bodyLen, null);
            }

            public MessageHeader CreateMessageHeader(int serviceKey, int bodyLen ,string destID)
            {
                MessageHeader header = (MessageHeader)((ITcpPassiveHelper)this.contractHelper).CreateMessageHeader(this.myID, serviceKey, bodyLen);
                if (destID != null)
                {
                    header.DestUserID = destID;
                    header.DestUserIDLen = this.contractHelper.GetBytesFromStr(destID).Length;
                }

                return header;
            }
            #endregion

            #region GetFailureMsg
            public string GetFailureMsg(int resultKey)
            {
                return ((ITcpPassiveHelper)this.contractHelper).GetFailureMsg(resultKey);
            }
            #endregion

            #endregion

            #region Logon
            public bool Logon()
            {
                MessageHeader header = this.CreateMessageHeader(RequestTypeDefinition.Logon, 0);
                NetMessage respond = this.tcpServerAgent.CommitRequest(new Message(header, null), DataPriority.Common, true);

                return respond.Header.Result == ServiceResultType.ServiceSucceed;          
            }

            public void Logout()
            {
                MessageHeader header = this.CreateMessageHeader(RequestTypeDefinition.Logout, 0);
                this.tcpServerAgent.CommitRequest(new Message(header, null), DataPriority.Common, false);          
            }
            #endregion      

            #region SendCheckMessage
            public void SendCheckMessage()
            {
                MessageHeader header = this.CreateMessageHeader(RequestTypeDefinition.Check, 0);
                this.tcpServerAgent.CommitRequest(new Message(header, null), DataPriority.High, false);
            }
            #endregion

            #region SendInstantMsg
            public void SendInstantMsg(string friendID, string msg)
            {
                InstantMessageContract contract = new InstantMessageContract(this.contractHelper);
                contract.MessageText = msg;
                contract.MessageTextLen = this.contractHelper.GetBytesFromStr(contract.MessageText).Length;

                MessageHeader header = (MessageHeader)((ITcpPassiveHelper)this.contractHelper).CreateMessageHeader(this.myID, P2PRequestType.InstantMessage, contract.GetStreamLength());
                header.DestUserID = friendID;
                header.DestUserIDLen = this.contractHelper.GetBytesFromStr(header.DestUserID).Length;
                this.tcpServerAgent.CommitRequest(new ESFramework.Network.Message(header, contract), DataPriority.Common, false);
            }
            #endregion

            #region File
            #region SendTransFileQuery
            public void SendTransFileQuery(string destID, string fileName, int fileSize)
            {
                FileTransmitQueryContract contract = (FileTransmitQueryContract)P2PRequestType.GetEmptyContract(P2PRequestType.FileTransmitQuery, this.contractHelper);
                contract.FileName = fileName;
                contract.FileNameLen = this.contractHelper.GetBytesFromStr(fileName).Length;
                contract.FileLength = fileSize;

                MessageHeader header = this.CreateMessageHeader(P2PRequestType.FileTransmitQuery ,contract.GetStreamLength() ,destID) ;

                this.tcpServerAgent.CommitRequest(new Message(header, contract), DataPriority.Common, false);
            }
            #endregion

            #region CreateFileTransmitter
            public IFileTransmitter CreateFileTransmitter(string friendID, string filePath)
            {           
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.FileTransmit, 0, friendID);
                FilePackageContract contract = new FilePackageContract(this.contractHelper);
                Message packMsg = new Message(header, contract);
                IFileTransmitter transmitter = new FileTransmitter(this.tcpServerAgent, (ITcpPassiveHelper)this.contractHelper, packMsg, filePath);
                //transmitter.AckChecked = true;
                transmitter.FileDataPriority = DataPriority.Low;
                transmitter.SendingSpan = 100;
                transmitter.PackageSize = this.filePackageSize;

                return transmitter;
            }
            #endregion

            #region SendFileTransmitRespond
            public void SendFileTransmitRespond(string destID ,bool agree)
            {
                FileTransmitRespondContract contract = (FileTransmitRespondContract)P2PRequestType.GetEmptyContract(P2PRequestType.FileTransmitRespond, this.contractHelper);
                contract.Accept = agree ? 1 : 0;
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.FileTransmitRespond, contract.GetStreamLength(), destID);
                this.tcpServerAgent.CommitRequest(new Message(header, contract),DataPriority.Common , false);
            }
            #endregion

            #region SendCancelFileTransmit
            public void SendCancelFileTransmit(string destID)
            {
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.FileTransmitCancel, 0, destID);
                this.tcpServerAgent.CommitRequest(new Message(header, null),DataPriority.Common , false);
                this.tcpServerAgent.ClearQueue(DataPriority.Low);
            }
            #endregion
            #endregion

            #region AV
            public void SendAVRespond(bool agree ,string destID)
            {
                AVRespondContract contract = (AVRespondContract)P2PRequestType.GetEmptyContract(P2PRequestType.AVRespond, this.contractHelper);
                contract.Agree = agree ? 1 : 0;
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.AVRespond, contract.GetStreamLength(), destID);
                this.tcpServerAgent.CommitRequest(new Message(header, contract), DataPriority.Common, false);
           
            }

            public void SendAVQuery(string destID)
            {
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.AVQuery, 0 ,destID);
                this.tcpServerAgent.CommitRequest(new Message(header, null), DataPriority.Common, false);
            }

            public void SendAudio(string destID, byte[] audioData)
            {
                AudioContract contract = new AudioContract(this.contractHelper);
                contract.DataLen = audioData.Length;
                contract.Data = audioData;
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.AudioMessage, contract.GetStreamLength(), destID);
                this.tcpServerAgent.CommitRequest(new Message(header, contract), DataPriority.CanBeDiscarded, false);
            }

            public void SendVideo(string destID, Bitmap bitmap)
            {
                VideoContract contract = new VideoContract(this.contractHelper);          
                byte[] jpgImg = ESFramework.Common.AdvancedFunction.CompressBitmapToJpg(bitmap);
                contract.DataLen = jpgImg.Length;
                contract.Data = jpgImg;
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.VideoMessage, contract.GetStreamLength(), destID);
                this.tcpServerAgent.CommitRequest(new Message(header, contract), DataPriority.CanBeDiscarded, false);
            }

            public void SendAVCancel(string destID)
            {
                MessageHeader header = this.CreateMessageHeader(P2PRequestType.AVCancel, 0, destID);
                this.tcpServerAgent.CommitRequest(new Message(header, null), DataPriority.Common, false);
            }
            #endregion
        }

    //-----------------------服务端------------------

     public class AsynTcp : ITcp, INet, ITcpEventList, ITcpClientsController
        {
            public event CbSimpleInt ConnectionCountChanged;
            public event CbSimpleStr DynamicMsgArrived;
            public event CallBackForTcpMonitor ServiceCommitted;
            public event CallBackForTcpMonitor ServiceDirectCommitted;
            public event CbSimpleInt SomeOneConnected;
            public event CallBackForTcpUser1 SomeOneDisConnected;
            public event CallBackForTcpUser UserAction;

            public AsynTcp()
            {
                this.xtcpListener = null;
                this.messageDispatcher = null;
                this.contextKeyMgr = new ContextKeyManager();
                this.stateIsStop = true;
                this.validateRequest = false;
                this.curPort = 0x22b8;
            }

            private void ActivateUserActionEvent(int ConnectID, TcpUserAction action)
            {
                if (this.UserAction != null)
                {
                    this.UserAction(ConnectID, action);
                }
            }

            private bool CheckData(ContextKey key)
            {
                int num1 = key.NetStream.GetHashCode();
                if (this.stateIsStop)
                {
                    this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                    return false;
                }
                if (key.BytesRead == 0)
                {
                    this.DisposeOneConnection(num1, DisconnectedCause.LineOff);
                    return false;
                }
                if (key.BytesRead == 8)
                {
                    string text1 = Encoding.BigEndianUnicode.GetString(key.Buffer, 0, 8);
                    this.DisposeOneConnection(num1, DisconnectedCause.LineOff);
                    return false;
                }
                return true;
            }

            private void DisposeOneConnection(int connectID)
            {
                this.contextKeyMgr.RemoveContextKey(connectID);
            }

            public void DisposeOneConnection(int connectID, DisconnectedCause cause)
            {
                this.DisposeOneConnection(connectID);
                if (this.SomeOneDisConnected != null)
                {
                    this.SomeOneDisConnected(connectID, cause);
                }
                this.ActivateUserActionEvent(connectID, TcpUserAction.Exit);
            }

            public NetAddinType GetProtocalType()
            {
                return NetAddinType.Tcp;
            }

            public void InitializeAll()
            {
                this.xtcpListener = new XTcpListener(this.curPort);
                this.xtcpListener.TcpConnectionEstablished += new CBackUserLogon(this.xtcpListener_TcpConnectionEstablished);
                this.xtcpListener.DynamicMsgArrived += new CbSimpleStr(this.PutoutDynamicMsg);
                this.contextKeyMgr.StreamCountChanged += new CbSimpleInt(this.OnStreamCountChanged);
            }

            private void OnStreamCountChanged(int count)
            {
                if (this.ConnectionCountChanged != null)
                {
                    this.ConnectionCountChanged(count);
                }
            }

            private void PutoutDynamicMsg(string msg)
            {
                if (this.DynamicMsgArrived != null)
                {
                    this.DynamicMsgArrived(msg);
                }
            }

            private void RecieveDataFrom(ContextKey key)
            {
                key.StreamState = NetStreamState.Reading;
                key.NetStream.BeginRead(key.Buffer, key.StartOffsetForRecieve, key.MaxRecieveCapacity, new AsyncCallback(this.ServeOverLap), key);
            }

            public void SendData(int ConnectID, byte[] data)
            {
                this.SendData(ConnectID, data, 0, data.Length);
            }

            public void SendData(int ConnectID, byte[] data, int offset, int size)
            {
                if ((((data != null) && (data.Length != 0)) && ((offset >= 0) && (size >= 0))) && ((offset + size) <= data.Length))
                {
                    ISafeNetworkStream stream1 = this.contextKeyMgr.GetNetStream(ConnectID);
                    if (stream1 != null)
                    {
                        stream1.Write(data, offset, size);
                        if (this.ServiceDirectCommitted != null)
                        {
                            RespondInformation information1 = new RespondInformation();
                            information1.ConnectID = ConnectID;
                            information1.ServiceKey = -1;
                            if ((offset == 0) && (size == data.Length))
                            {
                                information1.repondData = data;
                            }
                            else
                            {
                                information1.repondData = new byte[size];
                                for (int num1 = 0; num1 < size; num1++)
                                {
                                    information1.repondData[num1] = data[offset + num1];
                                }
                            }
                            this.ServiceDirectCommitted(information1);
                        }
                    }
                }
            }

            private void ServeOverLap(IAsyncResult ar)
            {
                ContextKey key1 = (ContextKey) ar.AsyncState;
                int num1 = key1.NetStream.GetHashCode();
                try
                {
                    key1.BytesRead = key1.NetStream.EndRead(ar);
                    if (!this.CheckData(key1))
                    {
                        return;
                    }
                    byte[] buffer1 = null;
                    ArrayList list1 = this.messageDispatcher.DealRequestMessage(key1.RequestData, out buffer1, ref key1.Validation);
                    if (this.validateRequest && key1.Validation.gotoCloseConnection)
                    {
                        this.DisposeOneConnection(num1, key1.Validation.cause);
                    }
                    key1.StreamState = NetStreamState.Writing;
                    if ((list1 != null) && (list1.Count != 0))
                    {
                        foreach (object obj1 in list1)
                        {
                            byte[] buffer2 = (byte[]) obj1;
                            key1.NetStream.Write(buffer2, 0, buffer2.Length);
                            if (this.ServiceCommitted != null)
                            {
                                RespondInformation information1 = new RespondInformation();
                                information1.ConnectID = num1;
                                information1.ServiceKey = this.messageDispatcher.GetServiceKey(buffer2);
                                information1.repondData = buffer2;
                                this.ServiceCommitted(information1);
                            }
                            this.ActivateUserActionEvent(num1, TcpUserAction.FunctionAccess);
                        }
                    }
                    if (key1.IsFirstMsg)
                    {
                        if ((list1 == null) || (list1.Count == 0))
                        {
                            key1.IsFirstMsg = true;
                        }
                        else
                        {
                            key1.IsFirstMsg = false;
                        }
                    }
                    key1.StreamState = NetStreamState.Idle;
                    key1.ResetBuffer(buffer1);
                    if (!this.stateIsStop)
                    {
                        this.RecieveDataFrom(key1);
                    }
                    else
                    {
                        this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                    }
                }
                catch (Exception exception1)
                {
                    if (exception1 is IOException)
                    {
                        this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                    }
                    exception1 = exception1;
                }
            }

            public void Start()
            {
                if (this.stateIsStop)
                {
                    this.xtcpListener.Start();
                    this.stateIsStop = false;
                }
            }

            public void Stop()
            {
                if (!this.stateIsStop)
                {
                    this.stateIsStop = true;
                    this.xtcpListener.Stop();
                    int num1 = 0;
                    while (!this.contextKeyMgr.IsAllStreamSafeToStop())
                    {
                        Thread.Sleep(200);
                        if (10 == num1++)
                        {
                            break;
                        }
                    }
                    this.contextKeyMgr.DisposeAllContextKey();
                }
            }

            public bool SynRecieveFrom(int ConnectID, byte[] buffer, int offset, int size, out int readCount)
            {
                readCount = 0;
                ISafeNetworkStream stream1 = this.contextKeyMgr.GetNetStream(ConnectID);
                if (stream1 == null)
                {
                    return false;
                }
                readCount = stream1.Read(buffer, offset, size);
                return true;
            }

            public void UnitializeAll()
            {
                this.Stop();
                this.xtcpListener.ExitListenThread();
                this.ConnectionCountChanged = null;
                this.DynamicMsgArrived = null;
                this.ServiceCommitted = null;
                this.ServiceDirectCommitted = null;
                this.SomeOneConnected = null;
                this.SomeOneDisConnected = null;
                this.UserAction = null;
            }

            private void xtcpListener_TcpConnectionEstablished(NetworkStream stream)
            {
                ISafeNetworkStream stream1 = new SafeNetworkStream(stream);
                ContextKey key1 = new ContextKey(stream1,1024);
                key1.ResetBuffer(null);
                this.contextKeyMgr.InsertContextKey(key1);
                int num1 = key1.NetStream.GetHashCode();
                if (this.SomeOneConnected != null)
                {
                    this.SomeOneConnected(num1);
                }
                this.ActivateUserActionEvent(num1, TcpUserAction.Logon);
                key1.IsFirstMsg = true;
                this.RecieveDataFrom(key1);
            }


            public int ConnectionCount
            {
                get
                {
                    return this.contextKeyMgr.ConnectionCount;
                }
            }

            public IReqestStreamDispatcher Dispatcher
            {
                set
                {
                    this.messageDispatcher = (ITcpReqStreamDispatcher) value;
                }
            }

            public int Port
            {
                get
                {
                    return this.curPort;
                }
                set
                {
                    this.curPort = value;
                }
            }

            public bool UserValidated
            {
                set
                {
                    this.validateRequest = value;
                }
            }


            private const int BufferSize = 1024;
            private ContextKeyManager contextKeyMgr;
            private int curPort;
            private ITcpReqStreamDispatcher messageDispatcher;
            private bool stateIsStop;
            private bool validateRequest;
            private IXTcpListener xtcpListener;
        }

    //---------------------------------------------------

    public class ContextKey
        {
            public ContextKey(ISafeNetworkStream net_Stream, int buffSize)
            {
                this.LeftDataMaxPercent = 0.66;
                this.bytesRead = 0;
                this.netStream = null;
                this.preLeftDataLen = 0;
                this.streamState = NetStreamState.Idle;
                this.netStream = net_Stream;
                this.requestData = new EnterpriseServerBase.Network.Tcp.RequestData();
                this.requestData.Buff = new byte[buffSize];
                this.requestData.ConnectID = this.netStream.GetHashCode();
                this.Validation = new RequestValidation();
            }

            public void ResetBuffer(byte[] leftData)
            {
                this.BytesRead = 0;
                for (int num1 = 0; num1 < this.Buffer.Length; num1++)
                {
                    this.Buffer[num1] = 0;
                }
                this.preLeftDataLen = 0;
                if ((leftData != null) && (leftData.Length <= (this.LeftDataMaxPercent * this.Buffer.Length)))
                {
                    this.preLeftDataLen = leftData.Length;
                    for (int num2 = 0; num2 < leftData.Length; num2++)
                    {
                        this.Buffer[num2] = leftData[num2];
                    }
                }
            }


            public byte[] Buffer
            {
                get
                {
                    return this.requestData.Buff;
                }
                set
                {
                    this.requestData.Buff = value;
                }
            }

            public int BytesRead
            {
                get
                {
                    return this.bytesRead;
                }
                set
                {
                    this.bytesRead = value;
                    this.requestData.ValidCount = this.bytesRead + this.preLeftDataLen;
                }
            }

            public bool IsFirstMsg
            {
                get
                {
                    return this.requestData.IsFirstMsg;
                }
                set
                {
                    this.requestData.IsFirstMsg = value;
                }
            }

            public int MaxRecieveCapacity
            {
                get
                {
                    return (this.Buffer.Length - this.preLeftDataLen);
                }
            }

            public ISafeNetworkStream NetStream
            {
                get
                {
                    return this.netStream;
                }
            }

            public int PreLeftDataLen
            {
                get
                {
                    return this.preLeftDataLen;
                }
                set
                {
                    this.preLeftDataLen = value;
                }
            }

            public EnterpriseServerBase.Network.Tcp.RequestData RequestData
            {
                get
                {
                    return this.requestData;
                }
            }

            public int StartOffsetForRecieve
            {
                get
                {
                    return this.preLeftDataLen;
                }
            }

            public NetStreamState StreamState
            {
                get
                {
                    return this.streamState;
                }
                set
                {
                    this.streamState = value;
                }
            }


            private int bytesRead;
            private double LeftDataMaxPercent;
            private ISafeNetworkStream netStream;
            private int preLeftDataLen;
            private EnterpriseServerBase.Network.Tcp.RequestData requestData;
            private NetStreamState streamState;
            public RequestValidation Validation;
        }

    //-----------------------服务端------------------------

     public class XTcpListener : IXTcpListener
        {
            public event CbSimpleStr DynamicMsgArrived;
            public event CBackUserLogon TcpConnectionEstablished;

            public XTcpListener(int port)
            {
                this.tcpListener = null;
                this.currentListenThread = null;
                this.stateIsStop = true;
                this.toExitListenThread = false;
                this.listenThreadStoppedSafe = true;
                this.tcpListener = new TcpListener(port);
            }

            public void ExitListenThread()
            {
                this.Stop();
                this.toExitListenThread = true;
                while (!this.listenThreadStoppedSafe)
                {
                    Thread.Sleep(200);
                }
            }

            private void ListenThreadMethod()
            {
                try
                {
                    this.listenThreadStoppedSafe = false;
                    while (!this.toExitListenThread)
                    {
                        if (this.stateIsStop)
                        {
                            Thread.Sleep(100);
                            continue;
                        }
                        if (!this.tcpListener.Pending())
                        {
                            Thread.Sleep(100);
                            continue;
                        }
                        TcpClient client1 = this.tcpListener.AcceptTcpClient();
                        if (this.TcpConnectionEstablished != null)
                        {
                            this.TcpConnectionEstablished(client1.GetStream());
                        }
                    }
                    this.listenThreadStoppedSafe = true;
                }
                catch (Exception exception1)
                {
                    string text1 =  exception1.Message;
                    this.PutoutDynamicMsg(text1);
                }
            }

            private void PutoutDynamicMsg(string msg)
            {
                if (this.DynamicMsgArrived != null)
                {
                    this.DynamicMsgArrived(msg);
                }
            }

            public void Start()
            {
                if (this.stateIsStop)
                {
                    this.tcpListener.Start();
                    this.stateIsStop = false;
                    if ((this.currentListenThread == null) || !this.currentListenThread.IsAlive)
                    {
                        this.currentListenThread = new Thread(new ThreadStart(this.ListenThreadMethod));
                        this.currentListenThread.Start();
                        this.toExitListenThread = false;
                        string text1 = "currentListenThread线程启动";
                        this.PutoutDynamicMsg(text1);
                    }
                }
            }

            public void Stop()
            {
                if (!this.stateIsStop)
                {
                    this.tcpListener.Stop();
                    this.stateIsStop = true;
                }
            }


            private Thread currentListenThread;
            private bool listenThreadStoppedSafe;
            private bool stateIsStop;
            private TcpListener tcpListener;
            private bool toExitListenThread;
        }

    //---------------------------客户端-------------------------

     public class AgileTcp : ITcp, ITcpClientsController, IDisposable
        {
            private IBufferPool bufferPool;
            private IContextKeyManager contextKeyManager = new ContextKeyManager();
            private IContractHelper contractHelper;
            private IEsbLogger esbLogger = new EmptyEsbLogger();
            private IEsfTcpListener esfTcpListener = null;
            private int maxMessageSize = 0xf4240;
            private int port = 0x1f40;
            private int recieveBuffSize = 0x400;
            private volatile bool stop = true;
            private ITcpStreamDispatcher tcpStreamDispatcher = null;

            public event CbSimpleInt ConnectionCountChanged;

            public event CallBackRespond ServiceCommitted;

            public event CallBackRespond ServiceDirectCommitted;

            public event CbSimpleInt SomeOneConnected;

            public event CallBackDisconnect SomeOneDisConnected;

            private void contextKeyManager_StreamCountChanged(int val)
            {
                if (this.ConnectionCountChanged != null)
                {
                    this.ConnectionCountChanged(val);
                }
            }

            private void DataManaging(ContextKey key)
            {
                int hashCode = key.NetStream.GetHashCode();
                int messageHeaderLength = this.contractHelper.MessageHeaderLength;
                while (key.NetStream.DataAvailable && !this.stop)
                {
                    byte[] buff = null;
                    try
                    {
                        NetHelper.RecieveData(key.NetStream, key.Buffer, 0, messageHeaderLength);
                        IMessageHeader header = this.contractHelper.ParseMessageHeader(key.Buffer, 0);
                        if (!this.contractHelper.ValidateMessageToken(header))
                        {
                            this.DisposeOneConnection(hashCode, DisconnectedCause.MessageTokenInvalid);
                            return;
                        }
                        RoundedMessage reqMsg = new RoundedMessage();
                        reqMsg.ConnectID = hashCode;
                        reqMsg.Header = header;
                        if (!key.FirstMessageExist)
                        {
                            reqMsg.IsFirstMessage = true;
                            key.FirstMessageExist = true;
                        }
                        if ((messageHeaderLength + header.MessageBodyLength) > this.maxMessageSize)
                        {
                            this.DisposeOneConnection(hashCode, DisconnectedCause.MessageSizeOverflow);
                            return;
                        }
                        if (header.MessageBodyLength > 0)
                        {
                            if ((header.MessageBodyLength + messageHeaderLength) <= this.recieveBuffSize)
                            {
                                NetHelper.RecieveData(key.NetStream, key.Buffer, 0, header.MessageBodyLength);
                                reqMsg.Body = key.Buffer;
                            }
                            else
                            {
                                buff = this.bufferPool.RentBuffer(header.MessageBodyLength);
                                NetHelper.RecieveData(key.NetStream, buff, 0, header.MessageBodyLength);
                                reqMsg.Body = buff;
                            }
                        }
                        bool closeConnection = false;
                        NetMessage msg = this.tcpStreamDispatcher.DealRequestData(reqMsg, ref closeConnection);
                        if (buff != null)
                        {
                            this.bufferPool.GivebackBuffer(buff);
                        }
                        if (closeConnection)
                        {
                            this.DisposeOneConnection(hashCode, DisconnectedCause.OtherCause);
                            return;
                        }
                        if ((msg != null) && !this.stop)
                        {
                            byte[] buffer = msg.ToStream();
                            key.NetStream.Write(buffer, 0, buffer.Length);
                            if (this.ServiceCommitted != null)
                            {
                                this.ServiceCommitted(hashCode, msg);
                            }
                        }
                        continue;
                    }
                    catch (Exception exception)
                    {
                        if (exception is IOException)
                        {
                            this.DisposeOneConnection(hashCode, DisconnectedCause.NetworkError);
                            break;
                        }
                        this.esbLogger.Log(exception.Message, "ESFramework.Network.Tcp.AgileTcp", ErrorLevel.Standard);
                        exception = exception;
                        continue;
                    }
                }
                key.IsDataManaging = false;
            }

            public void Dispose()
            {
                this.Stop();
                this.esfTcpListener.Dispose();
            }

            public void DisposeOneConnection(int connectID, DisconnectedCause cause)
            {
                this.contextKeyManager.RemoveContextKey(connectID);
                if (this.SomeOneDisConnected != null)
                {
                    this.SomeOneDisConnected(connectID, cause);
                }
            }

            private void esfTcpListener_TcpConnectionEstablished(NetworkStream stream)
            {
                ISafeNetworkStream stream2 = new SafeNetworkStream(stream);
                ContextKey key = new ContextKey(stream2, this.recieveBuffSize);
                this.contextKeyManager.InsertContextKey(key);
                int hashCode = key.NetStream.GetHashCode();
                if (this.SomeOneConnected != null)
                {
                    this.SomeOneConnected(hashCode);
                }
            }

            public void Initialize()
            {
                this.esfTcpListener = new EsfTcpListener(this.port);
                this.esfTcpListener.TcpConnectionEstablished += new CBackConnection(this.esfTcpListener_TcpConnectionEstablished);
                this.contextKeyManager.StreamCountChanged += new CbSimpleInt(this.contextKeyManager_StreamCountChanged);
            }

            public void SendData(int ConnectID, NetMessage msg)
            {
                if (msg != null)
                {
                    ISafeNetworkStream netStream = this.contextKeyManager.GetNetStream(ConnectID);
                    if (netStream != null)
                    {
                        try
                        {
                            byte[] buffer = msg.ToStream();
                            netStream.Write(buffer, 0, buffer.Length);
                            if (this.ServiceDirectCommitted != null)
                            {
                                this.ServiceDirectCommitted(ConnectID, msg);
                            }
                        }
                        catch (Exception exception)
                        {
                            if (exception is IOException)
                            {
                                this.DisposeOneConnection(ConnectID, DisconnectedCause.NetworkError);
                            }
                            throw exception;
                        }
                    }
                }
            }

            public void Start()
            {
                if (this.stop)
                {
                    this.stop = false;
                    this.esfTcpListener.Start();
                    new CbSimple(this.TaskChecker).BeginInvoke(null, null);
                }
            }

            public void Stop()
            {
                if (!this.stop)
                {
                    this.stop = true;
                    this.esfTcpListener.Stop();
                    this.contextKeyManager.DisposeAllContextKey();
                }
            }

            private void TaskChecker()
            {
                while (!this.stop)
                {
                    foreach (ContextKey key in this.contextKeyManager.ContextKeyList)
                    {
                        if (this.stop)
                        {
                            break;
                        }
                        if (!key.IsDataManaging && key.NetStream.DataAvailable)
                        {
                            key.IsDataManaging = true;
                            new CbContextKey(this.DataManaging).BeginInvoke(key, null, null);
                        }
                    }
                    Thread.Sleep(50);
                }
            }

            public IBufferPool BufferPool
            {
                set
                {
                    this.bufferPool = value;
                }
            }

            public int ConnectionCount
            {
                get
                {
                    return this.contextKeyManager.ConnectionCount;
                }
            }

            public IContractHelper ContractHelper
            {
                set
                {
                    this.contractHelper = value;
                }
            }

            public ITcpStreamDispatcher Dispatcher
            {
                set
                {
                    this.tcpStreamDispatcher = value;
                }
            }

            public IEsbLogger EsbLogger
            {
                set
                {
                    if (value != null)
                    {
                        this.esbLogger = value;
                    }
                }
            }

            public int MaxMessageSize
            {
                set
                {
                    this.maxMessageSize = value;
                }
            }

            public int Port
            {
                get
                {
                    return this.port;
                }
                set
                {
                    this.port = value;
                }
            }

            public int RecieveBuffSize
            {
                get
                {
                    return this.recieveBuffSize;
                }
                set
                {
                    this.recieveBuffSize = value;
                }
            }
        }

     //----------------------------------------------------

     public class IocpTcp : ITcp, INet, ITcpEventList, ITcpClientsController, IOCPPackageHandler
        {
            public event CbSimpleInt ConnectionCountChanged;
            public event CbSimpleStr DynamicMsgArrived;
            public event CallBackForTcpMonitor ServiceCommitted;
            public event CallBackForTcpMonitor ServiceDirectCommitted;
            public event CbSimpleInt SomeOneConnected;
            public event CallBackForTcpUser1 SomeOneDisConnected;
            public event CallBackForTcpUser UserAction;

            public IocpTcp()
            {
                this.iocpMgr = null;
                this.messageDispatcher = null;
                this.contextKeyMgr = new ContextKeyManager();
                this.stateIsStop = true;
                this.validateRequest = false;
                this.curPort = 0x22b8;
            }

            private void ActivateUserActionEvent(int ConnectID, TcpUserAction action)
            {
                if (this.UserAction != null)
                {
                    this.UserAction(ConnectID, action);
                }
            }

            private void BindRequestToQueue(IAsyncResult ar)
            {
                try
                {
                    ContextKey key1 = (ContextKey) ar.AsyncState;
                    key1.BytesRead = key1.NetStream.EndRead(ar);
                    if (!this.CheckData(key1))
                    {
                        return;
                    }
                    this.iocpMgr.Push(key1);
                }
                catch (Exception exception1)
                {
                    exception1 = exception1;
                }
            }

            private bool CheckData(ContextKey key)
            {
                int num1 = key.NetStream.GetHashCode();
                if (this.stateIsStop)
                {
                    this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                    return false;
                }
                if (key.BytesRead == 0)
                {
                    this.DisposeOneConnection(num1, DisconnectedCause.LineOff);
                    return false;
                }
                if (key.BytesRead == 8)
                {
                    string text1 = Encoding.BigEndianUnicode.GetString(key.Buffer, 0, 8);
                    this.DisposeOneConnection(num1, DisconnectedCause.LineOff);
                    return false;
                }
                return true;
            }

            private void DisposeOneConnection(int connectID)
            {
                this.contextKeyMgr.RemoveContextKey(connectID);
            }

            public void DisposeOneConnection(int connectID, DisconnectedCause cause)
            {
                this.DisposeOneConnection(connectID);
                if (this.SomeOneDisConnected != null)
                {
                    this.SomeOneDisConnected(connectID, cause);
                }
                this.ActivateUserActionEvent(connectID, TcpUserAction.Exit);
            }

            public NetAddinType GetProtocalType()
            {
                return NetAddinType.Tcp;
            }

            public void HandlerPackage(object package)
            {
                ContextKey key1 = package as ContextKey;
                if (key1 != null)
                {
                    int num1 = key1.NetStream.GetHashCode();
                    try
                    {
                        byte[] buffer1 = null;
                        ArrayList list1 = this.messageDispatcher.DealRequestMessage(key1.RequestData, out buffer1, ref key1.Validation);
                        if (this.validateRequest && key1.Validation.gotoCloseConnection)
                        {
                            this.DisposeOneConnection(num1, key1.Validation.cause);
                        }
                        else
                        {
                            key1.StreamState = NetStreamState.Writing;
                            if ((list1 != null) && (list1.Count != 0))
                            {
                                foreach (object obj1 in list1)
                                {
                                    byte[] buffer2 = (byte[]) obj1;
                                    key1.NetStream.Write(buffer2, 0, buffer2.Length);
                                    if (this.ServiceCommitted != null)
                                    {
                                        RespondInformation information1 = new RespondInformation();
                                        information1.ConnectID = num1;
                                        information1.ServiceKey = this.messageDispatcher.GetServiceKey(buffer2);
                                        information1.repondData = buffer2;
                                        this.ServiceCommitted(information1);
                                    }
                                    this.ActivateUserActionEvent(num1, TcpUserAction.FunctionAccess);
                                }
                            }
                            if (key1.IsFirstMsg)
                            {
                                if ((list1 == null) || (list1.Count == 0))
                                {
                                    key1.IsFirstMsg = true;
                                }
                                else
                                {
                                    key1.IsFirstMsg = false;
                                }
                            }
                            key1.StreamState = NetStreamState.Idle;
                            key1.ResetBuffer(buffer1);
                            if (!this.stateIsStop)
                            {
                                this.RecieveDataFrom(key1);
                            }
                            else
                            {
                                this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                            }
                        }
                    }
                    catch (Exception exception1)
                    {
                        if (exception1 is IOException)
                        {
                            this.DisposeOneConnection(num1, DisconnectedCause.ServerStopped);
                        }
                        exception1 = exception1;
                    }
                }
            }

            public void InitializeAll()
            {
                this.xtcpListener = new XTcpListener(this.curPort);
                this.xtcpListener.TcpConnectionEstablished += new CBackUserLogon(this.xtcpListener_TcpConnectionEstablished);
                this.xtcpListener.DynamicMsgArrived += new CbSimpleStr(this.PutoutDynamicMsg);
                this.contextKeyMgr.StreamCountChanged += new CbSimpleInt(this.OnStreamCountChanged);
                this.iocpMgr = new IOCPManager();
                this.iocpMgr.Initialize(this, 50);
            }

            private void OnStreamCountChanged(int count)
            {
                if (this.ConnectionCountChanged != null)
                {
                    this.ConnectionCountChanged(count);
                }
            }

            private void PutoutDynamicMsg(string msg)
            {
                if (this.DynamicMsgArrived != null)
                {
                    this.DynamicMsgArrived(msg);
                }
            }

            private void RecieveDataFrom(ContextKey key)
            {
                try
                {
                    key.StreamState = NetStreamState.Reading;
                    key.NetStream.BeginRead(key.Buffer, key.StartOffsetForRecieve, key.MaxRecieveCapacity, new AsyncCallback(this.BindRequestToQueue), key);
                }
                catch (Exception exception1)
                {
                    exception1 = exception1;
                }
            }

            public void SendData(int ConnectID, byte[] data)
            {
                this.SendData(ConnectID, data, 0, data.Length);
            }

            public void SendData(int ConnectID, byte[] data, int offset, int size)
            {
                if ((((data != null) && (data.Length != 0)) && ((offset >= 0) && (size >= 0))) && ((offset + size) <= data.Length))
                {
                    ISafeNetworkStream stream1 = this.contextKeyMgr.GetNetStream(ConnectID);
                    if (stream1 != null)
                    {
                        stream1.Write(data, offset, size);
                        if (this.ServiceDirectCommitted != null)
                        {
                            RespondInformation information1 = new RespondInformation();
                            information1.ConnectID = ConnectID;
                            information1.ServiceKey = -1;
                            if ((offset == 0) && (size == data.Length))
                            {
                                information1.repondData = data;
                            }
                            else
                            {
                                information1.repondData = new byte[size];
                                for (int num1 = 0; num1 < size; num1++)
                                {
                                    information1.repondData[num1] = data[offset + num1];
                                }
                            }
                            this.ServiceDirectCommitted(information1);
                        }
                    }
                }
            }

            public void Start()
            {
                try
                {
                    if (this.stateIsStop)
                    {
                        this.stateIsStop = false;
                        this.xtcpListener.Start();
                        this.iocpMgr.Start();
                    }
                }
                catch (Exception exception1)
                {
                    throw exception1;
                }
            }

            public void Stop()
            {
                if (!this.stateIsStop)
                {
                    this.stateIsStop = true;
                    this.xtcpListener.Stop();
                    this.iocpMgr.Stop();
                    int num1 = 0;
                    while (!this.contextKeyMgr.IsAllStreamSafeToStop())
                    {
                        Thread.Sleep(200);
                        if (10 == num1++)
                        {
                            break;
                        }
                    }
                    this.contextKeyMgr.DisposeAllContextKey();
                }
            }

            public bool SynRecieveFrom(int ConnectID, byte[] buffer, int offset, int size, out int readCount)
            {
                readCount = 0;
                ISafeNetworkStream stream1 = this.contextKeyMgr.GetNetStream(ConnectID);
                if (stream1 == null)
                {
                    return false;
                }
                readCount = stream1.Read(buffer, offset, size);
                return true;
            }

            public void UnitializeAll()
            {
                this.Stop();
                this.xtcpListener.ExitListenThread();
                this.ConnectionCountChanged = null;
                this.DynamicMsgArrived = null;
                this.ServiceCommitted = null;
                this.SomeOneConnected = null;
                this.SomeOneDisConnected = null;
                this.UserAction = null;
                this.ServiceDirectCommitted = null;
            }

            private void xtcpListener_TcpConnectionEstablished(NetworkStream stream)
            {
                ISafeNetworkStream stream1 = new SafeNetworkStream(stream);
                ContextKey key1 = new ContextKey(stream1, 0x400);
                key1.ResetBuffer(null);
                this.contextKeyMgr.InsertContextKey(key1);
                int num1 = key1.NetStream.GetHashCode();
                if (this.SomeOneConnected != null)
                {
                    this.SomeOneConnected(num1);
                }
                this.ActivateUserActionEvent(num1, TcpUserAction.Logon);
                key1.IsFirstMsg = true;
                this.RecieveDataFrom(key1);
            }


            public int ConnectionCount
            {
                get
                {
                    return this.contextKeyMgr.ConnectionCount;
                }
            }

            public IReqestStreamDispatcher Dispatcher
            {
                set
                {
                    this.messageDispatcher = (ITcpReqStreamDispatcher) value;
                }
            }

            public int Port
            {
                get
                {
                    return this.curPort;
                }
                set
                {
                    this.curPort = value;
                }
            }

            public bool UserValidated
            {
                set
                {
                    this.validateRequest = value;
                }
            }


            private const int BufferSize = 0x400;
            private ContextKeyManager contextKeyMgr;
            private int curPort;
            private IIOCPManager iocpMgr;
            private const int MaxWorkThreadNum = 50;
            private ITcpReqStreamDispatcher messageDispatcher;
            private bool stateIsStop;
            private bool validateRequest;
            private IXTcpListener xtcpListener;
        }

  • 相关阅读:
    js数组的用法以及数组根据下标(数值或字符)移除元素
    组件创建、组件注册方式
    vue.runtime.esm.js:593 [Vue warn]: Invalid prop: custom validator check failed for prop "value".报错解决
    uni-app 子组件如何调用父组件的方法
    wap2app(十)--wap2app 添加原生底部导航,添加原生标题栏,填坑
    wap2app(九)-- 使用mui.previewImage之后,页面a链接不能跳转
    wap2app(八)-- iphoneX 底部导航的兼容问题
    wap2app(七)-- 长按保存图片
    我的新书《Android App开发从入门到精通》终于出版啦
    Android微信登录、分享、支付
  • 原文地址:https://www.cnblogs.com/fx2008/p/2252510.html
Copyright © 2011-2022 走看看