网易云VIP音乐NCM文件转MP3,C语言版本。
前言
网易云的Vip音乐下载下来,格式不是mp3/flac这种通用的音乐格式,而是经过加密的ncm文件。只有用网易云的音乐App才能够打开。于是想到可不可以把.ncm文件转换成mp3或者flac文件,上google查了一下,发现有不少人已经做了这件事,但没有发现C语言版本的,就想着写一个纯C语言版本的ncm转mp3/flac。
NCM文件结构
ncm文件的结构,网上有人解析出来了,分为下面几个部分
| 信息 | 大小 | 说明 |
|---|---|---|
| Magic Header | 10 bytes | 文件头 |
| Key Length | 4 bytes | AES128加密后的RC4密钥长度,字节是按小端排序。 |
| Key Data | Key Length | 用AES128加密后的RC4密钥。 1. 先按字节对0x64进行异或。 2. AES解密,去除填充部分。 3. 去除最前面'neteasecloudmusic'17个字节,得到RC4密钥。 |
| Music Info Length | 4 bytes | 音乐相关信息的长度,小端排序。 |
| Music Info Data | Music Info Length | Json格式音乐信息数据。 1. 按字节对0x63进行异或。 2. 去除最前面22个字节。 3. Base64进行解码。 4. AES解密。 6. 去除前面6个字节得到Json数据。 |
| CRC | 4 bytes | 跳过 |
| Gap | 5 bytes | 跳过 |
| Image Size | 4 bytes | 图片的大小 |
| Image | Image Size | 图片数据 |
| Music Data | - | 1. RC4-KSA生成S盒。 2. 用S盒解密(自定义的解密方法),不是RC4-PRGA解密。 |
两个AES对应密钥
unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };
unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 };
不得不佩服当初破解这个东西的人,不仅把文件结构摸得请清楚楚,还把密钥也搞到手,应该是个破解大神。有了上面的东西,剩下的就很简单了,按部就班来就行了。
一些算法准备
开始前我们需要把AES算法,BASE64算法,RC4算法和Json解析算法先写好。
除此之外还有一个编码问题,解析出来的ncm文件是用utf-8编码存储的,所以它在中文windows系统下汉字会出现乱码,因为中文windows系统采用的编码是GBK,两者不兼容,所以我们要写一个编码转换算法,将utf8格式字符串转位GBK的。Linux下不用转换,Linux本身就是用UTF-8的。
C语言没有这些库,都要自己来。
- AES用GitHub上的
tiny-AES-c - JSON用GitHub上的CJSON
CJSON - Base64和RC4算法比较简单我们自己写
unsigned char* base64_decode(unsigned char* code,int len,int * actLen)
{
//根据base64表,以字符找到对应的十进制数据
int table[] = { 0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,52,53,54,55,56,57,58,
59,60,61,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,
22,23,24,25,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51
};
long str_len;
unsigned char* res;
int i, j;
//计算解码后的字符串长度
//判断编码后的字符串后是否有=
if (strstr(code, "=="))
str_len = len / 4 * 3 - 2;
else if (strstr(code, "="))
str_len = len / 4 * 3 - 1;
else
str_len = len / 4 * 3;
*actLen = str_len;
res = malloc(sizeof(unsigned char) * str_len + 1);
res[str_len] = '\0';
//以4个字符为一位进行解码
for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
{
res[j] = ((unsigned char)table[code[i]]) << 2 | (((unsigned char)table[code[i + 1]]) >> 4);
res[j + 1] = (((unsigned char)table[code[i + 1]]) << 4) | (((unsigned char)table[code[i + 2]]) >> 2);
res[j + 2] = (((unsigned char)table[code[i + 2]]) << 6) | ((unsigned char)table[code[i + 3]]);
}
return res;
}
- RC4生成S盒
//用key生成S盒
/*
* s: s盒
* key: 密钥
* len: 密钥长度
*/
void rc4Init(unsigned char* s, const unsigned char* key, int len)
{
int i = 0, j = 0;
unsigned char T[256] = { 0 };
for (i = 0; i < 256; i++)
{
s[i] = i;
T[i] = key[i % len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + T[i]) % 256;
unsigned tmp = s[i];
s[i]=s[j];
s[j]=tmp;
}
}
//针对NCM文件的解密
//异或关系
/*
* s: s盒
* data: 要加密或者解密的数据
* len: data的长度
*/
void rc4PRGA(unsigned char* s, unsigned char* data, int len)
{
int i = 0;
int j = 0;
int k = 0;
int idx = 0;
for (idx = 0; idx < len; idx++)
{
i = (idx + 1) % 256;
j = (i + s[i]) % 256;
k= (s[i] + s[j]) % 256;
data[idx]^=s[k]; //异或
}
}
- Windows下utf8转GBK
#ifdef WIN32
#include<Windows.h>
//返回转换好的字符串指针
unsigned char* utf8ToGbk(unsigned char*src,int len)
{
wchar_t* tmp = (wchar_t*)malloc(sizeof(wchar_t) * len+2);
unsigned char* newSrc = (unsigned char*)malloc(sizeof(unsigned char) * len + 2);
MultiByteToWideChar(CP_UTF8, 0, src, -1, tmp, len);
WideCharToMultiByte(CP_ACP, 0, tmp, -1, newSrc, len+2, NULL,NULL);
return newSrc;
}
#endif
NCM文件解析
按照NCM文件结构一步一步读取数据来进行解析
//fileName:要转换的文件
void readFileData(const char* fileName)
{
FILE* f;
f = fopen(fileName, "rb");
if (!f)
{
printf("No such file: %s\n", fileName);
return;
}
unsigned char buf[16];
int len=0;
int i = 0;
unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };
unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 };
fseek(f, 10, SEEK_CUR); //f从当前位置移动10个字节
fread(buf, 1, 4, f); //读取rc4 key 的长度
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* rc4Key= (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(rc4Key, 1, len, f); //读取rc4数据
//解密rc4密钥
for (i = 0; i < len; i++)
{
rc4Key[i] ^= 0x64;
}
struct AES_ctx ctx;
AES_init_ctx(&ctx, core_key); //使用core_key密钥
int packSize = len / 16; //采用的是AES-ECB加密方式,和Pkcs7padding填充
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &rc4Key[i * 16]);
}
int pad = rc4Key[len - 1]; //获取填充的长度
rc4Key[len - pad] = '\0'; //去除填充的部分,得到RC4密钥
fread(buf, 1, 4, f); //读取Music Info 长度数据
len = ((buf[3] << 8 | buf[2]) << 16) | (buf[1] << 8 | buf[0]);
unsigned char* meta = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(meta, 1, len, f); //读取Music Info数据
//解析Music info信息
for (i = 0; i < len; i++)
{
meta[i] ^= 0x63;
}
int act = 0;
unsigned char* data = base64_decode(&meta[22], len - 22, &act); //base64解码
AES_init_ctx(&ctx, meta_key); //AES解密
packSize = act / 16;
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &data[i * 16]);
}
pad = data[act - 1];
data[act - pad] = '\0'; //去除填充部分
unsigned char* newData = data;
#ifdef WIN32
newData = utf8ToGbk(data, strlen(data));
#endif
cJSON* cjson = cJSON_Parse(&newData[6]); //json解析,获取格式和名字等
if (cjson == NULL)
{
printf("cjson parse failed\n");
return;
}
//printf("%s\n", cJSON_Print(cjson)); //输出json
fseek(f, 9, SEEK_CUR); //从当前位置跳过9个字节
fread(buf, 1, 4, f); //读取图片大小
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* img = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(img, 1, len, f); //读取图片数据
int offset= 1024 * 1024 * 10; //10MB 音乐数据一般比较大一次读入10MB
int total = 0;
int reSize = offset;
unsigned char* musicData = (unsigned char*)malloc(offset); //10m
while (!feof(f))
{
len = fread(musicData+total, 1, offset, f); //每次读取10M
total += len;
reSize += offset;
musicData=realloc(musicData,reSize); //扩容
}
unsigned char sBox[256] = { 0 }; //s盒
rc4Init(sBox, &rc4Key[17], strlen(&rc4Key[17])); //用rC4密钥进行初始化s盒
rc4PRGA(sBox, musicData, total); //解密
//拼接文件名(artist + music name+format)
char* musicName = cJSON_GetObjectItem(cjson, "musicName")->valuestring;
cJSON* sub = cJSON_GetObjectItem(cjson, "artist");
char*artist=cJSON_GetArrayItem(cJSON_GetArrayItem(sub, 0),0)->valuestring;
char* format = cJSON_GetObjectItem(cjson, "format")->valuestring;
char* saveFileName =(char*)malloc(strlen(musicName) + strlen(artist) + strlen(format)+5);
sprintf(saveFileName, "%s - %s.%s", artist, musicName, format);
FILE* fo=fopen(saveFileName, "wb");
if (fo == NULL)
{
printf("The fileName - '%s' is invalid in this system\n", saveFileName);
}
else
{
fwrite(musicData, 1, total, fo);
fclose(fo);
}
#ifdef WIN32
free(newData);
#endif
free(data);
free(meta);
free(img);
free(musicData);
fclose(f);
}
- AES采用的是AES-ECB模式,pack7padding填充方式。即16个字节为一组,如果不够16个字节,那就缺几个字节就填充几个字节,每个字节的值都是缺少的字节数。所以获取最后一个字节的值就知道要填充了几个字节。
- RC4解密那里,不是按RC4的来的,虽说叫RC4,但只有生成S盒那里是一样的,其它的不是按RC4算法来的。
- 有些解析出来音乐的名字,系统是不支持的,比如带'/'的,在创建新文件写入时会失败。
- 以"結束バンド - ギターと孤独と蒼い惑星.ncm"为例看看它的json数据是怎么样的
{
"musicId": 1991012773,
"musicName": "ギターと孤独と蒼い惑星",
"artist": [["結束バンド", 54103171]],
"albumId": 153542094,
"album": "ギターと孤独と蒼い惑星",
"albumPicDocId": "109951167983448236",
"albumPic": "https://p4.music.126.net/rfstzrVK05hCPjU-4mzSFA==/109951167983448236.jpg",
"bitrate": 320000,
"mp3DocId": "f481d20151f01d5d681d2768d753ad64",
"duration": 229015,
"mvId": 0,
"alias": ["TV动画《孤独摇滚!》插曲"],
"transNames": [],
"format": "mp3",
"flag": 4
}
可以根据需要自由提取需要的信息
完整代码
点击查看代码
/*
* date:2022-12-12
* author: FL
* purpose: ncm file to mp3
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "aes.h"
#include "cJSON.h"
#ifdef WIN32
#include<Windows.h>
//返回转换好的字符串指针
unsigned char* utf8ToGbk(unsigned char*src,int len)
{
wchar_t* tmp = (wchar_t*)malloc(sizeof(wchar_t) * len+2);
unsigned char* newSrc = (unsigned char*)malloc(sizeof(unsigned char) * len + 2);
MultiByteToWideChar(CP_UTF8, 0, src, -1, tmp, len); //转为unicode
WideCharToMultiByte(CP_ACP, 0, tmp, -1, newSrc, len+2, NULL,NULL); //转gbk
return newSrc;
}
#endif
void swap(unsigned char* a, unsigned char* b)
{
unsigned char t = *a;
*a = *b;
*b = t;
}
//用key生成S盒
/*
* s: s盒
* key: 密钥
* len: 密钥长度
*/
void rc4Init(unsigned char* s, const unsigned char* key, int len)
{
int i = 0, j = 0;
unsigned char T[256] = { 0 };
for (i = 0; i < 256; i++)
{
s[i] = i;
T[i] = key[i % len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + T[i]) % 256;
swap(s + i, s + j);
}
}
//针对NCM文件的解密
//异或关系
/*
* s: s盒
* data: 要加密或者解密的数据
* len: data的长度
*/
void rc4PRGA(unsigned char* s, unsigned char* data, int len)
{
int i = 0;
int j = 0;
int k = 0;
int idx = 0;
for (idx = 0; idx < len; idx++)
{
i = (idx + 1) % 256;
j = (i + s[i]) % 256;
k = (s[i] + s[j]) % 256;
data[idx] ^= s[k]; //异或
}
}
//base64 解码
/*
* code: 要解码的数据
*/
unsigned char* base64_decode(unsigned char* code, int len, int* actLen)
{
//根据base64表,以字符找到对应的十进制数据
int table[] = { 0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,62,0,0,0,
63,52,53,54,55,56,57,58,
59,60,61,0,0,0,0,0,0,0,0,
1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,
22,23,24,25,0,0,0,0,0,0,26,
27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51
};
long str_len;
unsigned char* res;
int i, j;
//计算解码后的字符串长度
//判断编码后的字符串后是否有=
if (strstr(code, "=="))
str_len = len / 4 * 3 - 2;
else if (strstr(code, "="))
str_len = len / 4 * 3 - 1;
else
str_len = len / 4 * 3;
*actLen = str_len;
res = malloc(sizeof(unsigned char) * str_len + 1);
res[str_len] = '\0';
//以4个字符为一位进行解码
for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
{
res[j] = ((unsigned char)table[code[i]]) << 2 | (((unsigned char)table[code[i + 1]]) >> 4);
res[j + 1] = (((unsigned char)table[code[i + 1]]) << 4) | (((unsigned char)table[code[i + 2]]) >> 2);
res[j + 2] = (((unsigned char)table[code[i + 2]]) << 6) | ((unsigned char)table[code[i + 3]]);
}
return res;
}
void readFileData(const char* fileName)
{
FILE* f;
f = fopen(fileName, "rb");
if (!f)
{
printf("No such file: %s\n", fileName);
return;
}
unsigned char buf[16];
int len=0;
int i = 0;
unsigned char meta_key[] = { 0x23,0x31,0x34,0x6C,0x6A,0x6B,0x5F,0x21,0x5C,0x5D,0x26,0x30,0x55,0x3C,0x27,0x28 };
unsigned char core_key[] = { 0x68,0x7A,0x48,0x52,0x41,0x6D,0x73,0x6F,0x35,0x6B,0x49,0x6E,0x62,0x61,0x78,0x57 };
fseek(f, 10, SEEK_CUR); //f从当前位置移动10个字节
fread(buf, 1, 4, f); //读取rc4 key 的长度
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* rc4Key= (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(rc4Key, 1, len, f); //读取rc4数据
//解密rc4密钥
for (i = 0; i < len; i++)
{
rc4Key[i] ^= 0x64;
}
struct AES_ctx ctx;
AES_init_ctx(&ctx, core_key); //使用core_key密钥
int packSize = len / 16; //采用的是AES-ECB加密方式,和Pkcs7padding填充
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &rc4Key[i * 16]);
}
int pad = rc4Key[len - 1]; //获取填充的长度
rc4Key[len - pad] = '\0'; //去除填充的部分,得到RC4密钥
fread(buf, 1, 4, f); //读取Music Info 长度数据
len = ((buf[3] << 8 | buf[2]) << 16) | (buf[1] << 8 | buf[0]);
unsigned char* meta = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(meta, 1, len, f); //读取Music Info数据
//解析Music info信息
for (i = 0; i < len; i++)
{
meta[i] ^= 0x63;
}
int act = 0;
unsigned char* data = base64_decode(&meta[22], len - 22, &act); //base64解码
AES_init_ctx(&ctx, meta_key); //AES解密
packSize = act / 16;
for (i = 0; i < packSize; i++)
{
AES_ECB_decrypt(&ctx, &data[i * 16]);
}
pad = data[act - 1];
data[act - pad] = '\0'; //去除填充部分
unsigned char* newData = data;
#ifdef WIN32
newData = utf8ToGbk(data, strlen(data));
#endif
cJSON* cjson = cJSON_Parse(&newData[6]); //json解析,获取格式和名字等
if (cjson == NULL)
{
printf("cjson parse failed\n");
return;
}
//printf("%s\n", cJSON_Print(cjson)); //输出json
fseek(f, 9, SEEK_CUR); //从当前位置跳过9个字节
fread(buf, 1, 4, f); //读取图片大小
len = (buf[3] << 8 | buf[2]) << 16 | (buf[1] << 8 | buf[0]);
unsigned char* img = (unsigned char*)malloc(sizeof(unsigned char) * len);
fread(img, 1, len, f); //读取图片数据
int offset= 1024 * 1024 * 10; //10MB 音乐数据一般比较大一次读入10MB
int total = 0;
int reSize = offset;
unsigned char* musicData = (unsigned char*)malloc(offset); //10m
while (!feof(f))
{
len = fread(musicData+total, 1, offset, f); //每次读取10M
total += len;
reSize += offset;
musicData=realloc(musicData,reSize); //扩容
}
unsigned char sBox[256] = { 0 }; //s盒
rc4Init(sBox, &rc4Key[17], strlen(&rc4Key[17])); //用rC4密钥进行初始化s盒
rc4PRGA(sBox, musicData, total); //解密
//拼接文件名(artist + music name+format)
char* musicName = cJSON_GetObjectItem(cjson, "musicName")->valuestring;
cJSON* sub = cJSON_GetObjectItem(cjson, "artist");
char*artist=cJSON_GetArrayItem(cJSON_GetArrayItem(sub, 0),0)->valuestring;
char* format = cJSON_GetObjectItem(cjson, "format")->valuestring;
char* saveFileName =(char*)malloc(strlen(musicName) + strlen(artist) + strlen(format)+5);
sprintf(saveFileName, "%s - %s.%s", artist, musicName, format);
FILE* fo=fopen(saveFileName, "wb");
if (fo == NULL)
{
printf("The fileName - '%s' is invalid in this system\n", saveFileName);
}
else
{
fwrite(musicData, 1, total, fo);
fclose(fo);
}
#ifdef WIN32
free(newData);
#endif
free(data);
free(meta);
free(img);
free(musicData);
fclose(f);
}
int main(int argc,char**argv)
{
readFileData("結束バンド - ギターと孤独と蒼い惑星.ncm");
return 0;
}
GitHub项目
星期五女孩

网易云VIP音乐NCM文件转MP3,C语言版本。的更多相关文章
- 下载网易云VIP音乐
有偿帮助.联系方式在个人信息里.
- 网易云音乐ncm格式分析以及ncm与mp3格式转换
目录 NCM格式分析 音频知识简介 两种可能 GitHub项目 格式分析 总体结构 密钥问题 代码分析 main函数 导入模块 dump函数 参考资料 代码完整版 转换工具 ncmdump ncmdu ...
- 网易云音乐 歌词制作软件 BesLyric (最新版本下载)
导读 BesLyric , 一款专门制作 网易云音乐 LRC 滚动歌词的软件! 搜索.下载.制作 歌词更方便! 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦!Beslyric 为你排忧解难! 本文 ...
- 网易云音乐PC客户端加密API逆向解析
1.前言 网上已经有大量的web端接口解析的方法了,但是对客户端的接口解析基本上找不到什么资料,本文主要分析网易云音乐PC客户端的API接口交互方式. 通过内部的代理设置,使用fiddler作为代理工 ...
- Python 爬取网易云歌手的50首热门作品
使用 requests 爬取网易云音乐 Python 代码: import json import os import time from bs4 import BeautifulSoup impor ...
- 网易云音乐ncm加密格式批量转换为flac,mp3
从网易云下载的某些付费歌曲下载下来会是ncm格式.ncm是个啥?就是你下完一首歌被网易云加密成它自己独有的ncm格式,这个ncm不能在其他播放器播放,如果网易云你会员到期了同样也会提示你无法播放(不是 ...
- Python解密网易云音乐缓存文件获取MP3
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:GeneralMonkey Python解密网易云音乐缓存文件获取MP3 ...
- 教你PC端网易云音乐自定义代理,VIP免费听歌!
今天分享一份福利吧,使用网易云音乐自定义代理实现免费听和下载VIP.极高音质.付费的歌曲,这里主要针对PC端电脑版的,需要自己写脚本运行. 01 安装node.js Node.js是一个让 JavaS ...
- 网易云音乐mp3外链、真实地址下载方法
一个网易音乐外链地址长期有效,很简单的方法: 第一步打开网易云音乐,随便找到一首歌,播放,复制网址的ID, 例如:杨钰莹的心雨,网址是: http://music.163.com/#/song?id= ...
- 网易云音乐MP3外链地址
网易云音乐MP3外链地址下载方法很简单的方法: 下载公式:http://music.163.com/song/media/outer/url?id=ID数字.mp3 把上面红色部分(ID数字)换成 ...
随机推荐
- LeetCode - 数组遍历
1. 485. 最大连续 1 的个数 1.1 分析题意 首先:我们求的是连续的1的个数,所以我们不能也没必要对数组进行排序: 其次:只要求求出最大连续1的个数,并不要求具体的区间数目,所以我们只需要用 ...
- 企业MES系统与ERP信息集成要素有哪些?
关于要讲明企业MES系统与ERP信息集成要素有哪些,得先弄清楚他们之间的关系:从工厂的管理来说,ERP在上MES在下,ERP统领企业全局包括MES,为管理层服务,重心在于企业决策,ERP对企业宏观管理 ...
- WPF开发经验-实现自带触控键盘的TextBox
一 引入 项目有个新需求,当点击或触碰TextBox时,基于TextBox的相对位置,弹出一个自定义的Keyboard,如下图所示: 二 KeyboardControl 先实现一个自定义的Keyboa ...
- PTA 1126 Eulerian Path
无向连通图,输出每个顶点的度并判断Eulerian.Semi-Eulerian和Non-Eulerian这3种情况,我们直接记录每个点所连接的点,这样直接得到它的度,然后利用深度优先和visit数组来 ...
- P1600 [NOIP2016 提高组] 天天爱跑步 (树上差分)
对于一条路径,s-t,位于该路径上的观察员能观察到运动员当且仅当以下两种情况成立:(d[ ]表示节点深度) 1.观察员x在s-lca(s,t)上时,满足d[s]=d[x]+w[x]就能观察到,所以我们 ...
- 我的Vue之旅 07 Axios + Golang + Sqlite3 实现简单评论机制
第三期 · 使用 Vue 3.1 + TailWind.CSS + Axios + Golang + Sqlite3 实现简单评论机制 效果图 CommentArea.vue 我们需要借助js的Dat ...
- 2022最新版JDK1.8的安装教程、包含jdk1.8的提取码(亲测可用)
文章目录 1.jdk的安装 1.1.下载(百度网盘jdk1.8提取码永久有效) 1.2.双击提取出来的exe,运行程序.如下图 1.3.进入安装向导 1.4.选择默认(安装所有的组件).同时更改安装路 ...
- 【.NET 6】RabbitMQ延迟消费指南
背景 最近遇到一个比较特殊需求,需要修改一个的RabbitMQ消费者,以实现在消费某种特定的类型消息时,延迟1小时再处理,几个需要注意的点: 延迟是以小时为单位 不是所有消息都延迟消费,只延迟特定类型 ...
- Chrony时间同步服务
概: 网络时间协议(Network Time Protocol,NTP)是用于网络时间同步的协议.提供NTP时间同步服务的软件有很多,这里采用Chrony软件来实现时间同步 chrony 的优势: ...
- DQL-模糊查询
DQL-模糊查询 模糊查询即模糊检索,是指搜索系统自动按照用户输入关键词的同义词进行模糊检索,从而得出较多的检索结果.与之相反的是"精准搜索".模糊检索也可以说是同义词检索,这里的 ...