前言

网易云的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); }
  1. AES采用的是AES-ECB模式,pack7padding填充方式。即16个字节为一组,如果不够16个字节,那就缺几个字节就填充几个字节,每个字节的值都是缺少的字节数。所以获取最后一个字节的值就知道要填充了几个字节。
  2. RC4解密那里,不是按RC4的来的,虽说叫RC4,但只有生成S盒那里是一样的,其它的不是按RC4算法来的。
  3. 有些解析出来音乐的名字,系统是不支持的,比如带'/'的,在创建新文件写入时会失败。
  4. 以"結束バンド - ギターと孤独と蒼い惑星.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项目

NcmToMp3

星期五女孩

网易云VIP音乐NCM文件转MP3,C语言版本。的更多相关文章

  1. 下载网易云VIP音乐

    有偿帮助.联系方式在个人信息里.

  2. 网易云音乐ncm格式分析以及ncm与mp3格式转换

    目录 NCM格式分析 音频知识简介 两种可能 GitHub项目 格式分析 总体结构 密钥问题 代码分析 main函数 导入模块 dump函数 参考资料 代码完整版 转换工具 ncmdump ncmdu ...

  3. 网易云音乐 歌词制作软件 BesLyric (最新版本下载)

    导读 BesLyric , 一款专门制作 网易云音乐 LRC 滚动歌词的软件! 搜索.下载.制作 歌词更方便! 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦!Beslyric 为你排忧解难! 本文 ...

  4. 网易云音乐PC客户端加密API逆向解析

    1.前言 网上已经有大量的web端接口解析的方法了,但是对客户端的接口解析基本上找不到什么资料,本文主要分析网易云音乐PC客户端的API接口交互方式. 通过内部的代理设置,使用fiddler作为代理工 ...

  5. Python 爬取网易云歌手的50首热门作品

    使用 requests 爬取网易云音乐 Python 代码: import json import os import time from bs4 import BeautifulSoup impor ...

  6. 网易云音乐ncm加密格式批量转换为flac,mp3

    从网易云下载的某些付费歌曲下载下来会是ncm格式.ncm是个啥?就是你下完一首歌被网易云加密成它自己独有的ncm格式,这个ncm不能在其他播放器播放,如果网易云你会员到期了同样也会提示你无法播放(不是 ...

  7. Python解密网易云音乐缓存文件获取MP3

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:GeneralMonkey Python解密网易云音乐缓存文件获取MP3 ...

  8. 教你PC端网易云音乐自定义代理,VIP免费听歌!

    今天分享一份福利吧,使用网易云音乐自定义代理实现免费听和下载VIP.极高音质.付费的歌曲,这里主要针对PC端电脑版的,需要自己写脚本运行. 01 安装node.js Node.js是一个让 JavaS ...

  9. 网易云音乐mp3外链、真实地址下载方法

    一个网易音乐外链地址长期有效,很简单的方法: 第一步打开网易云音乐,随便找到一首歌,播放,复制网址的ID, 例如:杨钰莹的心雨,网址是: http://music.163.com/#/song?id= ...

  10. 网易云音乐MP3外链地址

      网易云音乐MP3外链地址下载方法很简单的方法: 下载公式:http://music.163.com/song/media/outer/url?id=ID数字.mp3 把上面红色部分(ID数字)换成 ...

随机推荐

  1. Jupyter Notebook单元格加宽的方法3种

    Jupyter Notebook的代码单元格比较窄,在我的屏幕上只占了一半都不到,网络搜索下,共找到3种加宽的方法,总结一下. (一)只改变当前Jupyter笔记本的单元格宽度 在Jupyter No ...

  2. C#并发编程-4 同步

    如果程序用到了并发技术,那就要特别留意这种情况:一段代码需要修改数据,同时其他代码需要访问同一个数据. 这种情况就需要考虑同步地访问数据. 如果下面三个条件都满足,就必须用同步来保护共享的数据. 多段 ...

  3. C++面向对象编程之C++11语法糖

    1.variadic template(模板参数可变化) template... type就是说有可变模板参数,作为参数使用时类型就是 类型后 + ... ,例如type... / type& ...

  4. P3008 [USACO11JAN]Roads and Planes G (最短路+拓扑排序)

    该最短路可不同于平时简单的最短路模板. 这道题一看就知道用SPFA,但是众所周知,USACO要卡spfa,所以要用更快的算法. 单向边不构成环,双向边都是非负的,所以可以将图分成若干个连通块(内部只有 ...

  5. HDU3507 print article (斜率优化DP)

    状态表示:dp[i]表示打印前i个单词的最小成本:s[i]维护前缀和. 状态转移:dp[i]=min(dp[j]+(s[i]-s[j])2)+m , 0<=j<i. 换成y=kx+b的形式 ...

  6. 两将军问题和TCP三次握手

    两将军问题,又被称为两将军悖论.两军问题, 是一个经典的计算机思想实验. 首先, 为避免混淆,我们需要认识到两将军问题虽然与拜占庭将军问题相关,但两者不是一个东西.拜占庭将军问题是一个更通用的两将军问 ...

  7. 后端框架学习-----mybatis(4)

    文章目录 4.解决属性名和字段名不一致的问题 4.解决属性名和字段名不一致的问题 1.问题.数据库字段名和属性名不一致,导致查出的数据部分为空 2.resultMap(用于解决数据库表中的字段和属性) ...

  8. vue+elementUi实现将数字转化为 对应的字符串内容

    文章目录 1.实现的效果 2.template 3.方法中的数据 4.实际运用 1.实现的效果 数据库状态字段 vue前端效果 2.template prop是你的数据库的字段名称 <el-ta ...

  9. 消息队列之RabbitMQ介绍与运用

    RabbitMQ 说明 本章,我们主要从RabbitMQ简介.RabbitMQ安装.RabbitMQ常用命令.RabbitMQ架构模式.RabbitMQ使用.Quick.RabbitMQPlus的使用 ...

  10. 跟我学Python图像处理丨图像特效处理:毛玻璃、浮雕和油漆特效

    摘要:本文讲解常见的图像特效处理,从而让读者实现各种各样的图像特殊效果,并通过Python和OpenCV实现. 本文分享自华为云社区<[Python图像处理] 二十四.图像特效处理之毛玻璃.浮雕 ...