image frame

IP数据报

     IP 协议控制传输的协议单元称为 IP 数据报(IP Datagram,IP数据报、IP包或IP分组)。IP协议屏蔽了下层各种物理子网的差异,能够向上层提供统一格式的IP数据报。lP数据报采用数据报分组传输的方式,提供的服务是无连接方式。IP数据报的格式能够说明lP协议具有什么功能。IPv4数据报由报头和数据两部分组成,其中,数据是高层需要传输的数据,报头是为了正确传输高层数据而增加的控制信息。报头的前一部分长度固定,共20字节,是所有IP数据报必须具有。在首部固定部分的后面是可选字段,长度可变。

IP报文结构

1、固定部分

  • 版本:占 4 位,指 IP 协议的版本。通信双方使用的 IP 协议版本必须一致。广泛使用的 IP 协议版本号为 4(即 IPv4)。关于 IPv6,还处于草案阶段。
  • 首部长度:占 4 位,可表示的最大十进制数值是 15。请注意,这个字段所表示数的单位是 32 位字长(1 个 32 位字长是 4 字节),因此,当 IP 的首部长度为 1111 时(即十进制的 15),首部长度就达到 60 字节。当 IP 分组的首部长度不是 4 字节的整数倍时,必须利用最后的填充字段加以填充。因此数据部分永远在 4 字节的整数倍开始,这样在实现 IP 协议时较为方便。首部长度限制为 60 字节的缺点是有时可能不够用。但这样做是希望用户尽量减少开销。最常用的首部长度就是 20 字节(即首部长度为 0101),这时不使用任何选项。
  • 区分服务:占 8 位,用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过。1998 年 IETF 把这个字段改名为区分服务DS(Differentiated Services)。只有在使用区分服务时,这个字段才起作用。
  • 总长度:总长度指首部和数据之和的长度,单位为字节。总长度字长为 16 位,因此数据报的最大长度为 2^16-1=65535 字节。在 IP 层下面的每一种数据链路层都有自己的帧格式,其中包括帧格式中的数据字段的最大长度,这称为最大传送单元 MTU(Maximum Transfer Unit)。当一个数据报封装成链路层的帧时,此数据报的总长度(即首部加上数据部分)一定不能超过下面的数据链路层的 MTU 值。
  • 标识:占 16 位。IP 软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。但这个“标识”并不是序号,因为IP是无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的 MTU 而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。
  • 标志:占 3 位,但只有 2 位有意义。标志字段中的最低位记为 MF(More Fragment)。MF=1 即表示后面“还有分片”的数据报。MF=0 表示这已是若干数据报片中的最后一个。标志字段中间的一位记为 DF(Don’t Fragment),意思是“不能分片”。只有当 DF=0 时才允许分片。
  • 片偏移:占 13 位。片偏移指出:较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。片偏移以 8  个字节为偏移单位。这就是说,除了最后一个分片,每个分片的长度一定是 8 字节(64 位)的整数倍。
  • 生存时间:占 8 位,生存时间字段常用的的英文缩写是 TTL(Time To Live),表明是数据报在网络中的寿命。由发出数据报的源点设置这个字段。其目的是防止无法交付的数据报无限制地在因特网中兜圈子,因而白白消耗网络资源。最初的设计是以秒作为 TTL 的单位。每经过一个路由器时,就把 TTL 减去数据报在路由器消耗掉的一段时间。若数据报在路由器消耗的时间小于 1 秒,就把 TTL 值减 1。当 TTL 值为 0 时,就丢弃这个数据报。后来把 TTL 字段的功能改为“跳数限制”(但名称不变)。路由器在转发数据报之前就把 TTL 值减 1。若 TTL 值减少到零,就丢弃这个数据报,不再转发。因此,TTL 的单位不再是秒,而是跳数。TTL 的意义是指明数据报在网络中至多可经过多少个路由器。显然,数据报在网络上经过的路由器的最大数值是 255。若把 TTL 的初始值设为 1,就表示这个数据报只能在本局域网中传送。
  • 协议:占 8 位,协议字段指出此数据报携带的数据是使用何种协议,以便使目的主机的 IP 层知道应将数据部分上交给哪个处理过程。6 指 TCP 协议,17 指的是 UDP 协议。
  • 首部校验和:占16位。这个字段只检验数据报的首部,但不包括数据部分。这是因为数据报每经过一个路由器,路由器都要重新计算一下首部检验和(一些字段,如生存时间、标志、片偏移等都可能发生变化)。不检验数据部分可减少计算的工作量。
  • 源地址和目的地址:32位,IPV4 地址。

2、可变部分

     IP 首部的可变部分就是一个可选字段。选项字段用来支持排错、测量以及安全等措施,内容很丰富。此字段的长度可变,从 1 个字节到 40 个字节不等,取决于所选择的项目。某些选项项目只需要 1 个字节,它只包括 1 个字节的选项代码。但还有些选项需要多个字节,这些选项一个个拼接起来,中间不需要有分隔符,最后用全 0 的填充字段补齐成为 4 字节的整数倍。增加首部的可变部分是为了增加 IP 数据报的功能,但这同时也使得 IP 数据报的首部长度成为可变的。这就增加了每一个路由器处理数据报的开销。实际上这些选项很少被使用。新的 IP 版本 IPv6 就将 IP 数据报的首部长度做成固定的。这些任选项定义如下:

  • 安全和处理限制(用于军事领域)。
  • 记录路径(让每个路由器都记下它的IP地址)。
  • 时间戳(Time Stamp)(让每个路由器都记下IP数据报经过每一个路由器的IP地址和当地时间)。
  • 宽松的源站路由(Loose Source Route)(为数据报指定一系列必须经过的IP地址)。
  • 严格的源站路由(Strict Source Route)(与宽松的源站路由类似,但是要求只能经过指定的这些地址,不能经过其他的地址)。

     这些选项很少被使用,并非所有主机和路由器都支持这些选项。

命令行常用指令

     MySQL 创建数据库并指定字符集:

1
2
drop schema if exists `test`;
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

     Brew 相关的

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
26
27
//苹果电脑安装脚本:
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
//苹果电脑卸载脚本:
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh)"
// 搜索包
brew search mysql
// 安装包
brew install mysql
// 查看包信息,比如目前的版本,依赖,安装后注意事项等
brew info mysql
// 卸载包
brew uninstall wget
// 显示已安装的包
brew list
// 查看brew的帮助
brew –help
// 更新, 这会更新 Homebrew 自己
brew update
// 检查过时(是否有新版本),这会列出所有安装的包里,哪些可以升级
brew outdated
brew outdated mysql
// 升级所有可以升级的软件们
brew upgrade
brew upgrade mysql
// 清理不需要的版本极其安装包缓存
brew cleanup
brew cleanup mysql

     MySQL 源码构建参数(将目录修改为你自己的)

1
2
3
4
5
6
7
8
-DCMAKE_BUILD_TYPE=Debug
-DWITH_BOOST=/Users/setsunayang/Documents/learning/mysql/boost_1_77_0
-DCMAKE_INSTALL_PREFIX=/Users/setsunayang/Documents/learning/mysql/build
-DWITH_SSL=system

./mysqld --basedir=/Users/setsunayang/Documents/learning/mysql/build \
--datadir=/Users/setsunayang/Documents/learning/mysql/build/data \
--initialize-insecure --user=mysql

     Git 相关

1
2
3
4
5
6
git remote rm origin 
git remote add origin [url]
git config --global user.name "杨海波"
git config --global user.email “3546514206@QQ.COM"

git rm -r --cached

     设置主机名称

1
sudo scutil --set HostName SetsunaYang

     Macbook Pro 显示被隐藏的文件或文件夹

1
2
3
4
5
defaults write com.apple.finder AppleShowAllFiles -boolean true;killall Finder
defaults write com.apple.finder AppleShowAllFiles -boolean false;killall Finder

chflags hidden
chflags nohidden

     设置环境变量

1
echo 'export PATH="/opt/homebrew/opt/go@1.20/bin:$PATH"' >> ~/.zshrc

     修改环境变量相关

1
2
3
4
5
6
7
8
# 查看当前 shell 是 zsh 还是 bash
dscl . -read /Users/$USER UserShell
# 如果是 bash
open ~/.bash_profile
source ~/.bash_profile
# 如果是 zsh
open ~/.zshrc
source ~/.zshrc

     IDEA 相关

1
2
3
# 解决 Goland 无法调试的问题。在 bin/goland.vmoptions 增加如下虚拟机参数
-Djava.net.preferIPv4Stack=true
-Djava.net.preferIPv6Addresses=true

     ““prettyZoo.app”已损坏,无法打开。 您应该将它移到废纸篓。” 解决办法

1
sudo xattr -rd com.apple.quarantine '/Applications/prettyZoo.app'

     Jetbrains 全家桶自定义虚拟机参数

1
2
3
4
5
6
7
8
9
-Xms512m
-Xmx8192m

-XX:ErrorFile=/Users/setsunayang/Documents/JetBrains/ERROR/java_error_in_web_%p.log
-XX:HeapDumpPath=/Users/setsunayang/Documents/JetBrains/ERROR/java_error_in_web.hprof

-Dide.managed.by.toolbox=/Applications/JetBrains Toolbox.app/Contents/MacOS/jetbrains-toolbox
-Dtoolbox.notification.token=f2154f48-d8ed-468a-b154-d82403469df5
-Dtoolbox.notification.portFile=/Users/setsunayang/Library/Caches/JetBrains/Toolbox/ports/f96117ec-2396-417f-a3d8-406dde460690.port

     electron 从应用创建到打包

1
2
3
4
5
6
mkdir my-electron-app && cd my-electron-app
npm init
npm install electron --save-dev
npm install --save-dev @electron-forge/cli
npx electron-forge import
npm run make

     “Electron 官方安全模型 + 工程最佳实践”意义上的标准 main.js

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
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
52
53
54
55
56
57
58
59
60
const { app, BrowserWindow, ipcMain } = require('electron/main');
const path = require('path');

let mainWindow = null;

/* ================= 创建窗口 ================= */

function createMainWindow() {
mainWindow = new BrowserWindow({
width: 900,
height: 600,
show: false, // 等页面 ready 再显示,避免白屏
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: false
}
});

// 加载页面(开发 / 生产通用写法)
mainWindow.loadFile('index.html');

// 页面准备好再显示
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});

// 窗口关闭
mainWindow.on('closed', () => {
mainWindow = null;
});
}

/* ================= App 生命周期 ================= */

app.whenReady().then(() => {
createMainWindow();

// macOS:点击 dock 图标重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
});
});

// 非 macOS:所有窗口关闭就退出
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

/* ================= IPC(示例,可按需删) ================= */

// 推荐使用 invoke / handle
ipcMain.handle('app:get-version', () => {
return app.getVersion();
});

回调机制

1、C语言中的回调

     函数指针是指向函数的指针变量。通常我们说的指针变量是指向一个整型、字符型或数 组等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参 数。函数指针变量的声明: typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型 以下实例声明了函数指针变量 p,指向函数 max:

1
2
3
4
5
6
7
8
9
10
11
12
13
int max(int x, int y) { return x > y ? x : y; }

int main(void) {
/* p 是函数指针 */
int (*p)(int, int) = &max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", &a, &b, &c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}

     输出的结果:

1
2
请输入三个数字:1 2 3 
最大的数字是: 3

     函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用 的函数。下面的实例中 populate_array 函数定义了三个参数,其中第三个参数是函数的指 针,通过该函数来设置数组的值。实例中我们定义了回调函数 getNextRandomValue,它返 回一个随机值,它作为一个函数指针传递给 populate_array 函数。populate_array 将调 用 10 次回调函数,并将回调函数的返回值赋值给数组。编译执行,输出结果如下:

1
16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709

2、什么是回调

     软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调 用、回调和异步调用。回调是一种双向调用的模式,也就是说,被调用方在接口被调用时也 会调用对方的接口。

     同步调用:一种阻塞式调用,调用方要等待对方执行完毕才能返回,它是一种单向调 用。

     回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口。

     异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收 到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。 回调和异步调用的关系非常紧密:通常可以使用回调来实现异步消息的注册,通过异步 调用来实现消息的通知。

程序调用的几种方式

     回调机制的实现通常需要实现一个回调函数,回调函数,顾名思义,用于回调的函数。回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机。回调函数包含下面几个特性:

  • 属于工作流的一个部分;

  • 必须按照工作流指定的调用约定来申明(定义);

  • 他的调用时机由工作流决定,回调函数的实现者不能直接调用回调 函数来实现工作流的功能。

基于转加密的握手式国密通信

基于转加密的握手式国密通信

     1、客户端上送 SM4 密钥密文:

  • 客户端根据算法生成两把 SM4 密钥:sm4_key、h_mac_key;

  • 客户端使用内置 SM2 公钥对 sm4_key、h_mac_key 加密得到密文 sm4_cipher_key、h_mac_cipher_key;

  • 调用服务端握手接口将 sm4_cipher_key、h_mac_cipher_key 发送到服务端;

     2、服务端转加密取 SM4 密钥报文

  • 服务端根据算法生成 SM4 密钥 sm4_transfer_key 用于转加密;

  • 服务端使用内置的 SM2 公钥对 sm4_transfer_key 加密得到密文 sm4_transfer_cipher_key;

  • 调用机密机接口上送 sm4_cipher_key、h_mac_cipher_key 密文和 sm4_transfer_cipher_key ;

     3、加密机做转加密

  • 加密机用 SM2 私钥对 sm4 密文解密得到 sm4_key;

  • 机密机用 SM2 私钥对 sm4_transfer_cipher_key 解密得到 sm4_transfer_key;

  • 加密机用 sm4_transfer 对 sm4_key 加密 sm4_key_cipher_transfer 并返回;

  • 加密机用 用同样的方式得到 h_mac_key_cipher_transfer 并返回;

     4、服务端获取 SM4 密钥明文与 h_mac_key 明文并缓存作为后续报文交互

     5、报文通讯加解密

  • 客户端使用 sm4_key 对交易明文加密得到交易密文;

  • 客户端使用 h_mac_key 对交易明文计算 hmac ;

  • 客户端上送通讯加密报文(交易密文+hmac);

  • 服务端接收到通讯加密报文后使用 sm4_key 进行解密得到交易明文,使用 h_mac_key 对交易明文计算 hmac 并与上送的 hmac 进行对比校验报文的完整性。

基于密码机转加密的握手式国密通信第二版

     由于密码机接口的设计问题,上述 3 步骤中,需要调用密码机两次,分别发送 sm4_key 和 h_mac_key 给服务端。修改为第二版之后,h_mac_key 用 sm4_key 进行 SM4 加密,这样经过服务端转加密流程解出 sm4_key 之后,直接解密 h_mac_cipher_key 得到 h_mac_key。

  • Copyrights © 2017 - 2026 杨海波
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信