编程我只用CPP 发布的文章

一、关于滑动窗口协议

在TCP协议中,所有的SEQ包发送出去都必须要受到对方的ACK才认为是发送成功,如果长时间没有收到ACK回复确认,发送方需要重新发送该包。

而如果发送方每次都是发送一个包,然后等到接收方回复ACK了再发送下一个包,那么数据包的传输效率就相当低了。滑动窗口协议的作用就是为了解决这个问题。

在滑动窗口协议中,发送方可以同时发送多个数据包并可以不用等待接收方确认,这样就大大增加了网络的推图两。唯一的限制是:接收方未确认的数据包不得超过双方约定的窗口个数。

TCP包头中有一个两字节的字段表示滑动窗口的大小,因此最大的滑动窗口大小为65535,这个大小是由接收方提出的,以接收方的大小为准。

最大窗口65536并不是绝对的,TCP选项中有提供一个窗口放大的选项可以对这个数值缩放。

例如以下数据,将发送的字节从1至11进行标号,接收方提出的窗口大小为6,此时已发送的1/2/3字节数据被接收方确认,当前窗口覆盖了从第4字节到第9字节的区域。4/5/6字节是已经发送但是还未收到确认,此时发送方还可以发送的数据域只剩下三个,即7/8/9三个数据。当7/8/9发送完成后,发送方无法再继续发送数据。只有接收方再确认序号4的数据后,发送方才能继续发送数据10。此时可用的窗口将会向右延伸到10,同时左边已发送并确认的数据也会向右延伸到4。

这个过程看起来就像是一个窗口不停向右滑动,因此该协议被称为滑动窗口协议。

822287-20180227212818806-1798395637.png

滑动窗口涉及到以下几个概念:

  1. 窗口合拢:当窗口左边界向右靠近时,这种现象发生在数据被发送方确认时。
  2. 窗口张开:窗口的右边界向右移动的时候,这种现象发生在接收端处理的数据的时候。
  3. 窗口收缩:窗口右边界向左移动时,这种现象不常发生。
注意:窗口大小是由接收方通告的,通过采取慢启动和拥塞避免算法等机制来使带宽和性能取得最佳。

二、坚持定时器

2.1 坚持定时器的引入

思考以下场景:接收方的接收缓冲区已满,因此接收方发送了一个窗口大小为0的数据到发送方,此时发送方停止发送数据。经过一段时间后,接收方有空闲的缓冲区可以接收数据,此时给发送方发了一个窗口大小不为0的包,但是恰巧这个包在路上出现了意外没有到达发送方。此时发送方还在一直等待接收方发送不为0的窗口过来,但是接收方又在等待发送方继续发送数据。两者相当于发生了死锁。

2.2 工作原理

为了解决上面这个问题,TCP引入了坚持定时器,该定时器功能为:TCP连接的一方收到对方窗口为0的通知时,启动该定时器,若定时器持续时间到达后还没有收到对方窗口大小不为0的通知,主动发送一个零窗口探测包(仅携带1字节数据),对方则需要在这个包中回复当前窗口的大小。

三、关于window scale选项

window scale选项是提供给滑动窗口协议用于对窗口大小放大使用,在SYN包或者SYN,ACK包中作为选项字段,表示滑动窗口的实际大小要根据这个值来进行缩放(SYN包本身的窗口不会缩放)。

发送方和接收方的滑动窗口缩放因子大小可以不一样,两者互不干扰,但实际上窗口大小还是以接收方为准。使用wireshark抓包很容易就能看到这个缩放选项,以下是一个数据包示例。

这个包是客户端发起的三次握手数据包:

image.png

客户端设置了窗口缩放大小为256,因此后面实际计算的时候窗口大小要乘以这个缩放大小1025(使用wireshark很容日就能看到),乘以256后得到实际的窗口大小为262400

image0855a2b1457c0f0d.png

Calculated window size: wireshark根据缩放因子计算出来的实际窗口大小。

Window size scaling factor: 窗口缩放因子。

这两个参数都是wireshark自动生成的,并不是携带在TCP包中的字段。

本文内容来源于知乎问答:Cache 和 Buffer 都是缓存,主要区别是什么?,根据各回答内容整理得到。

首先整理下两者的概念:

​ cache是缓存,buffer是缓冲。两者从名字来看十分相近,功能并不一样,不仔细琢磨很容易把两者混为一谈。

区别:

  1. 缓存的主要目的是为了提速,系统把部分磁盘的内存放到缓存中来提高运行速度。关键的一点是,如果缓存丢失,并不影响磁盘数据读取,只是读写速度慢一些。
  2. 缓冲的作用恰如其名——起一个缓冲的作用。例如写文件的时候,每次写一个字节,如果每次都把这1个字节写到磁盘,严重影响运行效率。而缓冲的作用就是把这些1字节的数据存起来,到一定的数量之后统一写到磁盘。同时,和缓存不同的是,缓冲区中的数据如果丢失了,数据就会永久丢失(例如:linux系统中dmesg的实现就是一个环形缓冲区,当日志过多时,后来的日志就会刷掉原来的,导致日志信息显示不全)。

一、简介

go module公共代理仓库,代理并缓存go模块。你可以利用该代理来避免DNS污染导致的模块拉取缓慢或失败的问题,加速你的构建。

简单来说就是国内访问被墙,go get无法在线获取到仓库,代理仓库就是帮我们解决这个问题的。

二、使用方法

  1. 使用go1.11以上版本并开启go module机制
  2. 导出GOPROXY环境变量
export GOPROXY=https://xxxx.xx

三、国内优质代理仓库

3.1 七牛

export GOPROXY=https://goproxy.cn

3.2 阿里云

export GOPROXY=https://mirrors.aliyun.com/goproxy/

一、下载安装

mariadb是属于mysql的一个分支,是其创始人在mysql被卖给oracle之后重新分出来的,maria取自于他女儿的名字。mariadb完全兼容于mysql,在很多新版本的linux系统中,mysql都已经被替换成了mariadb。

mariadb的官网:mariadb官网,下载地址:下载地址。最新稳定版本的下载直链为:

wget https://downloads.mariadb.com/MariaDB/mariadb-10.5.0/bintar-linux-systemd-x86_64/mariadb-10.5.0-linux-systemd-x86_64.tar.gz

首先把安装包下载到本地,然后解压到/usr/local目录:

tar -zxvf mariadb-10.5.0-linux-systemd-x86_64.tar.gz  -C /usr/local/
ln -s /usr/local/mariadb-10.5.0-linux-systemd-x86_64/ /usr/local/mysql

初始化数据库,设定数据存储目录为/appdata/mysql,启动用户为mysql

# 创建mysql用户
useradd -s /sbin/nologin -M mysql
# 创建数据库文件夹
mkdir /appdata/mysql -p
# 初始化数据库
/usr/local/mysql/scripts/mysql_install_db \
    --basedir=/usr/local/mysql \
    --datadir=/appdata/mysql \
    --user=mysql

初始化数据库的过程中如果报错:

> Installing MariaDB/MySQL system tables in '/xxx/mariadb' ...
/usr/local/mariadb/bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory
 
Installation of system tables failed!  Examine the logs in
/udata/mariadb for more information.
 
...

说明系统缺少组件库libaio,需要安装手动安装:

# centos
yum install libaio libaio-devel
# ubuntu
apt install libaio1

执行成功后输出:

Installing MariaDB/MySQL system tables in '/appdata/mysql' ...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

...

到这里数据库就已经安装完成了,接下来要做的就是配置。

二、配置

修改/etc/mysql/my.cnf,设置pid/socket/log等文件的路径,把它们统一存到/appdata/mysql/run/下:

[mysqld]
datadir=/appdata/mysql
socket=/appdata/mysql/run/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mariadb according to the
# instructions in http://fedoraproject.org/wiki/Systemd

[mysqld_safe]
log-error=/appdata/mysql/run/mysql.log
pid-file=/appdata/mysql/run/mysql.pid

[mysql]
socket=/appdata/mysql/run/mysql.sock

[mysqladmin]
socket=/appdata/mysql/run/mysql.sock

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d

注意:

  1. /appdata/mysql/run目录要提前创建
  2. 如果修改了socket的路径,还要修改[mysql]和[mysqladmin]段的socket路径,要和[mysqld]中的socket路径一致

设置路径权限:

chown mysql.mysql -R /usr/local/mysql /appdata/mysql

添加mysql命令到系统路径,修改/etc/profile文件:

MYSQL_HOME=/usr/local/mysql
export PATH=$PATH:$MYSQL_HOME/bin

修改后source /etc/profile生效。

三、添加系统服务

3.1 service系统服务

对于使用service命令启动的服务,复制mysql主目录下的support/mysql.server文件到/etc/init.d/

cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
chmod +x /etc/init.d/mysqld

然后修改文件中的配置:

basedir=/usr/local/mysql # 安装目录
datadir=/appdata/mysql # 数据目录
mysqld_pid_file_path=/appdata/mysql/run/mysql.pid # pid文件目录
注意:配置要和上面my.cnf中的配置一一对应

启动:service mysqld start

添加到开机启动:

chkconfig --add mysqld
chkconfig mysqld on

3.2 systemd系统服务

systemd服务的文件在安装路径/support-files/systemd/mariadb.service

cp support-files/systemd/mariadb.service /etc/systemd/system/mysqld.service

复制完后执行systemctl start mysqld启动服务,然后设置开机启动:

systemctl enable mysqld

四、设置root用户密码

系统服务起来后,可以使用mysqladmin初始化root用户的密码:

mysqladmin -u root password '123456'

如果出现:

image.png

说明没有没有权限登录,需要通过安全模式启动mysql来修改root密码,在my.cnf中添加以下内容:

[mysqld]
skip-grant-tables
skip-networking

然后重启服务,使用root身份登录(不用密码),执行以下命令修改密码:

use mysql;
# 刷新权限
flush privileges;
# 设置密码
set password for 'root'@'localhost' = password('123456');
# 刷新权限
flush privileges;

如果执行命令的时候出现报错:

ERROR 1290 (HY000): The MariaDB server is running with the --skip-grant-tables option so it cannot execute this statement

说明安全模式下的权限还没有更新,要先刷新一下权限才行:

flush privileges;

修改完成后去掉my.cnf中添加的参数,重启服务,使用上面设置的密码登陆就可以了:

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 10.4.8-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> 

一、刷breed

breed是一个专门用于刷固件的后台,支持对系统当前的数据进行操作。breed是刷固件最重要的一步,K2系统默认的版本已经做了限制,不能直接刷上breed,需要在网上找教程刷。

我手里的这个路由器原来刷过,因此这里略过这一步。

进去breed的方法

  1. 拔掉路由器电源,按住重置键,然后插上电源线,通电后10秒钟左右松开重置键。
  2. PC通过网线直连路由器,访问http://192.168.1.1

breed首页:

image72bde19e2f98c417.png

二、下载固件

相关资源地址:

下载固件在百度网盘下载,K2的固件名字是:RT-AC54U-GPIO-1-PSG1218-64M,不要下错了。

三、刷入固件

进入breed,固件更新页面选择下载的固件,然后点击上传(注意勾上自动重启选项):

imagedfbeb6a99ac68291.png

确认固件信息,主要是对比MD5是否正确,原始的MD5在百度网盘中有下载。确认无误后点击更新开始刷固件:

imagef959b6c3faaa4d55.png

刷固件需要1-2分钟时间,耐心等待,不要断开电源:

image9da269ad0e96538f.png

刷入完成后,设备自动重启,等待一两分钟系统启动:

imagec4088814141c788a.png

Padavan固件的首页是192.168.123.1,默认密码是admin/admin,启动后在浏览器直接访问:

imagec25d77c20d6085ba.png

大功告成!

四、其他

一、缓存

缓存的作用一般是为了减轻数据库压力设计的,因为数据都是读写磁盘,当并发量大的时候,磁盘IO可能跟不上并发量。而缓存一般设计都是放在内存中的,最常见的例如redis和memcached,都是把数据都缓存在内存中。读写内存的速度比磁盘快很多,因此把常用的数据都放到内存中做缓存可以给数据库减轻很大的压力。

一个正常的缓存处理流程为:

  1. 先判断缓存中是否存在数据,如果缓存存在数据,直接读缓存中的数据。
  2. 如果缓存不存在,从数据库读数据。
  3. 如果数据库中存在数据,返回数据库中的数据并写到缓存。
  4. 如果数据库中也不存在数据,直接返回。

image2db4e44cb2632283.png

而作为缓存系统,需要考虑的几个问题是:缓存穿透、缓存击穿和缓存雪崩。

二、缓存穿透

缓存穿透:查询一个数据库不存在的数据,因为数据都不存在,所以缓存中也不存在,请求会直接去数据库中查询,这种情况就产生了缓存穿透。

例如对一个只含非负整数的表中查询id = -1的记录,缓存中不存在数据,所以每次都会请求数据库。当有攻击者恶意使用这种方式频繁请求数据时,会给后端数据库造成极大的压力。

解决方案:

  1. 增加数据校验,直接过滤掉明显不存在的数据。这里一般采用布隆过滤器,预先生成所有可能的数据的bitmap,请求数据库前先在过滤器中查找,过滤掉不可能的数据。
  2. 对不存在的值也设置缓存,在数据库中查询到不存在的数据之后在缓存中添加一个key=null的值。

三、缓存击穿

缓存击穿:某个key缓存在某一时刻失效,由于请求量特别大,导致这一时刻的所有请求同时都直接请求数据库,给数据库带来巨大压力。

解决方案:使用互斥锁或者其它方法锁住这个key(单例模式),当有请求已经在查询这个key时,等待请求,这个请求完成后数据写入缓存再从缓存中读取数据。

四、缓存雪崩

缓存雪崩:给一些键值设置了过期时间,同一时刻这些数据同时失效,请求直接到达数据库。

解决方案:

  1. 和缓存击穿一样,使请求单例执行。
  2. 不要给缓存的过期时间都设置成一样的,设置超时事件的时候加上一个随机时间作为时间。

缓存雪崩和击穿其实一样,缓存击穿时缓存雪崩的一个特例。

两者的区别:缓存雪崩是指多个缓存数据同时失效,而缓存击穿特指某一个数据失效。

一、证书相关

查看证书详细信息:

openssl x509 -in cert.pem -noout -text

查看证书所有者:

> openssl x509 -in cert.pem -noout -subject
subject= /CN=www.dyxmq.cn

打印指纹信息:

> openssl x509 -in cert.pem -noout -fingerprint
SHA1 Fingerprint=7B:29:CE:09:28:4B:38:82:DF:53:14:B0:15:00:03:66:2B:C0:90:9E

转换证书格式:

> openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER

二、测试指令

测试openssl库功能,已ssl3协议连接baidu.com的443端口:

openssl s_client -connect baidu.com:443 -ssl3

一、问题现象

磁盘下有个大文件占满了空间,删除后发现文件不见了,但是磁盘空间并没有释放。

问题原因

文件还被其他进程占用,调用rm后实际上还并没有真正删除,只有当文件解除占用后(引用计数变为0)才会释放掉这部分空间。

解决方案

使用lsof命令查看文件还在被哪个进程占用的,关闭进程接触解除占用:

lsof | grep delete

过滤delete表示显示出文件已经被删除了,但是实际还被进程占有着没释放的文件。

二、场景模拟

通过以下代码模拟出进程占用文件场景,代码的作用是打开文件,然后睡眠100秒,最后结束退出

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int fd;

    if (argc < 2) {
        printf("Usage: %s file\n", argv[0]);
        return 0;
    }

    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open error");
        return -1;
    }

    sleep(100);
    close(fd);

    return 0;
}

编译,并在当前目录生成一个1G左右的文件用作测试:

> gcc test.c # 编译程序
> df -h /data # 当前data分区使用了12G,剩余7.5G
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb1        20G   12G  7.5G  60% /data
> dd if=/dev/zero of=testfile bs=1M count=1024 # 生成一个1G左右的文件
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 4.7793 s, 225 MB/s
> df -h /data # 目前磁盘使用了13G,剩余6.5G
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb1        20G   13G  6.5G  66% /data

然后执行程序,程序会卡住并睡眠100S。删除测试文件,查看磁盘空间发现没有被归还:

使用lsof命令查看文件还在被占用,kill掉之后文件解除占用,空间被归还: