密码学—Vigenere破解Python程序

news2024/9/21 11:15:27

文章目录

    • 概要
    • 预备知识点学习
    • 整体流程
    • 技术名词解释
    • 技术细节
    • 小结
    • 代码

概要

破解Vigenere需要Kasiski测试法与重合指数法的理论基础

具体知识点细节看下面这两篇文章

预备知识点学习

下面两个是结合起来使用猜测密钥长度的,只有确认了密钥长度之后才可以进行破解。

❀点击学习Kasiski测试法❀
❀点击学习重合指数法❀

整体流程

破解的方法就是不同的凯撒表中每个字母相乘得到的交互重合指数是否接近0.065,并且每两个表都要通过将凯撒表位移得到最接近0.065的那个偏移量,然后就可以得知这两个表相对位移多少的时候交互重合指数最接近0.065,以此类推将所有单表能够一起的可能组合都算一遍指数,最后观察每个组合MIC然后找出接近0.065且算出偏移量将所有未知数列出公式计算可得出26份密钥(难点在于如何计算出26份密钥!!!为此本座真的头秃了一大片)

流程如下
按照猜测的密钥长度分组计算单个凯撒表的每一个字母出现的频率

所有凯撒表两两组合

计算俩表之间的偏移量为g的时候,对应的交互重合指数为MIC
这里的重合指数计算是通过两个表的下标一致来进行相乘,只是偏移量不同
然后会出现26次偏移,因为凯撒密钥空间为26个字母
因此俩凯撒表组合就会产生26份重合指数。如图所示为任意两组的MIC数据
在这里插入图片描述

得到了任意组合的26份交互重合指数后我们要找出最接近0.065的,并且算出他的相对位移(偏移量),就会得到类似的公式。如何在程序中实现,这个让我头秃了(后面附上代码)


i=0,j=1,相对位移s=?时,MIc=最接近0.065



i=4,j=5,相对位移s=?时,MIc=最接近0.065
则有:
k0-k2=2
k0-k3=9



k4-k5=4
由此求得密钥字间的相对位移

因为已知k0 ~ k5运算得到的公式,然后我们可以假定k0为a ~ z的字母,得到一个字母之后其他未知数也就能够计算了,因此会产生26份密钥出来


有26份密钥之后,需要找出最真密钥,其他都是伪密钥。
需要用到统计的26个字母常用的频率,这个可能就是Vigenere的缺陷了,一旦是一个非正常文本就几乎破解不了。因为用的就是常用词频统计的出来的概率,同样这个表不用我们统计,网上一查一堆。这里将其称为AZ表

'''a-z的频率,这是不是指你加密的词频,
而是大家阅读各式各样的文章中出现次数
最多的也可以理解为大数据统计出来的'''
[0.082, 0.015, 0.028, 0.043, 
0.127, 0.022, 0.02, 0.061,
 0.07, 0.002, 0.008,0.04, 
 0.024, 0.067, 0.075, 0.019, 
 0.001, 0.06, 0.063, 0.091,
  0.028, 0.01, 0.023, 0.001, 
  0.02, 0.001]

由于已知密钥了,所以只需要将其密钥的单个字母对单个凯撒解密之后,将其解密的凯撒表与AZ表对应的字母概率相乘,最后将其所有结果加起来是否接近0.065,如果接近则可以考虑将其纳入真实密钥的列表名单。

最后缩小范围得到所有最接近密钥之后,我们可以选择采用一个一个的将密文解密,解出来之后看是否读的通顺即为密钥。(我到了这一步就用肉眼观察了)

技术名词解释

  • Ic重合指数:指的是单个凯撒表的每一个字母概率的平方,最后26个字母得出的概率相加
  • MIc交互重合指数:不同表之间对应字母概率相乘,最后26个字母得出的概率相加
  • 相对位移:不同表之间虽然用不同密钥加密,但是不同密钥之间又有一个密钥距离,比如1表用a加密,2表用c加密,那么这两个表的相对位移就是2,但是在破解的时候不知道是偏移多少,所以需要循环将其偏移,然后计算MIc计算出最接近0.065的就是可能的真实偏移量。
    具体为何要偏移26,是因为密钥空间26,所以你必须位移26次。

技术细节

程序中实现的分工必须要很明确,否则乱成一坨。

函数分工
shiftCaserCode:将密文成不同的凯撒表
caltMIC:计算重合指数
caltResult:找出所有可能的密钥
findEndKey:通过与AZ表概率交互重合得出最接近0.065的密钥

小结

破解Vigenere十分艰辛,但是收获满满,不仅对破解有了一定了解,很大一部分影响是编程技巧在此得到了提升,对自己的编程能力更加有信心。
比如如何在Kasiski测试法中如何找到片段出现次数最多且要考虑不同的长度片段的统计还要计算出相同长度出现次数最高的那一个。
在计算重合指数的时候对字典有了一个更深的了解,字典可以按需使用,我这里就采用数组作为键,大大提高了编程效率,还认识到了在3.6版本之后的Python已经支持按顺序打印输出了,不再是无序的字典,这也帮我解决了很大一部分繁琐的操作。

代码

由于在我的程序中是用类和pyqt5结合使用,所以下面的函数都是在类中定义的,主要运行功能在outputKeys函数中进入


class CrackVigenere(ModuleCode):
    name = 'CrackVigenere'
    # 正常文本统计出来的概率
    NormalFrequ = [0.082, 0.015, 0.028, 0.043, 0.127, 0.022, 0.02, 0.061, 0.07, 0.002, 0.008,
                   0.04, 0.024, 0.067, 0.075, 0.019, 0.001, 0.06, 0.063, 0.091, 0.028, 0.01, 0.023, 0.001, 0.02, 0.001]

    def __init__(self, appInit):
        super().__init__(appInit, self.name)

    def newMe(self, appInit):
        return CrackVigenere(appInit)

    def InitUI(self):
        super(CrackVigenere, self).InitUI()

    def bindCapabilities(self):
        self.enterbtn.clicked.connect(lambda: self.outputKeys())
        self.save_file_btn.clicked.connect(lambda: self.saveFileText())
        self.choice_file_btn.clicked.connect(lambda: self.choiceFIleText())

    # ===============================================找出所有可能的密钥==================================================
    def shiftCaserCode(self, mess, keylen):
        newdit = {}
        for i in range(keylen):
            newdit[i] = []
        c = self.messGroupByN(mess, keylen)  # 将密文分组
        for i in range(len(c)):  # 将分好组的c密文抽出每一行对应一个加密字母,变成一行的单行凯撒加密
            for j in range(len(c[i])):
                if j < len(c[i]):
                    newdit[j].append(ord(c[i][j]) - ord('A'))
        return newdit

    def messGroupByN(self, mess, n):  # 根据密钥长度分组
        sunlist = []
        temp = [''] * n
        # print(math.ceil(len(mess) / n))
        count = 0
        # print(mess)
        for ch in mess:
            if ord('A') <= ord(ch) <= ord('Z') or ord('a') <= ord(ch) <= ord('z'):
                temp[count % n] = ch.upper()
                count += 1
                if count % n == 0 and count != 0:
                    sunlist.append(temp[0:n + 1])
                    temp = [''] * n
        if count % n != 0:
            sunlist.append(list(temp[0:count % n]))
        return sunlist

    def caltMIC(self, newdit, keylen):
        dit_A_Z = {}  # 保存本行每一个字母出现的次数
        curr_p = {}  # # 对应该行每一个字母出现的概率
        nextdit_A_Z = {}  # 保存本行每一个字母出现的次数
        nextcurr_p = {}  # # 对应该行每一个字母出现的概率
        MIc = {}  # 是curr_p * nextcurr_p 的遍历,交互重合指数

        for i in range(26):  # 初始化字典,分别用于存放26个 字母出现次数 和 相对位移的概率
            dit_A_Z[i] = 0
            curr_p[i] = 0
            nextdit_A_Z[i] = 0
            nextcurr_p[i] = 0

        for i in range(keylen - 1):  # (keylen - 1 * keylen)/ 2 = 总次数,因为每一行都要对应进行一次重合指数的计算

            for j in range(len(newdit[i])): dit_A_Z[newdit[i][j]] += 1  # 统计i行中字母次数,newdit[i]对应每一行的长度

            for row in range(i + 1, keylen):  # 操作i行后面的行
                p = []  # 存放一组某行j中与i的相对位移的每一个概率
                for j in range(len(newdit[row])): nextdit_A_Z[newdit[row][j]] += 1  # 同理先统计j行中字母次数
                sum = 0
                for j in range(26):  # 统计每一个字母出现的概率
                    curr_p[j] = dit_A_Z[j] / len(newdit[i])  # i行每个字母出现的概率,放进这里一起统计能够减少i的一次统计循环
                    nextcurr_p[j] = nextdit_A_Z[j] / len(newdit[row])  # j行出现字母的概率
                maxnum = 0
                temp_Mic = {}
                print("========================{},{}=================================\n".format(i, row))

                for g in range(26):  # 开始计算相对位移的每个概率
                    for index in range(26):  # 计算MIc交互重合指数,相对位移是 g
                        sum += curr_p[(index + g) % 26] * nextcurr_p[index]  # 本行位移,往后的行不位移
                    if float(self.Icleft.toPlainText()) < sum < float(self.Icright.toPlainText()):
                        # 目前是按照区间寻找接近0.065
                        # 若在本轮ij的重合指数最接近0.065,然后记录下对应的i j g 和概率p(保留三位小数)
                        temp_Mic[(i, row, g)] = self.loseTailNum(sum, 3)
                        # MIc[(i, row, g)] = self.loseTailNum(sum, 3)
                    # break(是否退出循环?有待考证)

                    print("{}".format(self.loseTailNum(sum, 3)), end='  ')
                    if (g + 1) % 9 == 0: print()

                    sum = 0  # 清空继续存下一个相对位移概率

                print("\n=========================================================")
                if self.checkbox.isChecked():
                    if len(temp_Mic) != 0:  # 当26个相对位移计算完就找出本轮i j交互重合指数最大的,并且是上面判断过要接近0.065的
                        max_item = sorted(temp_Mic.items(), key=lambda x: x[1], reverse=True)[0]
                        MIc[max_item[0]] = max_item[1]
                else:
                    for item in temp_Mic.items():
                        MIc[item[0]] = item[1]

                for d in range(26):  # 清空数据重新加载下一j行的数据
                    curr_p[d] = 0
                    nextdit_A_Z[d] = 0
                    nextcurr_p[d] = 0
            for d in range(26): dit_A_Z[d] = 0  # 返回循环之后,i需往后移动,所以保存上一行的数据要清空
        print("每一项元素为:(i, j, g): (i和j的交互重合指数)\n", MIc)
        return MIc

    def loseTailNum(self, num, Tailindex):
        newnum = str(num).split(".")[0] + "." + str(num).split(".")[1][0:Tailindex]
        # print(float(newnum))
        return float(newnum)

    def caltResult(self, DIT_jie, MIc):
        keylen = len(DIT_jie)
        save = []  # 保存每一组解,每组解都是一个字典
        for i in range(26):
            for j in range(keylen): DIT_jie[j] = None

            DIT_jie[0] = i  # 仅用第一个未知变量可就出所有

            for k in MIc.keys():  # MIc.keys()的结构: (i,j,g),可以通过i j来判断是代表keys哪一个位置的字母

                for j in range(26):
                    trigger = True
                    for v in DIT_jie.values():
                        if v == None:
                            trigger = False
                            break
                    if trigger == True: break

                    if DIT_jie[k[0]] == None:  # 最后左边
                        DIT_jie[k[0]] = 0
                        for j_2 in range(26):
                            if (DIT_jie[k[0]] - DIT_jie[k[1]]) % 26 == k[2]:
                                break
                            else:
                                DIT_jie[k[0]] = j_2
                    elif DIT_jie[k[1]] == None:  # 最后右边
                        DIT_jie[k[1]] = 0
                        for j_2 in range(26):
                            if (DIT_jie[k[0]] - DIT_jie[k[1]]) % 26 == k[2]:
                                break
                            else:
                                DIT_jie[k[1]] = j_2
                    else:  # 正常计算
                        if DIT_jie[k[1]] == None: DIT_jie[k[1]] = 0
                        if (DIT_jie[k[0]] - DIT_jie[k[1]]) % 26 == k[2]:
                            # print(DIT_jie[k[0]],"-",DIT_jie[k[1]], "=", k[2])
                            break
                        else:
                            DIT_jie[k[1]] = j
                        if j == 25: DIT_jie[k[1]] = 0

            # 如果说在本轮的k中符合了该方程就退出循环,
            # 否则一直通过循环j改变第二个值,因为这里i必定大于j,所以只要i对应的未知变量确定了第二个只需通过迭代循环找出符合条件即可
            save.append(DIT_jie.copy())  # 保存本轮循环的解
        return save

    def findKeys(self, cipher, keylen):
        # =======================================================================================================================
        # 定义
        MIc = {}  # 是curr_p * nextcurr_p 的遍历,交互重合指数
        newdit = {}  # 首先生成一个新表,存放对应每一个key的单行凯撒加密,长度为keylen的表,即row为keylen
        DIT_jie = {}  # 键:keylen就有多少个键,键对应着是第i?key,值对应着第i个key等于多少
        # =======================================================================================================================
        # 初始化
        for i in range(keylen):
            DIT_jie[i] = 0
        # =======================================================================================================================
        # 生成每一行的凯撒加密
        newdit = self.shiftCaserCode(cipher, keylen)
        # =======================================================================================================================
        # MIC重合指数计算
        MIc = self.caltMIC(newdit, keylen)
        # =======================================================================================================================
        # 找出所有可能的密钥
        save = []  # 保存每一组解,每组解都是一个字典
        save = self.caltResult(DIT_jie, MIc)
        # =======================================================================================================================
        # 下面是密钥数字转密钥字母
        re = []  # 保存每一组的数字解
        allkeys = []  # 保存每一组密钥的字母解
        t = []
        for i in save: re.append(i.values())  # 取出每一组解的字典的值,即该组解的结果
        for i in re:
            for j in i: t.append(chr(j + ord('A')))
            allkeys.append(t.copy())
            t.clear()  # 清空列表,继续循环保存每个字母解
        # print(allkeys)  # 打印测试
        self.showPLT(MIc)  # 显示每一组解对应的i,j的MIC值
        return allkeys, newdit  # 返回所有字母解

    def showPLT(self, MIc):
        plt.close()
        x_data = ["({},{})\ng={}".format(i[0][0], i[0][1], i[0][2]) for i in MIc.items()]
        y_data = [round(i, 3) for i in MIc.values()]

        plt.rcParams["font.sans-serif"] = ['SimHei']
        plt.rcParams["axes.unicode_minus"] = False

        plt.subplot(111)
        plt.plot(x_data, y_data, marker='.', c='r', ms=5, linewidth='1', label="线形交互重合指数")
        plt.legend(loc="lower right")
        for i in range(len(x_data)):
            plt.text(i, y_data[i], str(y_data[i]), ha="center", va="bottom", fontsize=10)
        plt.title("密文子串中最接近0.065的交互重合指数")
        plt.xlabel("(i,j)和相对位移g")
        plt.show()

    # ========================================找出最后最符合的唯一密钥======================================================================

    def findEndKey(self, keys, newdit):

        dit_A_Z = {}  # 保存本行每一个字母出现的次数
        curr_p = {}  # # 对应该行每一个字母出现的概率
        MIc = {}  # 是curr_p * nextcurr_p 的遍历,交互重合指数

        for i in range(26):  # 初始化字典,分别用于存放26个 字母出现次数 和 相对位移的概率
            dit_A_Z[i] = 0
            curr_p[i] = 0
        A_Z_P = []
        for key in keys:
            sum = 0  # 清空继续存下一个相对位移概率
            for i in range(len(key)):  # (keylen - 1 * keylen)/ 2 = 总次数,因为每一行都要对应进行一次重合指数的计算
                # g = keys

                for j in range(len(newdit[i])): dit_A_Z[newdit[i][j]] += 1  # 统计i行中字母次数,newdit[i]对应每一行的长度

                for j in range(26):  # 统计每一个字母出现的概率
                    curr_p[j] = dit_A_Z[j] / len(newdit[i])  # i行每个字母出现的概率

                g = ord(key[i]) - ord('A')  # 密钥中一个字符就是一个相对位移
                for index in range(26):  # 计算MIc交互重合指数,相对位移是 g
                    sum += curr_p[(index + g) % 26] * self.NormalFrequ[index]  # 本行位移,往后的行不位移

                # print(sum)
                for d in range(26):  # 清空数据重新加载下一j行的数据
                    curr_p[d] = 0
                    dit_A_Z[d] = 0  # 返回循环之后,i需往后移动,所以保存上一行的数据要清空
            A_Z_P.append(sum / len(key))
        max_num = 0
        max_index = 0
        for i in range(len(A_Z_P)):
            if A_Z_P[i] > max_num:
                max_index = i
                max_num = A_Z_P[i]
        return [A_Z_P, (max_index, max_num)]
    
    #功能运行入口
    def outputKeys(self):

        key = self.keytext.toPlainText()
        c = self.input_text.toPlainText()

        if c == '':
            self.input_text.setText('what is your text?')
            return
        elif key == '':
            self.input_text.setText('what is your key?')
            return

        save_Ic = None
        self.outPut_text.clear()  # 清理掉之前保留的文本,以防每次输出都叠加
        cipher = ''  # 存放没有非法符号的密文
        for ch in c:
            if ord('A') <= ord(ch) <= ord('Z') or ord('a') <= ord(ch) <= ord('z'):
                cipher += ch
        print('发现密钥中...')

        chkeys, newdit = self.findKeys(cipher, int(key))

        A_Z_P, data = self.findEndKey(chkeys, newdit)  # 拆包
        keyIndex, keyIc = data  # 拆包
        keyIc = self.loseTailNum(keyIc, 3)
        endkey = ""
        for ch in chkeys[keyIndex]:
            endkey += ch
        self.message += "最有可能的密钥:{}\n重合指数为:{}\n".format(endkey, keyIc)

        for i in chkeys:
            self.message += ''.join(i)
            self.message += '\n'
        self.outPut_text.setText(self.message)
        self.toSaveFile = ''.join(self.message)
        self.finalizeGC()  # 清除保存的数据用于下次计算
        print('成功')

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

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

相关文章

Jupyter Notebook左侧大纲目录设置

在 Jupyter Notebook 中&#xff0c;可以通过安装jupyter_contrib_nbextensions插件来实现在页面左边显示大纲的功能。 1. 安装插件 pip install jupyter_contrib_nbextensions 1.1 如何安装 windows cmd小黑裙窗口&#xff1b; 1.查看目前安装了哪些库 conda list 2. 使用…

【Oracle】springboot连接Oracle写入blob类型图片数据

目录 一、表结构二、mapper 接口和sql三、实体类四、controller五、插入成功后的效果 springboot连接Oracle写入blob类型图片数据 一、表结构 -- 创建表: student_info 属主: scott (默认当前用户) create table scott.student_info (sno number(10) constraint pk_si…

Vue3 完整项目搭建 Vue3+Pinia+Vant3/ElementPlus+typerscript

❤ Vue3 项目 1、Vue3+Pinia+Vant3/ElementPlus+typerscript环境搭建 1、安装 Vue-cli 3.0 脚手架工具 npm install -g @vue/cli2、安装vite环境 npm init @vitejs/app报错 使用: yarn create @vitejs/app依然报错 转而使用推荐的: npm c

Redisson分布式锁原理

1、Redisson简介 一个基于Redis实现的分布式工具&#xff0c;有基本分布式对象和高级又抽象的分布式服务&#xff0c;为每个试图再造分布式轮子的程序员带来了大部分分布式问题的解决办法。 2、使用方法 引入依赖 <dependency><groupId>org.springframework.bo…

基于Python所写的Word助手设计

点击以下链接获取源码资源&#xff1a; https://download.csdn.net/download/qq_64505944/87959100?spm1001.2014.3001.5503 《Word助手》程序使用说明 在PyCharm中运行《Word助手》即可进入如图1所示的系统主界面。在该界面中&#xff0c;通过顶部的工具栏可以选择所要进行的…

阿里云顺利通过云原生中间件成熟度评估

前言&#xff1a; 2023 年 6 月 6 日&#xff0c;由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;承办的“ICT中国2023 高层论坛-云原生产业发展论坛”在北京召开&#xff0c;会上正式发布了一系列云原生领域评估结果。阿里云计算有限公司&#xff08;以…

图解红黑树

gitee仓库&#xff1a;https://gitee.com/WangZihao64/data-structure-and-algorithm/tree/master/RBTree 目录 概念红黑树的性质红黑树的调整规则 概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Bl…

Redis设计与实现笔记之字典

1.字典的实现 Redis中字典使用的哈希表结构 typedef struct dictht {// 哈希表数组dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩码&#xff0c;用于计算索引值// 总是等于 size - 1unsigned long sizemask;// 该哈希表已有节点的数量unsigned long use…

3D web可视化工具HOOPS Communicator与Autodesk的对比分析

越来越多的开发人员转向基于Web的2D和3D可视化和交互服务。这些使您只需使用网络浏览器即可快速向同事、客户或其他任何人展示设计。该领域的工具提供了大量功能&#xff0c;这些功能可能适合也可能不适合您的特定开发需求。 HOOPS Communicator的原始开发人员之一分享了对该市…

chatgpt赋能python:Python输出NaN的原因及解决方法

Python输出NaN的原因及解决方法 NaN&#xff08;Not a Number&#xff09;是一种特殊的数值类型&#xff0c;表示不是一个数字。在Python中&#xff0c;当某种计算结果无法表示为有限数字时&#xff0c;就会输出NaN。本文将介绍Python中输出NaN的原因&#xff0c;并提供一些解…

python: more Layer Architecture and its Implementation in Python

sql server: --学生表 DROP TABLE DuStudentList GO create table DuStudentList (StudentId INT IDENTITY(1,1) PRIMARY KEY,StudentName nvarchar(50),StudentNO varchar(50), --学号StudentBirthday datetime --学生生日 ) go mod…

Qt关闭主窗口后,退出所有异步线程

目录 1.要知道主窗口什么时候关闭2.关闭异步线程 1.要知道主窗口什么时候关闭 在widget.h新增下面的函数 private slots:void closeEvent(QCloseEvent *event);在widget.cpp新增 void Widget::closeEvent(QCloseEvent *event) {qDebug() << "关闭主窗口了&#x…

「网络编程」第三讲:认识协议及简单的协议定制

「前言」文章内容是关于协议的&#xff0c;大致内容是再次认识协议及简单协议的定制&#xff0c;目的是帮助理解协议&#xff0c;下面开始讲解&#xff01; 「归属专栏」网络编程 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」「句子分享」 我与我周…

qt 调节win声音版本大小

QT4 情况下&#xff0c;运行的&#xff0c;会出错&#xff0c;目前暂时没有办法解决在&#xff0c;win下调节音量大小问题在这里插入代码片 参考资料 QT 对window系统下音量的设置和获取 还有个很好贴子&#xff0c;没有找到

LLM 应用参考架构:ArchGuard Co-mate 实践示例

随着&#xff0c;对于 LLM 应用于架构领域探索的进一步深入&#xff0c;以及 ArchGuard Co-mate 开发进入深入区&#xff0c;我们发现越来越多的通用模式。 在先前的文章里&#xff0c;我们总结了一系列的设计原则&#xff0c;在这篇文章里&#xff0c;我们将介绍 ArchGuard Co…

架构重构|性能和扩展性大幅提升的Share Creators智能数字资产管理软件3.0

作为数字资产管理行业的领军者&#xff0c;Share Creators智能数字资产管理软件持续致力于帮助企业和团队智能化管理数字资产&#xff0c;提升工业化管线制作效率。经过本次重构&#xff0c;Share Creators 3.0版本重装上阵&#xff0c;全面更新的服务架构标志着软件整体性能的…

C语言-数字爆炸游戏

问题&#xff1a; 你好&#xff0c;欢迎来到数字爆炸&#xff0c;系统随机生成一个数字&#xff0c;猜大了&#xff0c;提示猜大了&#xff0c;猜小了&#xff0c;提示猜小了。 思路&#xff1a; 先写游戏大概思路首先&#xff0c;会有菜单吧&#xff0c;所以先写一个菜单函数…

服务启动后能ping通但无法访问

近期业务需要&#xff0c;重启了服务器&#xff08;centos 7.4&#xff09;&#xff0c;但是各类服务启动后&#xff0c;仍然无法访问&#xff0c;Nginx的白页面都访问不到&#xff0c;能ping通&#xff0c;nslookup 域名解析也没问题&#xff0c;考虑是防火墙 -- 查看防火墙启…

《2023 信创软件品牌影响力报告》发布!融云入选「信创生态」代表厂商

信创正由试点实践转向全面推广。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 由于外围环境收紧和国内数字经济高速发展等原因&#xff0c;信创产业在顶层战略的指引推动下蓬勃发展&#xff0c;相关政策有力促进了芯片、操作系统、数据库、办公软件、工业软…

jquery html页面先加载内容过几秒后显示数据

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;推荐专栏&#xff1a;点击进入查看 &a…