HJ212协议C#代码解析实现

news2024/11/24 19:15:42

HJ212协议C#代码解析实现

HJ212协议是环保中一个非常重要的标准协议(字符串协议),之前写了两篇C++ HJ212协议解析的相关博文:

  • 环保 HJ212协议解析
  • 基于Qt5.14.2的HJ212 TCP服务端接收解析入库程序
    最近在学习C#,所以打算基于C#重新实现一遍,算是熟悉一下C#的基本语法。

HJ212协议简介

由于是做环保相关的,有时需要对212协议进行拆包和解包。HJ212协议是一种字符串协议,数据传输通讯包主要由包头、数据段长度、数据段、CRC校验、包尾组成,其中“数据段”内容包括请求编码、系统编码、命令编码、密码、设备唯一标识、总包数、包号、指令参数。请求编码为请求的时间戳,系统编码ST统一规定为22,命令编码CN为该数据包的时间类型,访问密码、设备唯一标识在对接时由平台提供,指令参数为数据内容。通讯协议的数据结构如图4所示。
通讯协议的数据结构
图4 通讯协议的数据结构

6.1.1通讯包结构组成

名称类型长度描述
包头字符2固定为##
数据段长度十进制整数4数据段的ASCII字符数。例如数据段的字符数为128,则写为“0128”
数据段字符0<=n<=9999变长的数据
CRC校验十六进制4数据段的校验结果,例如C901,如果CRC错,即执行超时
包尾字符2回车换行(\r\n)

《污染物在线监控(监测)系统数据传输标准》简称《HJ212-2017》标准PDF文档可以从中华人民共和国生态环境部的官网下载,具体地址为:HJ212-2017》标准PDF文档
如下图所示:
HJ212-2017标准下载
目前HJ212标准协议已经发布了两个版本,一个是HJ/T 212-2005,另一个是 HJ 212-2017,最新的HJ 212-2017下载地址为:污染物在线监控(监测)系统数据传输标准(HJ 212-2017代替HJ/T 212-2005)

## 基于C#的HJ212解析类

首先创建一个基于C# .Net的库项目,名称为:HJ212ParseLibrary,相关类实现代码如下:

(1)、通用工具类 CommonUtils

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

namespace HJ212ParseLibrary
{
    /// <summary>
    /// 通用工具类
    /// </summary>
    public class CommonUtils
    {
        /// <summary>
        /// 切分数据
        /// </summary>
        /// <param name="cp"></param>
        /// <returns></returns>
        public static List<Dictionary<string, string>>  SplitKV(string cp)
        {
            List<Dictionary<string, string>> resultList = new List<Dictionary<string, string>>();
            var arr1 = cp.Split(';');
            foreach (var i in arr1)
            {
                Dictionary<string, string> item = new Dictionary<string, string>();
                var arr2 = i.Split(',');
                foreach(var j in arr2)
                {
                    var arrKv = j.Split('=');
                    if (arrKv.Length == 2)
                    {
                        item.Add(arrKv[0], arrKv[1]);   
                    }
                }
                resultList.Add(item);
            }
            return resultList;
        }
        /// <summary>
        /// 组合数据
        /// </summary>
        /// <param name=""></param>
        /// <returns></returns>
        public static string JoinKV(List<Dictionary<string, string>> myList)
        {
            List<string> item = new List<string>();
            foreach (var i in myList)
            {
                List<string> arrKv = new List<string>();
                foreach (var j in i)
                {
                    arrKv.Add(j.Key + "=" + j.Value);
                }
                item.Add(Join(arrKv, ','));
            }
            return Join(item, ';');
        }
        /// <summary>
        /// 数据组合字符串
        /// </summary>
        /// <param name="arrKv">字符串列表</param>
        /// <param name="sep">分隔符</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private static string Join(List<string> arrKv, char sep = ' ')
        {
            string result = "";
            bool isFirst = true;
            foreach (var item in arrKv)
            {
                if (isFirst)
                {
                    result = item;
                    isFirst = false;
                    continue;
                }
                result += sep + item;
            }
            return result;
        }
        /// <summary>
        /// 将字符串数组按照sep分隔符分割之后,生成新的字符串
        /// </summary>
        /// <param name="arrKv">字符串数组</param>
        /// <param name="sep">分隔符</param>
        /// <returns></returns>
        private static string Join(string[] arrKv, char sep = ' ')
        {
            string result = "";
            bool isFirst = true;
            foreach (var item in arrKv)
            {
                if (isFirst)
                {
                    result = item;
                    isFirst = false;
                    continue;
                }
                result += sep + item;
            }
            return result;
        }
        /// <summary>
        /// 计算计算字节数组的CRC-16校验码
        /// </summary>
        /// <param name="byteArr">字节数组</param>
        /// <returns>CRC-16校验码</returns>
        public static uint GetCrc16(byte[] byteArr)
        {
            uint crc_reg, check;
            int i, j;
            crc_reg = 0xFFFF;
            for (i = 0; i < byteArr.Length; i++)
            {
                crc_reg = (crc_reg >> 8) ^ byteArr[i];
                for (j = 0; j < 8; j++)
                {
                    check = crc_reg & 0x0001;
                    crc_reg >>= 1;
                    if (check == 0x0001)
                    {
                        crc_reg ^= 0xA001;
                    }
                }
            }
            return crc_reg;
        }
        /// <summary>
        /// 将对应位置置1,n从1开始
        /// </summary>
        /// <param name="x"></param>
        /// <param name="n"></param>
        public static void SetBit(int x, int n)
        {
            x |= 1 << (n - 1);
        }
        /// <summary>
        /// 将对应位置置0,n从1开始
        /// </summary>
        /// <param name="x"></param>
        /// <param name="n"></param>
        public static void CtrlBit(int x, int n)
        {
            x &= ~(1 << (n - 1));
        }
        public static int GetBit(int x, int n)
        {
            return x & (1 << (n - 1));
        }
    }
}

(2)、HJ212协议 CP数据部分 HJ212DataCP

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

namespace HJ212ParseLibrary
{
    /// <summary>
    /// HJ212协议 CP数据部分
    /// </summary>
    public class HJ212DataCP
    {
        public HJ212DataCP()
        {

        }
        public HJ212DataCP(string s)
        {
            List<Dictionary<string, string>> arr = CommonUtils.SplitKV(s);
            foreach (var i in arr)
            {
                int kvlen = i.Count;
                string key = "";
                foreach (var j in i)
                {
                    // 对指定监测因子的项,统一使用因子代表
                    if (j.Key == "PolId")
                    {
                        key = j.Value;
                        this.KVDict[key] = new Dictionary<string, string>();
                        continue;
                    }
                    string name, field;
                    // 查询是否包含标准协议中的设备状态
                    var f1 = j.Key.IndexOf("SB");
                    var f2 = j.Key.IndexOf("-");
                    if (f2 != -1 && f1 != 0)
                    {
                        // a20004-Rtd   name-field
                        name = j.Key.Substring(0, f2);
                        field = j.Key.Substring(f2 + 1);
                        // i11001-Info   field
                        if (field == "Info")
                        {
                            field = name;
                        }
                        else
                        {
                            key = name;
                        }
                    }
                    else
                    {
                        if (j.Key == "DataTime")
                        {
                            this.DataTime = j.Value;
                        }
                        name = j.Key;
                        key = name;
                        field = "value";
                    }
                    if (this.KVDict.ContainsKey(key))
                    {
                        // 如果存在key,则追加
                        this.KVDict[key].Add(field, j.Value);
                    } else
                    {
                        // 如果不存在该key,则创建一个字典对象,并添加到KVDict中
                        Dictionary<string, string> dict = new Dictionary<string, string>();
                        dict.Add(field, j.Value);
                        this.KVDict.Add(key, dict);
                    }
                }
            }
        }
        public void Clear()
        {
            this.KVDict.Clear();
        }
        public Dictionary<string, Dictionary<string, string>> KVDict = new Dictionary<string, Dictionary<string, string>>();   // 监测因子数据字典
        string DataTime;
    }
}

(3)、HJ212协议 数据区 HJ212Data

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

namespace HJ212ParseLibrary
{
    /// <summary>
    /// HJ212协议 数据区
    /// 请求编码QN + 系统编码ST + 命令编码CN + 密码PW + 设备唯一标识MN + 标志位Flag + 总包数PNUM + 包号PNO + 指令参数CP
    /// 其中,指令CP字符数范围为:[0,9899],CP=&&数据区&&
    /// </summary>
    public class HJ212Data
    {
        public string QN;		// 请求编码 20字符	QN=YYYYMMDDHHmmssZZZ
        public string ST;		// 系统编码 5字符		ST=21
        public string CN;		// 命令编码 7字符		CN=2011
        public string PW;		// 访问密码 9字符		PW=123456
        public string MN;		// 设备标识 27字符	MN=[0-9A-F]
        public string Flag;	    // 标志位   8整数		Flag=7	bit:000001(协议版本)0(是否有包号)0(是否需应答)
        public string PNUM;	    // 总包数   9字符		PNUM=0000	[不分包则没有本字段]
        public string PNO;		// 包号     8字符	PNO=0000	[不分包则没有本字段]
        public string CP;		// 指令参数	<=9899字符	CP=&&数据区&&
        public HJ212DataCP CPs;
        private bool bValid = false;
        // 设置Flag,bit从1开始
        public void SetFlag(int bit, bool enable)
        {
            int flag = 4;
            flag = Int32.Parse(Flag);
            if (enable)
            {
                CommonUtils.SetBit(flag, bit);
            } else
            {
                CommonUtils.CtrlBit(flag, bit);
            }
            this.Flag = flag.ToString();
        }

        // 获取Flag,bit从1开始
        public int GetFlag(int bit)
        {
            int flag = 4;
            if (Int32.TryParse(Flag, out flag))
            {
                return CommonUtils.GetBit(flag, bit);
            }
            return CommonUtils.GetBit(flag, bit);
        }

        // 有效性
        public bool IsValid()
        {
            return bValid;
        }

        // 长度
        public int Size()
        {
            return QN.Length + ST.Length + CN.Length + PW.Length + MN.Length +
                Flag.Length + PNUM.Length + PNO.Length + CP.Length;
        }
        // 构造函数
        public HJ212Data()
        {

        }
        public HJ212Data(string s)
        {
            CopyStr(s);
        }
        public void CopyStr(string s)
        {
            // 查找数据段
            int d1 = s.IndexOf("CP=&&");        // 数据段开始位置
            int d2 = s.IndexOf("&&", d1 + 5);   // 数据段结束位置
            String tmpStr = "";
            if (d1 != -1 && d2 != -1)
            {
                CP = s.Substring(d1 + 5, d2 - d1 - 5);
                CPs = new HJ212DataCP(CP);
                tmpStr = s.Substring(0, d1) + s.Substring(d2 + 2);
            }
            else
            {
                tmpStr = s;
            }
            Dictionary<string, string> strDict = new Dictionary<string, string>();
            var arr1 = tmpStr.Split(';');
            foreach (var i in arr1)
            {
                var arr2 = i.Split('=');
                if (arr2.Length == 2)
                {
                    strDict.Add(arr2[0], arr2[1]);
                }
            }
            if (strDict.ContainsKey("QN"))
            {
                QN = strDict["QN"];
            }
            if (strDict.ContainsKey("ST"))
            {
                ST = strDict["ST"];
            }
            if (strDict.ContainsKey("CN"))
            {
                CN = strDict["CN"];
            }
            if (strDict.ContainsKey("PW"))
            {
                PW = strDict["PW"];
            }
            if (strDict.ContainsKey("MN"))
            {
                MN = strDict["MN"];
            }
            if (strDict.ContainsKey("Flag"))
            {
                Flag = strDict["Flag"];
            }
            if (strDict.ContainsKey("PNUM"))
            {
                PNUM = strDict["PNUM"];
            }
            if (strDict.ContainsKey("PNO"))
            {
                PNO = strDict["PNO"];
            }
            bValid = true;
        }
        // 获取数据项
        public List<Dictionary<string, string>> Cp2Object()
        {
            return CommonUtils.SplitKV(CP);
        }
        // 字符串输出
        public override string ToString()
        {
            string str = "";
            if (!string.IsNullOrEmpty(QN))
            {
                str += ("QN=" + QN + ";");
            }
            if (!string.IsNullOrEmpty(ST))
            {
                str += ("ST=" + ST + ";");
            }
            if (!string.IsNullOrEmpty(CN))
            {
                str += ("CN=" + CN + ";");
            }
            if (!string.IsNullOrEmpty(PW))
            {
                str += ("PW=" + PW + ";");
            }
            if (!string.IsNullOrEmpty(MN))
            {
                str += ("MN=" + MN + ";");
            }
            if (!string.IsNullOrEmpty(Flag))
            {
                str += ("Flag=" + Flag + ";");
            }
            if (!string.IsNullOrEmpty(PNUM))
            {
                str += ("PNUM=" + PNUM + ";");
            }
            if (!string.IsNullOrEmpty(PNO))
            {
                str += ("PNO=" + PNO + ";");
            }
            str += ("CP=&&" + CP + "&&");
            return str;
        }
    }
}

(4)、HJ212协议实体类 HJ212

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;



namespace HJ212ParseLibrary
{
    /// <summary>
    /// HJ212协议 整包=包头+数据段长度+数据段+CRC校验+包尾组成
    /// ##0637ST=22;CN=2011;PW=123456;MN=LB4200001;CP=&&DataTime=20210107143500;a05002-Rtd=0,a05002-Flag=N;a21005-Rtd=0.825,a21005-Flag=N;a34006-Rtd=874,a34006-Flag=N;a24088-Rtd=0,a24088-Flag=N;a21003-Rtd=5.656,a21003-Flag=N;a21004-Rtd=9.253,a21004-Flag=N;a21002-Rtd=17.894,a21002-Flag=N;a05024-Rtd=5.803,a05024-Flag=N;a34007-Rtd=2679,a34007-Flag=N;a34002-Rtd=123.97,a34002-Flag=N;a34004-Rtd=24.82,a34004-Flag=N;a01006-Rtd=1032.3,a01006-Flag=N;a01002-Rtd=20.9,a01002-Flag=N;a21026-Rtd=0.795,a21026-Flag=N;a01007-Rtd=3.3,a01007-Flag=N;a34049-Rtd=3553,a34049-Flag=N;a01001-Rtd=-0.6,a01001-Flag=N;a99999-Rtd=0,a99999-Flag=N;a01008-Rtd=349,a01008-Flag=N&&3300
    /// </summary>
    public class HJ212
    {
        public string header = "##";        // 包头 2字符
        public string dataLen = "0000";     // 数据段长度    4整数,如长度128,写为"0128"
        HJ212Data data;                     // 数据段  0<=n<=9999
        public string crc = "0000";         // CRC校验码  4hex
        public string tailer = "\r\n";      // 包尾 2字符
        public string full;                 // 全数据,整包
        /// 输出
        public override string ToString()
        {
            string dataStr = data.ToString();
            uint crc16 = CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));
            return string.Format("{0}{1:D4}{2}{3:X4}{4}", header, dataStr.Length, dataStr, crc16, tailer);  // D4标示4位10进制数字,X4表示4位16进制数
        }
        // 有效性
        public bool IsValid()
        {
            return bValid;
        }
        // 有效性
        public bool IsDataValid()
        {
            return this.data.CN == "2011" || this.data.CN == "2051" || this.data.CN == "2061"
            || this.data.CN == "2031" || this.data.CN == "2041" || this.data.CN == "3020";
        }
        // 长度
        public int Size()
        {
            return header.Length + dataLen.Length + data.Size() + crc.Length + tailer.Length;
        }
        // 清空数据区
        public void ClearCp()
        {
            this.data.CP = "";
            this.data.CPs.Clear();
        }
        // 设置是否需要应答
        public void SetNeedReply(bool need)
        {
            data.SetFlag(1, need);
        }
        // 设置是否有包号
        public void SetNeedSubPack(bool need)
        {
            data.SetFlag(2, need);
            if (!need)
            {
                data.PNUM = "";
                data.PNO = "";
            }
        }
        // 是否需应答
        public int IsNeedReply()
        {
            return data.GetFlag(1);
        }
        // 是否有包号
        public int IsNeedSubPack()
        {
            return data.GetFlag(2);
        }
        // 数据区。字段与其值用‘=’连接;
        // 在数据区中,同一项目的不同分类值间用‘,’来分隔,不同项目之间 用‘;’来分隔。
        public void Combine()
        {
            this.dataLen = string.Format("{0:D4", this.data.Size());
            string dataStr = this.data.ToString();
            uint jisuanCrc = CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));
            this.crc = string.Format("{0:D4", jisuanCrc);
            this.bValid = true;
        }
    public HJ212(string str)
    {
        this.full = str;
        int totalSize = str.Length;
        dataLen = str.Substring(header.Length, dataLen.Length);
        int dLen = Int32.Parse(dataLen);
        if ((10 + dLen) > totalSize) {
            return;
        }
        string dataStr = str.Substring(header.Length + dataLen.Length, dLen);
        data = new HJ212Data(dataStr);
        crc = str.Substring(header.Length + dataLen.Length + dLen, crc.Length);
        //uint jisuanCrc16 = CommonUtils.GetCrc16(dataStr.ToCharArray());
        uint jisuanCrc16 = CommonUtils.GetCrc16(Encoding.UTF8.GetBytes(dataStr));
        int srcCrc16 = Int32.Parse(crc, System.Globalization.NumberStyles.HexNumber);
        if (srcCrc16 != jisuanCrc16) {
            return;
        }
        bValid = true;
    }

    public HJ212(HJ212 r)
        {
            this.header = r.header;
            this.dataLen = r.dataLen;
            //this.data = new HJ212Data(r.data.ToString());
            this.data = r.data;
            this.crc = r.crc;
            this.tailer = r.tailer;
            this.full = r.full;
            this.bValid = r.bValid;
        }
        public HJ212(string st, string cn, string mn, string pw, string cp, bool needReply)
        {
            this.data.QN = DateTime.Now.ToString("yyyyMMDDHHMMss000");
            this.data.ST = st;
            this.data.CN = cn;
            this.data.PW = pw;
            this.data.CP = cp;
            this.SetNeedReply(needReply);
            this.Combine();
        }
        // 获取响应报文
        public HJ212 GetDataResponse(string cn)
        {
            HJ212 outHJ212 = new HJ212(this);
            outHJ212.data.ST = "91";
            outHJ212.data.CN = cn;
            outHJ212.ClearCp();
            outHJ212.Combine();
            return outHJ212;
        }
        /// <summary>
        /// 解析字符串,获取HJ212协议数据列表
        /// </summary>
        /// <param name="buffer">输入字符串</param>
        /// <returns>返回解析后的HJ212协议列表</returns>
        public static List<HJ212> Parse(string buffer)
        {
            List<HJ212> hj212List = new List<HJ212>();
            int bufferSize = buffer.Length;
            string strTemp = "";
            for (int i = 0; i < bufferSize; i++)
            {
                if ( i < bufferSize - 1 && buffer[i] == '#' && buffer[i + 1] == '#')
                {
                    if (strTemp.Length > 0)
                    {
                        hj212List.Add(new HJ212(strTemp));
                        strTemp = "";
                    }
                }
                strTemp += buffer[i];
                // 遍历到HJ212协议末尾,末尾以\r\n结束
                if (i > 0 && buffer[i - 1] == '\r' && buffer[i] == '\n')
                {
                    if (strTemp.Length > 0)
                    {
                        hj212List.Add(new HJ212(strTemp));
                        strTemp = "";
                    }
                }
            }

            if (strTemp.Length > 0)
            {
                hj212List.Add(new HJ212(strTemp));
                strTemp = "";
            }
            return hj212List;
        }
        private bool bValid = false;
    }
}

测试程序

新建一个基于C# .Net的控制台程序ConsoleHJ212App,然后输入如下测试代码:

using HJ212ParseLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleHJ212App
{
    public class Program
    {
        static void Main(string[] args)
        {
            string buffer = "##0637ST=22;CN=2011;PW=123456;MN=LB4200001;CP=&&DataTime=20210107143500;a05002-Rtd=0,a05002-Flag=N;a21005-Rtd=0.825,a21005-Flag=N;a34006-Rtd=874,a34006-Flag=N;a24088-Rtd=0,a24088-Flag=N;a21003-Rtd=5.656,a21003-Flag=N;a21004-Rtd=9.253,a21004-Flag=N;a21002-Rtd=17.894,a21002-Flag=N;a05024-Rtd=5.803,a05024-Flag=N;a34007-Rtd=2679,a34007-Flag=N;a34002-Rtd=123.97,a34002-Flag=N;a34004-Rtd=24.82,a34004-Flag=N;a01006-Rtd=1032.3,a01006-Flag=N;a01002-Rtd=20.9,a01002-Flag=N;a21026-Rtd=0.795,a21026-Flag=N;a01007-Rtd=3.3,a01007-Flag=N;a34049-Rtd=3553,a34049-Flag=N;a01001-Rtd=-0.6,a01001-Flag=N;a99999-Rtd=0,a99999-Flag=N;a01008-Rtd=349,a01008-Flag=N&&3300\r\n##0404ST=22;CN=2011;PW=123456;MN=LB4200001;CP=&&DataTime=20210107152600;a05002-Rtd=1.755,a05002-Flag=N;a21005-Rtd=0.699,a21005-Flag=N;a34006-Rtd=845,a34006-Flag=N;a24088-Rtd=4.945,a24088-Flag=N;a21003-Rtd=3.877,a21003-Flag=N;a21002-Rtd=14.502,a21002-Flag=N;a05024-Rtd=10.389,a05024-Flag=N;a34007-Rtd=2859,a34007-Flag=N;a21026-Rtd=1.525,a21026-Flag=N;a34049-Rtd=3704,a34049-Flag=N;a99999-Rtd=6.7,a99999-Flag=N&&9C01\r\n";
            List<HJ212> hj212List = HJ212.Parse(buffer);
            foreach (HJ212 item in hj212List)
            {
                Console.WriteLine(item.ToString());
            }
        }
    }
}

并引用HJ212ParseLibrary库项目,在VS2022中运行结果如下图所示:
调试运行结果1

运行结果如下:
解析HJ212协议测试实例
可以看到,我们输入的数据和解析到的数据是一致的,有2个协议数据报文。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1523255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

T1.数据库MySQL

二.SQL分类 2.1 DDL 2.1.1数据库操作 1). 查询所有数据库 show databases ; 2). 查询当前数据库 select database(); 3)创建数据库 create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则] ; 4&#xff09;删除数据库 drop database …

【你也能从零基础学会网站开发】Web建站之jQuery进阶篇 jQuery常见属性和方法概述与使用

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 jQuery创建新的…

Docker 哲学 - 容器操作 (二)

命令行启动 参数键值之间可以使 " " 或者 空格 卷的挂载是在容器创建时指定的&#xff0c;不能在容器运行时再添加 当加上 --network-alias 设置同一网络下别名参数后 &#xff0c;inspect 该容器发现 会同步到 容器信息中 2、给容器打日志 docker logs 【-…

PHP+golang开源办公系统CRM管理系统

基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约&#xff0c;易于功能扩展&#xff0c;方便二次开发。 服务器运行环境要求 PHP > 7.…

静态库与动态库的制作和使用

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 前言 库&#xff1f; 为什么要使用库 静态库 静态库的制作和使用 动态库 动态库的制作和使用 四种方法&#xff1a; 直接将库拷贝(安装)到系统路径中 配置环境变量 软链接 添加配置文件 动态库和静态库同时…

jvm 内存泄露、内存溢出、栈溢出区别

JVM&#xff08;Java虚拟机&#xff09;是负责执行Java程序的运行环境。以下是对内存泄露、内存溢出和栈溢出这几个概念的解释&#xff1a; 内存泄露&#xff08;Memory Leak&#xff09;&#xff1a; 内存泄露指的是程序中分配的内存空间在不再被使用时没有被释放的情况。这可…

【python】集合

前言 简洁整理&#xff0c;无废话 集合概念 含义&#xff1a;跟数学中的基本一样 形式&#xff1a;{1,a,(1,2)} 性质&#xff1a;不重复性&#xff0c;集合中每个元素不会有重复&#xff1b;集合中必须是不可变元素&#xff0c;不能有列表可以有元组 创建&#xff1a;{}或…

2核4g服务器可以带多少用户?

腾讯云轻量应用服务器2核4G5M配置性能测评&#xff0c;腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;并发数10&#xff0c;支持每天5000IP人数访问&#xff0c;腾讯云百科txybk.com整理2核4G服务器支持多少人同时在线&#xff1f;并发数测试、CPU性能、内存性能、…

Unity2019.2.x 导出apk 安装到安卓Android12+及以上的系统版本 安装出现-108 安装包似乎无效的解决办法

Unity2019.2.x 导出apk 安装到安卓Android12及以上的系统版本 安装出现-108 安装包似乎无效的解决办法 导出AndroidStudio工程后 需要设置 build.gradle文件 // GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAINbuildscript {repositor…

Vue3+TypeScript 学习回顾,温故而知新

文章简介&#xff1a; &#xff08;1&#xff09;简介&#xff1a; 在 Vue3 中编码规范如下&#xff1a; 编码语言: JavaScript代码风格: 组合式API选项式、API简写形式: setup语法糖 &#xff08;2&#xff09;复习内容&#xff1a; 1.核心: ref、reactive、computed、w…

亚马逊云科技Glue

Glue 最重要的部分&#xff0c; ETL&#xff1a;用于从 A 点&#xff08;我们的源数据&#xff09;提取、转换和加载数据到 B 点&#xff08;目标文件或数据存储库&#xff09;。 AWS Glue 会为您执行大量此类工作。 转换通常是更繁重的工作&#xff0c;需要从各种来源进行组合…

springboot整合swagger,postman,接口规范

一、postman介绍 1.1概述 工具下载 Postman&#xff08;发送 http 请求的工具&#xff09; 官网&#xff08;下载速度比较慢&#xff09;&#xff1a;Download Postman | Get Started for Free 网盘下载&#xff1a;百度网盘 请输入提取码 1.2Http 请求格式 请求地址请求方法状…

算法刷题笔记

1.力扣-1337.矩阵中战斗力最弱的K行 给你一个大小为 m * n 的矩阵 mat&#xff0c;矩阵由若干军人和平民组成&#xff0c;分别用 1 和 0 表示。 请你返回矩阵中战斗力最弱的 k 行的索引&#xff0c;按从最弱到最强排序。 如果第 i 行的军人数量少于第 j 行&#xff0c;或者两行…

C语言分析基础排序算法——归并排序

目录 归并排序 递归版本 非递归版本 非递归版本的问题 归并排序小优化 归并排序 归并排序&#xff0c;分为分治以及合并&#xff0c;分治部分可以使用递归或者非递归完成&#xff0c;归并排序的基本思路是&#xff1a;将已有序的子序列合并&#xff0c;得到完全有序的序列…

探索递归函数:C语言中的使用方法

递归函数是一种在程序设计中常见且强大的工具&#xff0c;它可以将一个问题分解成更小的子问题&#xff0c;并通过反复调用自身来解决这些子问题。在C语言中&#xff0c;递归函数的运用极大地增强了程序的灵活性和可读性。本文将探讨C语言中如何使用递归函数&#xff0c;以及递…

Python之Web开发中级教程----搭建Web框架二

Python之Web开发中级教程----搭建Web框架二 搭建虚拟环境 虚拟环境的作用 虚拟环境可以搭建独立的python运行环境, 使得单个项目的运行环境与其它项目互不影响. 搭建虚拟环境 &#xff08;1&#xff09;安装 sudo pip install virtualenv sudo pip install virtualenvwra…

JUC之AQS

AQS抽象的队列同步器 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {AbstractQueuedSynchronizer 是用来实现锁或者其他同步器组件的公共基础部分的抽象实现&#xff0c;是重量级基础框架及整个JUC体…

60 个深度学习教程:包含论文、实现和注释 | 开源日报 No.202

labmlai/annotated_deep_learning_paper_implementations Stars: 44.0k License: MIT annotated_deep_learning_paper_implementations 是一个包含深度学习论文的 60 个实现/教程&#xff0c;附带并排注释&#xff1b;包括 transformers&#xff08;原始、xl、switch、feedbac…

区块链推广海外市场怎么做,CloudNEO服务商免费为您定制个性化营销方案

随着区块链技术的不断发展和应用场景的扩大&#xff0c;区块链项目希望能够进入海外市场并取得成功已成为越来越多公司的目标之一。然而&#xff0c;要在海外市场推广区块链项目&#xff0c;需要采取有效的营销策略和措施。作为您的区块链项目营销服务商&#xff0c;CloudNEO将…

超详细——VsCode连接远程主机/虚拟机并设置免密登录

超详细——VsCode连接远程主机/虚拟机并设置免密登录 文章目录 超详细——VsCode连接远程主机/虚拟机并设置免密登录[toc]连接到远程主机/虚拟机步骤1、打开vscode拓展 搜索 remote-ssh并安装步骤2、打开远程主机/虚拟机的ssh服务步骤3、设置连接远程主机的配置文件 设置免密登…