修改配置文件
vim /etc/profile.d/git-prompt.sh
修改为:
if test -f /etc/profile.d/git-sdk.shthen TITLEPREFIX=SDK-${MSYSTEM#MINGW}else TITLEPREFIX=$MSYSTEMfiif test -f ~/.config/git/git-prompt.shthen . ~/.config/git/git-prompt.shelse PS1='\[\033]0;Bash\007\]' # 窗口标题 PS1="$PS1"'\n' # 换行 PS1="$PS1"'\[\033[32;1m\]' # 高亮绿色 PS1="$PS1"'➜ ' # unicode 字符,右箭头 PS1="$PS1"'\[\033[33;1m\]' # 高亮黄色 PS1="$PS1"'\W' # 当前目录 if test -z "$WINELOADERNOEXEC" then GIT_EXEC_PATH="$(git --exec-path 2>/dev/null)" COMPLETION_PATH="${GIT_EXEC_PATH%/libexec/git-core}" COMPLETION_PATH="${COMPLETION_PATH%/lib/git-core}" COMPLETION_PATH="$COMPLETION_PATH/share/git/completion" if test -f "$COMPLETION_PATH/git-prompt.sh" then . "$COMPLETION_PATH/git-completion.bash" . "$COMPLETION_PATH/git-prompt.sh" PS1="$PS1"'\[\033[31m\]' # 红色 PS1="$PS1"'`__git_ps1`' # git 插件 fi fi PS1="$PS1"'\[\033[36m\] ' # 青色fiMSYS2_PS1="$PS1"
]]>互联网的核心部分起特殊作用的是路由器. 路由器实现分组交换的关键构件, 其任务是转发收到的分组
电路交换的三个步骤:
分组交换技术的主要特点:
分组交换的主要有点
作用范围:
广域网(WAN)
城域网(MAN), 覆盖范围5-50km, 采用以太网技术
局域网(LAN), 范围1km
个人区域网(PAN)和无线个人区域网(WPAN)
按照使用中分类
公用网
专用网
计算机网络的性能
计算机网络的性能指标
速率: 指数据的传送速率, 也称为数据率或比特率
带宽: 在频域中是信号具有的频带宽度, 在时域中网络通道内传送数据的能力
吞吐量: 单位时间内通过某个网络的实际数据量
时延是指数据(一个报文或者分组)从网络(链路)的一端传送带另一端所需的时间
发送时延: 是主机或路由器发送数据帧所需要的时间, 也叫传输时延, 发送时延=(数据帧长度)/(发送速率)
传播时延: 是电磁波在信道中传播一定的距离需要花费的时间, 传播时延=信道长度/电磁波在信道上的传播速率
电磁波在自由空间的传播速率是光速310^5Km/s, 在铜线电缆中的传播速率为2.310^5Km/s, 在光纤中的传播速率为2*10^5Km/s
发送时延是只在网络适配器内部转发分组的时延, 传播时延在链路中的发送时延
处理时延: 是主机或者路由器收到分组是花费一定的时间进行处理(分析首部等)
排队时延: 是分组经过网络传输是经过路由器后要先在输入队列中排队等待处理的时延
时延带宽积: 是传播时延和带宽相乘, 时延带宽积是指链路的容纳量
往返时间RTT, 发送时间=数据长度/发送速率, 有效数据率=数据长度/(发送时间+RTT)
利用率有信道利用率和网络利用率, 网络当前时延=网络空闲时的时延/(1 - 利用率)
计算机网络
协议与划分层次
网络协议是为进行网络中的数据交换而建立规则,标准或约定
语法, 即数据与控制信息的结构或格式
语义, 即需要发出何种控制信息, 完成何种动作以及做出何种响应
同步, 即事件实现顺序的详细说明
网络层次
各层是独立的. 不需要知道他的下一层是如何实现的, 只需要通过层间的接口提供服务
灵活性好, 当某一层发生变化时, 只要层间接口关系不便, 各层均不受影响
结构上可分割开, 各层采用最适合的技术来实现
易于实现和维护, 能够实现和调试一个庞大而又复杂的系统变得易于处理
能促进标准化工作
通常各层需要完成的功能
差错控制
流量控制
分段和重装
复用和分用
连接建立和释放
具有五层协议的体系结构
OSI参考模型具有七层体系结构, 物理层, 数据链路层, 数据链路层, 网路层, 传输层, 会话层, 表示层, 应用层OSI参考模型
TCP/IP参考模型, 链路层, 网络层, 传输层, 应用层
五层参考模型
应用层, 通过应用进程间的交互来完成特定网络应用, 交互数据单元称为报文
运输层, 负责向两台主机中的进程之间的通信提供数据传输服务, 并不指定某个特定层网络应用, 多种应用都可以使用同一个运输层服务, 因此有复用和分用的功能, 复用是指多个应用同时使用运输层服务, 分用是运输层收到的信息分别交付到应用层中的相应进程
transmission Control protocol协议—-提供面向连接的, 可靠的数据传输服务, 数据传输单位是报文段
User Datagram protocol协议—-提供无连接的,尽力的数据传输服务, 数据传输单位是用户数据报
网络层, 为分组交换网上的不同主机提供通信服务, 将运输层产生的报文段或用户数据报封装成分组或包进行传送, 分组也称IP数据报, 另一个任务是将源主机运输层传下来的分组通过网络中的路由器找到目的主机
数据链路层, 数据链路层将网络层交下来的网络IP数据报组装成帧, 在相邻结点链路上传送帧, 每一帧包括数据和必要的控制信息(如同步信息等)
物理层, 光纤等
人们经常提到的TCP/IP并不一定是指这两个具体协议, 往往是整个TCP/IP协议族
实体, 协议, 服务和服务访问点
协议是控制两个对等实体(或多个)进行通信的规则的集合
在协议的控制下, 两个对等实体间的通信是的本层能够向上一层提供服务, 要实现本层协议还需要实现下面一层所提供的服务
协议是水平的, 服务是垂直的, 上层使用下层提供的服务必须通过与下一层交换命令(服务原语)
TCP/IP协议可以为各式各样的应用提供服务, 同时TCP/IP协议也允许IP协议在各式各样的网络构成的互联网上运行
物理层
物理层在计算机内部多采用并行传输方式, 但数据在通信线路上采用串行传输
一个数据通信系统可以划分为源系统, 传输系统, 目的系统
源系统
源点, 信号源
发送器, 通常源点生成的数字比特流要通过发送器编码后才能在传输系统进行传输, 典型的发送器是调制器, 很多计算机使用内置的调制解调器
接收器, 解调器
终点, 屏幕等
信号
模拟信号, 或连续信号, 调制解调器到电话局之间的用户线
数字信号, 或离散信号, 计算机到调制解调器
信道基本概念
单向通信, 单工通信
双向交替通信, 半双工通信
双向同时通信, 全双工通信
来自信源的信号称为基带信号, 计算机输出的代表各种文字或图像文件的数据信号都属于基带信号, 基带信号中往往包含较多的低频成分, 甚至有直流成分, 因此需要对基带信号进行调制解调
调制可分为两类, 一种是仅对基带信号的波形进行变换, 使他能与信道特性相适应, 变换后的信号还是基带信号, 这类调制称为基带调制, 另一类调制需要使用载波进行调制, 把基带信号的频率范围搬移到较高的频段, bin转换为模拟信号, 进过载波的信号称为带通信号调制
常用编码方式
不归零值, 正电平为1, 负电平为0
归零制, 正脉冲为代表1, 负脉冲为0
曼彻斯特编码, 周期内向上跳的代表0, 向下跳代表1
差分曼彻斯特编码, 在每一位中心处始终有跳变, 位开始边界有跳变代表0,而位开始边界没有跳变代表1。
基本的带通调制方法
调幅(AM) 载波的振幅随基带数字信号而变化
调频(FM) 载波的频率随基带数字信号而变化
调相(PM) 载波的初始相位随基带数字信号而变化
信噪比 = 10 log 10(s/n)
香农公式: 信道的极限信息传输速率是 C = W log2(1 + s/N)(bit/s), w是信道的带宽(以hz为单位), S为信道内所传信号的平均功率, N为信道内部的高斯噪声功率
信道的带宽或信道中的信噪比越大, 信息的极限传输速率就越高, 用编码的方法让每一个码元携带更多比特的信息量
物理层下面的传输媒体
导引型媒体
双绞线
同轴电缆
光缆, 传输损耗小, 中继距离长, 抗干扰强, 保密性好, 体积轻
信道复用技术
频分复用, 时分复用和统计时分复用
频分复用是所有用户在同样的时间占用不同的带宽(频率带宽)资源
时分复用的所有用户在不同的时间占用相同的频带宽度, 时分复用则有利于数字信号的传输
波分复用是光的频分复用
码分复用: 每一个用户可以在同样的时间使用同样的频带进行通信, 由于各用户使用不同码型, 因此各用户之间不会造成干扰, 其频谱类似与白噪声, 不易被发现
宽带接入技术
ADSL技术, 非对称数字用户线, 是用数字技术对现有的模拟电话用户线进行改造, 是他能承载宽带数字业务
光纤同轴混合网(HFC网)
FTTx技术, 光纤到户技术
数据链路层
数据链路层属于计算机网络的低层
点对点通道, 这种通道使用一对一的点对点通信方式
广播信道, 这种信道使用一对多的广播通信方式
使用点对点信道的数据链路层
数据链路和帧
链路就是一个结点到相邻结点的一段物理线路(有线或无线)
数据链路是另一个概念, 在一条线路上传送数据时, 除了一条必须的物理链路外, 还需要包括必要的通信协议来控制这些数据的传输才构成了数据链路, 现在最常用的方法是使用网络适配器来实现这些协议, 一般的适配器都包括了数据链路层和物理层这两层功能
帧
数据链路层将网络层交下来的数据构成帧发送到链路上, 以及把收到的帧中的数据取出在上交到网络层, 网络层协议数据单元是IP数据报
三个基本问题
封装成帧是在一段数据的前后加上首部和尾部, 首部和尾部的一个重要作用是帧定界, 为了提高数据传输速率, 帧的数据部分长度应尽可能的大于首部和尾部的长度, 每一种链路层协议所能传送的帧的数据部分长度上线为最大传送单元MTU
透明传输, 由于帧的开始就结束的标记使用专门的控制字符, 因此所传输任何8比特组合总一定不允许和用作帧定界的控制字符比特编码相同, 在数据链路层透明传输表示无论什么样的比特组合的数据都能够原样无差错的通过数据链路层, 因此, 对于所传送的数据来说, 这些数据就看不见数据链路层有什么妨碍数据传输的东西(转义字符)
差错检测, 传输错误的比特总数站所传输比特总数的比率称为误码率, 信噪比越高误码率越小(循环冗余校验)
循环冗余检验, 帧检验序列FCS是帧检验序列, 一般使用冗余检验法是模二运算, 异或运算, 发送的FCS是M + FCS(余数)
如果帧无差错, 将每一帧都除以除数, 余数为0
凡是被接收端数据链路层接受的帧均无差错
数据链路层没有提供可靠传输服务
在CRC上加上了帧编号, 确认和重传机制, 对于质量较好的有线传输线路, 不适用确认和重传机制
点对点协议PPP
PPP协议满足的需求
简单, 只进行封装校验
封装成帧
透明性
多种网络协议
多种类型链路
差错检测
检测连接状态
最大传送单元
网络层地址协商
数据压缩协商
只支持点对点链路通信, 只支持全双工链路
协议的组成
将IP数据报封装到串行链路的方法, PPP既支持异步链路(无奇偶校验的8比特数据), 也支持面向比特的同步链路
一个用来建立配置和测试数据链路连接的链路控制协议LCP
一套网络控制协议NCP, 其中每一个协议支持不同的网络层协议
PPP协议帧格式
PPP协议帧格式
PPP协议帧格式
首部的第一个字段和尾部第二字段都是标志字段, 字段A和C没有携带帧信息,第四个字段是协议字段, 当协议字段是0x0021时, ppp帧信息字段是IP数据报, 若为0xC021是ppp链路协议的LCP数据, 0x8021是控制数据
字节填充
将信息字段中的每一个0x7E 字节转变为2字节序列(0x7D, 0x5E)
若信息中出现了一个0x7D字节, 转变为(0x7D,0x5D)
若信息字段中出现小于0x20字符, 则在字符前加入一个0x7D字节, 同时改变编码
零比特填充, 在使用SONET/SDH链路使用同步传输时, 只要发现连续5个一,则立即加入一个0
使用广播信道的数据链路层
局域网的数据链路层
网络为一个单位所有, 且地理范围和站点数目均有限
具有广播功能
便于系统的扩展和逐渐演变, 各设备的位置可灵活调整和改变
提高了系统的可靠性, 可用性, 生存性
分类,星形网, 总线网, 环形网, 总线网中以太网最为出名
以太网标准
数据链路层被拆分为两层, 逻辑链路控制层LLC, 媒体接入控制MAC
适配器作用: 进行数据串行传输和并行传输的转换
CSMA/CD, 载波监听多点接入/碰撞检测
采用无连接的方式, 尽最大努力交付, 重传也当做新的数据帧
曼彻斯特编码
载波监听是检测信道, 每个站都必须不停检测信道
碰撞检测也就是边发边听
一个站不可能同时进行发送和接收
争用期是值以太网在发送数据帧后至多经过两个时间, 又称碰撞窗口, 经过争用期还没有检测到碰撞才能肯定这次发送没有发生碰撞
以太网使用截断二进制指数退避算法, 发生碰撞后不是等待信道空闲后立即重发, 而是推迟一个最忌时间, 若连续多次发生冲突, 使用此算法可以使重传需要推迟的平均时间随重传次数二增大(动态退避)
使用集线器的星形拓扑
使用集线器的以太网逻辑上仍是总线网, 使用的还是CSMA/CD协议
一个集线器有许多接口
集线器工作在物理层, 每个接口仅简单转发比特不进行检测
以太网的MAC层
硬件地址又称MAC地址, 固化在适配器ROM中的地址
mac帧由五个字段组成, 前两段分别为6字节长的目的地址和原地址地址, 第三个字段是类型字段
扩展以太网
最初是网桥, 对收到的帧进行转发和过滤, 交换式集线器称为以太网交换机
实质是多接口网桥, 每个接口都直接与单台主机或另一个以太网交换机相连, 还具有并行性, 链接多个主机同时通信
相互通信的主机都是独占传输媒体, 无碰撞的传输数据
虚拟局域网
100BASE-T 以太网, 使用IEEE802.3CSMA/CD协议, IEE802.3u
吉比特以太网IEE802.3z
10吉比特以太网和更快地以太网IEE802.3an
网络层
## 网络层提供的两种服务
网络层向上只提供简单灵活的,无连接的尽最大努力交付的数据报服务
虚电路服务和数据报服务的对比
对比的方面 | 虚电路服务 | 数据报服务
— | — | —-
思路|可靠通信应当由网络来保证|可靠通信应该由用户主机来保证
连接的建立 | 必须有 | 不需要
终点的地址 | 仅在连接建立阶段使用, 每个分组使用短的虚电路好 | 每个端点都有终点的完整地址
分组的转发 | 属于同一虚电路的分组按照同意路由转发 | 每个分组独立选择路由进行转发
结点故障 | 均失败 | 丢失分组
分组顺序 | 按序到达 | 无序
差错控制流量控制 | 由网络或者用户主机负责 | 由用户主机负责
网际协议IP
与IP协议配套的协议还有三个协议
地址解析协议ARP, 反向的RARP逆地址解析协议
网际控制报文协议ICMP
网际组管理协议IGMP
虚电路互连网络
将网络互连起来的设备要使用一些中间设备
物理层使用的中间设备叫做转发器
数据链路层使用的中间设备叫做网桥
网络层使用的中间设备叫做路由器
在网络层以上使用的中间设备叫做网关, 用网关连接两个不兼容的系统需要在高层进行协议的转换
当中间设备是装阿奇或网桥, 这仅仅是扩大一个网络, 这仍然是一个网络, 并不是网络互连
互联网可以由多种易购网络互连组成
分类ip地址
IP地址由两个长度的字段组成, 第一个字段是网络号, 标志主机(或路由器)所连接到的网络, 一个网络号在整个互联网范围内是必须是唯一的, 第二个字段是主机号, 标志主机
A类B类C类地址的网络号字段分别为一个, 两个, 三个字节长
D类地址(前四位1110)用于多播
E类地址(前四位1111)保留以后用
A类地址网络号占一个字节,只有7位可供使用, 可使用的网络好为(2^7 - 2), 一个是全0地址, 代表网络本身. 一个是127保留作为本地软件环回测试主机间进程使用
A类地址主机号占三个字节, 每一个A类网络最大主机数为(2^24 -2): 全0的表示表示网络地址, 全1的表示网络上的所有主机
B类地址可指派的网络数为2^14 -1个, 最大主机数为2^16 - 2个
ip地址与硬件地址
物理地址是数据链路层和物理层使用的地址, IP地址是网络层和以上各层使用的地址, 是一种逻辑地址
IP地址放在IP数据报的首部, 硬件地址放在mac帧的首部, 在网络层和网络层以上使用的是ip地址, 数据链路层及一下使用的是链路地址
在ip抽象的互联网上只能看见IP数据报
路由器只根据目的站的ip地址的网络号进行路由选择
在局域网的链路层, 只能看见mac帧
隐藏细节
地址解析协议ARP
ARP是在主机ARP高速缓存中存放一个IP地址到硬件地址的映射表, 并且实时更新
广播请求分组
ip数据报格式
固定首部
版本号4位, 协议版本ipv?
首部长度4位, 表示最大十进制数值为15字节, 最常用的首部长度为20字节(即0101)
区分服务8位, 一般不使用
总长度值首部和数据数据之和的长度, 单位为字节, 总长度为16位, 数据报最大长度为2^16 - 1字节
IP协议规定, 所有的主机路由必须能够接受长度不超过576字节的数据报, 超过时需要分片
标识16位, 作为计数器, 产生一个数据报就加1, ip作为无连接服务不存在按序接收,用来重组数据报, 在分片时这个标识字段复制给所有的数据报标识字段
标志3位, 只有两位有意义
最低位字段为MF,MF=1 标识后面还有分片的数据报,MF=0 标识是若干数据报片中的最后一个
中间一位为DF, 标识不能分片, 当DF=0才允许分片
片偏移13位, 分片后在原分组的相对位置
生存时间8位, 防止无法转发的, 每经过一个路由器就把TTL减去数据报在路由器消耗的时间, 小于一就减一,现在是跳数限制
协议8位,例如ICMP
首部校验和16位, 只校验数据报的首部, 先把IP数据报首部划分为16字节的序列, 校验和字段设为0, 算数运算想加所有的, 写入到检验和字段, 接收方收到后把所有的16字节使用运算相加后取反码, 未发生变化结果为0
源地址
目的地址
划分子网和构造超网
划分子网
ip地址空间利用率不合理
给每一个物理网络分配一个网络号会使路由表变的太大, 互联网中的网络越多, 路由器中的路由表项目数越多, 也会导致路由器成本变高
两级IP地址不灵活
划分子网的思路与
一个拥有许多物理网络的单位, 可将所属的网络划分成若干子网, 划分子网是单位内部的, 单位以外的看不见, 只表现一个物理网络
从主机网络号借用若干位做子网号
子网掩码, 用来确定网络划分
把三级ip地址和收到的数据报目的地址按位与就能得出子网的网络地址
使用子网时分组的转发
路由表必须包含: 目的网络地址, 子网掩码, 下一跳地址
相与之后的结果如果不在路由表, 继续将结果再次相与
无分类编址CIDR(构造超网)
消除了传统A类等划分子网的概念
将32位IP地址分为前后两部分, 前面部分是网络前缀,后面部分指主机
使用斜线记法,IP地址后面加上斜线,然后协商网络前缀所占的位数
CIDR使用32为的地址掩码,也可称为子网掩码,斜线表示1的个数
最长前缀匹配
网际控制报文协议
允许主机或路由器报告差错
种类
ICMP差错报告报文
ICMP询问报文
统一三个字段: 类型, 代码和校验和
ICMP报文种类 类型值 ICMP报文类型
差错报告报文 3 终点不可达
– 11 时间超过
– 12 参数问题
– 5 改变路由
询问报文 8/0 回送请求或应答
– 13/14 时间戳请求或回答
应用在ping/tracerout
互联网的路由选择协议
理想的路由算法
算法完整
计算简单
适应变化
具有稳定性
公平的
最佳的
分层次路由选择协议
自治系统
内部网关协议IGP, 处在相同同的自治系统中, 如RIP和OSPF协议
外部网关EGP协议
内部网关RIP是一种分布式的基于距离向量的路由网关协议, 距离也称跳数, RIP允许一条路劲最多15个路由器, 只适用于小型互联网
仅仅和邻路由器交换信息
交换的信息是路由表
按固定时间间隔交换信息
RIP协议首部4字节, 每个路由信息20字节, 最多包含25个路由
内部网关协议OSPF
开放最短路径优先, 是分布式的链路状态协议
洪泛法向本自治系统所有路由器发送信息
发送的是与本路由器相邻的所有路由器链路状态
只有当链路状态发生变化后才向路由器发送信息
外部网关协议BGP
互联网规模太大, 自治系统之间路由选择困难
自治系统之间的路由选择必须考虑油管策略
ipv6
IPv6基本首部
更大的地址空间
扩展的地址层次结构
灵活的首部格式
允许协议的继续扩充
支持资源的预分配
首部8字节对其
IPv6的改变
取消了首部长度字段, 固定40字节
取消服务类型字段, 优先级和流标号实现了功能
取消了总长度字段改为有效载荷长度字段
取消了标识,标志, 片偏移字段, 包含在分片扩展首部
取消了协议字段, 改用下一个首部字段
取消了检验和字段
取消了选项字段, 用扩展来实现功能
首部格式
版本4位
通信量8位,区分数据报类别或优先级
流标号20位,流是互联网上从特定源点到特定终点(单播或多播)的一些了数据报(视频等), 流经过的路径上都保证指明服务质量
有效载荷长度16位, 除基本首部以外的字节数, 最大值为64kB
下一个首部8位,相当于ipv4协议字段或可选字段
跳数限制,TTL
源地址128位
目的地址128位
ipv6的地址
单播:点对点通信
多播:一点对多点的通信
任播:终点是一组计算机
IPv4到IPv6的转变
双协议栈值一部分主机装有双协议站,v4和v6,双向通信,若DNS返回的是ipv4的地址,双协议栈主机就是用v4, 在v4向v6转发的时候转换后再发, 但是有些数据没法恢复, 例如流标号
隧道技术, 在ipv6进入v4网络时, 将ipv6数据报封装为v4数据部分,整个v6数据报变成了v4数据报数据部分
ip多播
vpn和网络地址转换NAT
vpn在对路由器中的所有路由器, 对目的地址是专用地址的数据报一律不进行转发,也称专用互联网
运输层
运输层协议概述
进程之间的通信
运输层向它上面的应用层提供通信服务, 是面向通信部分的最高层, 也是用户功能的最低层
运输层为相互通信的应用进程提供了端到端的逻辑通信
IP层真正通信的实体是主机中的进程
运输层的两个主要协议
用户数据报协议UDP
传输控制协议TCP
运输层端口
运输层所有的应用进程都可以通过运输层传送到IP层,这就是复用, 运输层从IP层收到发送给各应用进程的数据够分别交付给应用进程, 这就是分用
通信的终点虽然是应用进程, 单报文交付的是目的主机端口
晕乎数据协议UDP
udp是无连接的
尽最大努力交付
UDP是面向报文的, 一次性交付一个完整的报文
没有拥塞控制
UDP支持一对一, 一对多, 多对多的交互通信
UDP首部开销小
UDP首部格式, 首部字段只有8个字节
源端口, 不需要可全为0
目的端口
长度, 最小值为8
检验和, 判断是够有误
计算校验和时, 需要在头部加上一个伪首部(源IP地址, 目的IP地址, 0, 17, UDP长度), 仅用作计算检验和
传输控制TCP
面向连接
每条连接有两个TCP
TCP提供全双工通信
面向字节流, 指流入到进程或从进程流出的字节序列
tcp的链接
TCP连接的端点是套接字,套接字是IP拼接端口
可靠传输的工作原理
TCP下面的网络提供的都是不可靠的传输, 不可靠的传输信道能够实现可靠传输
停止等待协议
停等就是每发送一个分组就停止发送等到确认
无差错情况
出现差错后重传, 设置重传等待时间
确认丢失和确认迟到
信道利用率= 发送分组时间/(发送分组时间 + RTT + 确认分组所需时间)
为了提高传输速率, 使用流水线传输
连续ARQ协议
滑动窗口协议, 每收到一个确认, 发送窗口就向前滑动一个分组位置,接受方采用累计确认的方式
TCP报文段首部格式
源端口和目的端口, 各占2个字节
序号,占四个字节, 报文段序号
确认号, 占四字节, 期望收到对方下一个报文段的第一个数据字节的序号, 确认号表示序号前的所有数据都已正确收到
数据偏移, 占4位, 指出TCP报文段的数据起始处距离TCP报文段的起始出有多远
保留文6位
控制位6位, URG=1紧急字段
ACK=1是确认号字段才有效
推送PSH=1是立即创建一个报文段发送过去, 接收方收到后立即交付
复位RST=1表示需要释放链接,然后重新建立链接
同步SYN=1而ACK=0是表示这是一个请求连接报文段, 若同意连接, 响应报文段syn=1,ACK=1
终止FIN用来释放连接
窗口站两字节
检验和占两字节, 同UDP一样都要加上伪首部, 第四个字段应改为6
紧急指针,2字节在URG=1才有效,指出本报文段中的紧急数据字节数
选项, 长度可变, 最长20字节
TCP可靠传输的实现
以字节为单位的滑动窗口
超时重传时间的选择
选择确认SACK
TCP流量控制
流量控制是让发送方的发送速率不要太快, 要让接收方来得及接收,设置持续计时器防止丢失后形成死锁
TCP拥塞控制
拥塞控制是防止过多的数据注入到网络中, 这样可以使用网络中的路由器或链路不会过载
流量控制是点对点通信量的控制, 是一个端到端的问题, 抑制发送方的数据发送速率
拥塞控制方法
慢开始, 拥塞避免, 快重传, 快恢复
慢开始和拥塞避免
基于窗口的拥塞控制, 发送方维持一个拥塞窗口的状态变量, 窗口的大小取决于网络的拥塞程度, 发送方让自己的发送窗口等于拥塞窗口
判断出现网络拥塞的依据是出现了超时
慢开始是由小到大逐渐增大拥塞窗口数值,每经过一个传输轮次就加倍
防止拥塞窗口增长过大引起拥塞,设置慢开始门限
拥塞避免算法就是让拥塞串口慢慢增大, 每经过一个往返时间RTT就加1
快重传是不等待自己发送数据是才确认,而是立即发送确认
TCP运输链接管理
三次握手
四次挥手
应用层
不同的网络应用的应用进程之间, 需要不同通信规则
域名系统DNS
域名到IP的解析是有分布在互联网的域名服务器程序完成
域名不区分大小写, 完整域名不超过255个字符
域名服务器
一个服务器所负责管辖的范围叫区, 一个区的所有节点必须是能够连通的, DNS服务器的范围不是以域作为单位, 而是区作为单位, 区小于等于域
根域名服务器是最高层次的域名服务器, 所有根域名服务器都知道所有的顶级域名服务器
顶级域名服务器, 这些域名管理所有二级域名
权限域名服务器, 一个区的域名服务器
本地域名服务器
主机向本地域名服务器的查询一般采用递归查询
本地域名服务器向根域名服务器查询使用迭代查询
维护有高速缓存
文件传送协议FTP
FTP客户与服务器建立两个连接, “控制连接”和”数据传送连接”
简单文件传送协议TFTP
每次传送512字节数据
数据报文按序编号,从1开始
支持ASCII码或二进制传送
可以对文件读写
使用简单的首部
远程终端协议TELNET
万维网WWW
统一资源定位符url
超文本传送协议HTTP
http1.1 使用的持续连接
代理服务器
请求代理服务器
若存在就返回
否则代理服务器与互联网上的源点服务器建立TCP连接,并发送http报文
源点服务器吧请求的对象放在http响应报文返回代理服务器
代理服务器收到后复制到自己本地存储器中, 在吧这个对象放在http响应报文总通过已建立的链接返回请求该对象的浏览器
http报文结构
开始行, 区分请求报文和响应报文, 在开始行的三个字段之间用空格分隔开
首部行, 说明浏览器服务器或报文主体的一些信息,每一行的结束位置都要有回车和换行, 整个首部结束时, 还要空一行隔开
实体主题
电子邮件
简单邮件传送协议SMTP, 通用互联网邮件扩充(附件),邮件读取协议pop3
连接建立250, 不可用421
邮件传送, 出错451, 452空间不够, 500命令无法识别
连接释放221
动态主机配置协议DHCP
简单网络管理协议SNMP
网络安全
被动攻击是从网络上窃听他人通信内容
主动攻击
篡改, 更改报文流
恶意程序
计算机病毒
计算机蠕虫
特洛伊木马
逻辑炸弹
后门入侵
流氓软件
拒绝服务
安全的计算机网络
保密性
端点鉴别
信息完整性
运行完全性
两类密码体制
对称密码密码体制,DES属于对称秘钥密码体制, 保密性取决于对秘钥的保密, 算法是公开的
公钥密码体制使用不同的加密秘钥和解密秘钥,基于大数分解的RSA体制
数字签名
接受者能够核实发送至对报文的签名
接受者确认收到的数据没有被篡改过
发送者不可抵赖
鉴别
报文鉴别
密码散列函数
MD5和SHA-1,SHA
报文鉴别码
运输层安全协议
安全套接字层SSL
运输层安全TLS
应用层安全协议PGP
系统安全防火墙
防火墙是一种访问控制技术,严格控制网络边界的分组禁止任何不必要的通信
Manjaro是一个基于Arch Linux的对用户友好的操作系统。
它的特点如下:
2.1.1、下载:
https://manjaro.org/get-manjaro/
国外下载地址可以下载到最新版
国外官方bt下载:https://osdn.net/dl/manjaro/manjaro-xfce-18.0.2-stable-x86_64.iso.torrent
国内清华镜像下载:https://mirrors.tuna.tsinghua.edu.cn/manjaro-cd/
图形化安装方式按照提示安装即可,下面介绍命令行方式
下载Manjaro-architect 版
https://manjaro.org/get-manjaro/
使用usb写入工具https://etcher.io/,把下载的iso镜像文件写入u盘,
dd bs=4M if=/path/to/manjaro.iso of=/dev/sd[drive letter] status=progress
即可将其烧录至U盘中。实体机设置BIOS使用,使用f12 u盘启动电脑就可以了。
ping 114.114.114.114
nmcli dev wifi connect <SSID name> password <your password>
sudo pacman-mirrors -c China -i -m rank
选择最近的仓库源安装shutdown -h now
sudo pacman -Syysudo pacman -S wqy-bitmapfont wqy-microhei wqy-zenhei wqy-microhei-lite
参考引用:https://forum.manjaro.org/t/installation-with-manjaro-architect-iso/20429
更新就近源,使用网络安装的话,自动就帮你配置好源了。
sudo pacman-mirrors -c China -i -m rank
echo -e "\n[archlinuxcn]\nSigLevel = TrustAll\nServer = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/\$arch\n\n[antergos]\nSigLevel = TrustAll\nServer = https://mirrors.tuna.tsinghua.edu.cn/antergos/\$repo/\$arch\n"|sudo tee -a /etc/pacman.conf
添加archlinuxcn源(参考官方建议)
echo -e "\n[archlinuxcn]\nSigLevel = Optional TrustedOnly\nInclude = /etc/pacman.d/archlinuxcn\n"|sudo tee -a /etc/pacman.confecho -e "\nServer = https://cdn.repo.archlinuxcn.org/\$arch\n"|sudo tee -a /etc/pacman.d/archlinuxcnsudo pacman -Syy && sudo pacman -S archlinuxcn-keyringhttps://github.com/archlinuxcn/repo
sudo pacman -S package_name //安装软件sudo pacman -R package_name //删除软件sudo pacman -Ss string1 string2 ... //搜索sudo pacman -Si vlc //查看包信息sudo pacman -Sc //清空并且下载新数据sudo pacman -Syy //刷新缓存sudo pacman -Syu //更新系统(升级软件包)也可以通过图形化进行操作。
参考:https://wiki.archlinux.org/index.php/Pacman_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
sudo pacman -S wqy-bitmapfont wqy-microhei wqy-zenhei wqy-microhei-lite更多字体选用:sudo pacman -S adobe-source-han-sans-cn-fonts adobe-source-han-serif-cn-fonts noto-fonts-cjk noto-fonts noto-fonts-emoji
参考资料:https://wiki.archlinux.org/index.php/Fonts_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
sudo pacman -S fcitx-im fcitx-configtool fcitx-googlepinyin fcitx-sogoupinyinsudo echo -e "export GTK_IM_MODULE=fcitx\nexport QT_IM_MODULE=fcitx\nexport XMODIFIERS=@im=fcitx">>~/.xprofile
重启生效
如果是mint桌面环境,还需要在任务栏添加键盘小程序才能启动中英文切换。
假如我们需要将sdb1硬盘挂载到/data
df 命令查看需要挂载的磁盘名称,本例中是sdb1$ sudo df -l
mkdir命令创建一个文件夹,即挂载点,本例中挂载到根目录的/data文件夹$ sudo mkdir /data
将sdb1硬盘挂载到/data$ sudo mount -w /dev/sdb1 /data
实现系统启动后自动挂载该分区
$ sudo vim /etc/fstab 在/etc/fstab最后一行添加以下内容:/dev/sdb1 /data ext4 defaults 1 2
proxychains-ng:命令行代理sudo vim /etc/proxychains.conf
文件末尾添加:socks5 127.0.0.1 1080
然后,大多数的终端软件就可以上网了,例如:proxychains curl www.google.com
shadowsocks-qt5:s5代理
配置virtualbox
安装VirtualBox
提示:安装VirtualBox时,您需要知道正在运行的内核版本。要获取此信息,请在终端中输入命令uname -r。
要安装VirtualBox,请在终端中输入以下命令:
sudo pacman -S virtualbox virtualbox-guest-iso virtualbox-ext-vnc
然后需要选择要安装的相应VirtualBox主机模块,具体取决于当前运行的内核版本。与安装驱动程序相似,这样做可确保VirtualBox能够正常运行。例如,在运行内核3.7的情况下,将输入相应的数字以安装以下模块:
uname -r
你会得到类似的东西:3.7.4-1-MANJARO。这意味着内核是linux37。然后安装它
sudo pacman -S linux 37 -virtualbox-host-modules
安装完成后,就需要将VirtualBox模块添加到内核中。简单的方法是简单地重启系统。否则,要立即开始使用VirtualBox,请输入以下命令:
sudo vboxreload
向下滚动到页面底部以查找Oracle VM VirtualBox扩展包部分
2.单击安装的VirtualBox版本的相应链接以下载Extension Pack
3.下载Extension Pack后,启动VirtualBox应用程序,启动需要使用sudo virtualbox
5.选择“ 扩展”选项卡以查看该部分,然后单击位于最右侧的 名为“ 添加包”的图标
7.找到下载的Extension Pack,确保它突出显示,然后单击“ 打开”按钮开始安装过程
8.确认您要安装扩展包,然后确认您同意许可条款(至少需要向下滚动到术语底部以激活我同意按钮)
sudo pacman -S virtualbox-ext-oracle
最后一步是将您的个人用户帐户添加到vbox用户组。这是完全访问VirtualBox提供的功能所必需的,包括在Guest操作系统中使用USB设备的能力。
将您的帐户添加到vbox用户组
将用户名添加到vbox用户组的命令的语法是:
sudo gpasswd -a [用户名] vboxuserssudo gpasswd -a li vboxuserssudo VBoxManage extpack install --replace Oracle_VM_VirtualBox_Extension_Pack-${LatestVirtualBoxVersion}.vbox-extpack
]]>sudo apt-get install -y docker.io
等待安装完毕,现在我们使用下面的命令启动 Docker:
systemctl start docker
运行系统引导时启用 docker,命令:
systemctl enable docker
]]>面向新手的笔记
个人看法,先快速上手,再慢慢深入
框架名字 demo
关键字一般都能找到一些简单入门的程序,星星多的项目一般项目结构也有参考价值官网文档
官方文档
官方文档
重要的说三遍,要想把一个框架学精,一定要把官网看完,官网上的是出处,所有的教程都是从官网上学了以后加工的产物
做项目,按照一定的规范去写项目,去github找找有没有人写过某个项目开源了,参考优秀的项目架构,代码规范,任何语言都有代码规范检查工具,一定要用 python
有 pylint
,使用 git做版本控制
github上资源很多,搜教程,搜书,搜代理软件,慢慢探索吧
在windows下安装多版本的 python 可以使用默认安装的 py 命令
常用命令
py -0
py -3.6 xx.py
使用3.6版本运行xx.py文件py -3.6 -m pip
代替pip命令使用python3.6安装模块使用anacoda管理多个版本的python,这个程序是创建虚拟环境用的不同的虚拟环境就像不同的隔离环境,在A环境安装的包在B虚拟环境中需要再次安装,适合电脑上有多个项目,不同项目依赖的包存在冲突时使用
常用命令
conda create --name 虚拟环境的名字 python=3.6
创建了一个使用python3.6的虚拟环境activate 虚拟环境的名字
deactivate
或者直接关掉终端(cmd)窗口进入虚拟环境后可以使用pip安装需要用到的包,或者使用
conda install 包名
命令安装需要的包
特殊情况下import
时使用的的名字和安装时的名字不同比如openCV,百度查一下某某包的安装命令
pip install 包名
pip freeze
这里输出的内容可以保存到文件里,方便在新电脑上安装 pip freeze > requirements.txt
pip install -r requirements.txt
注意:这里的文件名是一般用这个,但可以随便起名字 项目中打开是 包名==版本号 这样格式的文件是什么就从哪里安装如果提示文件找不到,检查一下路径]]>补充 : git 使用教程 或者廖雪峰的git教程
网络问题,从这里 按照说明配置清华大学镜像,其他镜像源还有 豆瓣 阿里等,根据所在位置选择合适的镜像源
专为hexo打造的客户端,使用体验:
启动比 hexo client 快
传图片方便
可以创建管理文章
配合简单的node脚本方便快捷
"scripts": { "test": "hexo clean && hexo generate && hexo server", "pull": "git pull origin source", "push": "git add . && git commit -m\"add a post\" && git push origin source" }
]]>这是leetcode 第 312 题 戳气球
有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] nums[i] nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。
求所能获得硬币的最大数量。
示例1
输入: [3,1,5,8] 输出: 167 解释: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
dfs+记忆化搜索
动态规划
对于区间 [l, r] ,我们考虑最后一个被戳破的气球 k ,那么之前的步骤我们可以分为两步,也就是求 [l, k-1] 和 [k+1, r] 之间的最大分数。
那么为什么不考虑先戳破 k 呢?因为这样的话 [l, k-1] 和 [k+1, r] 就会连接在一起,两个子状态就不能独立计算了,互相会产生影响。
两个子区间的最大的分算完之后,最后 k 的得分就是 nums[l-1] nums[k] nums[r+1] ,取使得总得分最高的 k 就行了。
有一个小技巧就是,提示里也说了,就是刚开始的时候在首尾各添加一个分数为 1 的虚拟气球。
但是直接这样递归会超时,因为有很多的子状态都重复计算了,所以可以用一个全局的数组保存每个状态的分数。初始化为 -1 ,如果某个状态计算过了,就直接返回它的值就行了,不然就递归计算。
class Solution: def maxCoins(self, nums: List[int]) -> int: n = len(nums) nums = [1] + nums + [1] self.dp = [[-1]*(n+2) for _ in range(n+2)] res = self.dfs(1, n, nums) return res def dfs(self, l, r, nums): if self.dp[l][r] != -1: return self.dp[l][r] if l > r: return 0 res = 0 for k in range(l, r+1): res = max(res, nums[l-1]*nums[k]*nums[r+1]+self.dfs(l, k-1, nums)+self.dfs(k+1, r, nums)) self.dp[l][r] = res return res
class Solution: def maxCoins(self, nums: List[int]) -> int: n = len(nums) nums = [1] + nums + [1] dp = [[0]*(n+2) for _ in range(n+2)] for l in range(1, n+1): for i in range(1, n-l+2): j = i + l - 1 for k in range(i, j+1): dp[i][j] = max(dp[i][j], nums[i-1]*nums[k]*nums[j+1] + dp[i][k-1] + dp[k+1][j]) return dp[1][n]
]]>李永乐老师的视频,讲的很通俗易懂了,下面做一下leetcode对应的题
这是leetcode 第 887 题 鸡蛋掉落
你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。
每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
你的目标是确切地知道 F 的值是多少。
无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?
示例 1
输入:K = 1, N = 2输出:2解释:鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。如果它没碎,那么我们肯定知道 F = 2 。因此,在最坏的情况下我们需要移动 2 次以确定 F 是多少。
示例 2
输入:K = 2, N = 6输出:3
示例 3
输入:K = 3, N = 14输出:4
穷举法
二分法
动态规划
最简单的方法就是一层一层试验,这样只需要一个鸡蛋就可以了。
使用二分查找,例如,第一个鸡蛋再50层扔下,如果碎了,第二个鸡蛋从1-49逐层试验;如果没碎,第一个鸡蛋在75层扔下,如果碎了,第二个鸡蛋从51-74逐层试验…但是,这个方法,很容易悲剧,例如,当正好49层是可以安全落下的,需要尝试50次。比只有一个鸡蛋的情况,效果还要差。
采用动态规划的方法,最重要的是要找到子问题。做如下的分析,假设f{n}表示从第n层楼扔下鸡蛋,没有摔碎的最少尝试次数。第一个鸡蛋,可能的落下位置(1,n),第一个鸡蛋从第i层扔下,有两个情况:
碎了,第二个鸡蛋,需要从第一层开始试验,有i-1次机会
没碎,两个鸡蛋,还有n-i层。这个就是子问题了f{n-i}
所以,当第一个鸡蛋,由第i个位置落下的时候,要尝试的次数为1 + max(i - 1, f{n - i}),那么对于每一个i,尝试次数最少的,就是f{n}的值。状态转移方程如下:
f{n} = min(1 + max(i - 1, f{n - 1}) ) 其中: i的范围为(1, n), f{1} = 1
class Solution { public int superEggDrop(int K, int N) { return dp(K, N); } Map<Integer, Integer> memo = new HashMap(); public int dp(int K, int N) { if (!memo.containsKey(N * 100 + K)) { int ans; if (N == 0) ans = 0; else if (K == 1) ans = N; else { int lo = 1, hi = N; while (lo + 1 < hi) { int x = (lo + hi) / 2; int t1 = dp(K-1, x-1); int t2 = dp(K, N-x); if (t1 < t2) lo = x; else if (t1 > t2) hi = x; else lo = hi = x; } ans = 1 + Math.min(Math.max(dp(K-1, lo-1), dp(K, N-lo)), Math.max(dp(K-1, hi-1), dp(K, N-hi))); } memo.put(N * 100 + K, ans); } return memo.get(N * 100 + K); }}
class Solution { public int superEggDrop(int K, int N) { // Right now, dp[i] represents dp(1, i) int[] dp = new int[N+1]; for (int i = 0; i <= N; ++i) dp[i] = i; for (int k = 2; k <= K; ++k) { // Now, we will develop dp2[i] = dp(k, i) int[] dp2 = new int[N+1]; int x = 1; for (int n = 1; n <= N; ++n) { // Let's find dp2[n] = dp(k, n) // Increase our optimal x while we can make our answer better. // Notice max(dp[x-1], dp2[n-x]) > max(dp[x], dp2[n-x-1]) // is simply max(T1(x-1), T2(x-1)) > max(T1(x), T2(x)). while (x < n && Math.max(dp[x-1], dp2[n-x]) > Math.max(dp[x], dp2[n-x-1])) x++; // The final answer happens at this x. dp2[n] = 1 + Math.max(dp[x-1], dp2[n-x]); } dp = dp2; } return dp[N]; }}
leetcode hard 看科普视频看懂了,实际做 leetcode 动态规划还有欠缺,下一步重点理解动态规划
]]>生成 ssh 部署私钥
ssh-keygen -t ed25519 -f ~/.ssh/github-actions-deploy
在 GitHub repo 的 Settings/Deploy keys
中添加刚刚生成的公钥
在 GitHub repo 的 Settings/Secrets
中添加 GH_ACTION_DEPLOY_KEY
,值为刚刚生成的私钥
也可以使用添加到github的密钥对不需要添加部署公钥只添加私钥到Settings/Secrets
在项目的根目录添加 deploy.yml
,目录结构如下
.github └── workflows └── deploy.yml
添加部署私钥到 GitHub Actions 执行的容器中
在容器中安装 Hexo 以及相关的插件
克隆主题 matery 并覆盖默认配置
编译静态页面
推送编译好的文件到 GitHub Pages
编写部署的 action
name: Main workflowon: push: branches: - sourcejobs: build: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 - name: Use Node.js 10.x uses: actions/setup-node@v1 with: node-version: '10.x' - name: prepare build env env: GH_ACTION_DEPLOY_KEY: ${{ secrets.GH_ACTION_DEPLOY_KEY }} run: | mkdir -p ~/.ssh/ echo "$GH_ACTION_DEPLOY_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan github.com >> ~/.ssh/known_hosts git config --global user.name 'idongliming' git config --global user.email 'idongliming@qq.com' npm i -g hexo-cli npm i git clone -b master git@github.com:blinkfox/hexo-theme-matery.git themes/matery cp source/_data/matery.yml themes/matery/_config.yml - name: deploy to github run: | hexo generate && hexo deploy
使用github action 可以保证构建环境一致,临时更换电脑写博客不用搭建环境,装上git就可以写,这样的方式同样适用非专业的同学使用github搭建博客,用github desktop clone项目编写发布即可
排序算法是《数据结构与算法》中最基本的算法之一。
排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
public class BubbleSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); for (int i = 1; i < arr.length; i++) { // 设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已经完成。 boolean flag = true; for (int j = 0; j < arr.length - i; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = false; } } if (flag) { break; } } return arr; }}
public class SelectionSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); // 总共要经过 N-1 轮比较 for (int i = 0; i < arr.length - 1; i++) { int min = i; // 每轮需要比较的次数 N-i for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[min]) { // 记录目前能找到的最小值元素的下标 min = j; } } // 将找到的最小值和i位置所在的值进行交换 if (i != min) { int tmp = arr[i]; arr[i] = arr[min]; arr[min] = tmp; } } return arr; }}
public class InsertSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); // 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的 for (int i = 1; i < arr.length; i++) { // 记录要插入的数据 int tmp = arr[i]; // 从已经排序的序列最右边的开始比较,找到比其小的数 int j = i; while (j > 0 && tmp < arr[j - 1]) { arr[j] = arr[j - 1]; j--; } // 存在比其小的数,插入 if (j != i) { arr[j] = tmp; } } return arr; }}
public class ShellSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int gap = 1; while (gap < arr.length/3) { gap = gap * 3 + 1; } while (gap > 0) { for (int i = gap; i < arr.length; i++) { int tmp = arr[i]; int j = i - gap; while (j >= 0 && arr[j] > tmp) { arr[j + gap] = arr[j]; j -= gap; } arr[j + gap] = tmp; } gap = (int) Math.floor(gap / 3); } return arr; }}
public class MergeSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); if (arr.length < 2) { return arr; } int middle = (int) Math.floor(arr.length / 2); int[] left = Arrays.copyOfRange(arr, 0, middle); int[] right = Arrays.copyOfRange(arr, middle, arr.length); return merge(sort(left), sort(right)); } protected int[] merge(int[] left, int[] right) { int[] result = new int[left.length + right.length]; int i = 0; while (left.length > 0 && right.length > 0) { if (left[0] <= right[0]) { result[i++] = left[0]; left = Arrays.copyOfRange(left, 1, left.length); } else { result[i++] = right[0]; right = Arrays.copyOfRange(right, 1, right.length); } } while (left.length > 0) { result[i++] = left[0]; left = Arrays.copyOfRange(left, 1, left.length); } while (right.length > 0) { result[i++] = right[0]; right = Arrays.copyOfRange(right, 1, right.length); } return result; }}
public class QuickSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); return quickSort(arr, 0, arr.length - 1); } private int[] quickSort(int[] arr, int left, int right) { if (left < right) { int partitionIndex = partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } private int partition(int[] arr, int left, int right) { // 设定基准值(pivot) int pivot = left; int index = pivot + 1; for (int i = index; i <= right; i++) { if (arr[i] < arr[pivot]) { swap(arr, i, index); index++; } } swap(arr, pivot, index - 1); return index - 1; } private void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }}
public class HeapSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int len = arr.length; buildMaxHeap(arr, len); for (int i = len - 1; i > 0; i--) { swap(arr, 0, i); len--; heapify(arr, 0, len); } return arr; } private void buildMaxHeap(int[] arr, int len) { for (int i = (int) Math.floor(len / 2); i >= 0; i--) { heapify(arr, i, len); } } private void heapify(int[] arr, int i, int len) { int left = 2 * i + 1; int right = 2 * i + 2; int largest = i; if (left < len && arr[left] > arr[largest]) { largest = left; } if (right < len && arr[right] > arr[largest]) { largest = right; } if (largest != i) { swap(arr, i, largest); heapify(arr, largest, len); } } private void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }}
public class CountingSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int maxValue = getMaxValue(arr); return countingSort(arr, maxValue); } private int[] countingSort(int[] arr, int maxValue) { int bucketLen = maxValue + 1; int[] bucket = new int[bucketLen]; for (int value : arr) { bucket[value]++; } int sortedIndex = 0; for (int j = 0; j < bucketLen; j++) { while (bucket[j] > 0) { arr[sortedIndex++] = j; bucket[j]--; } } return arr; } private int getMaxValue(int[] arr) { int maxValue = arr[0]; for (int value : arr) { if (maxValue < value) { maxValue = value; } } return maxValue; }}
public class BucketSort implements IArraySort { private static final InsertSort insertSort = new InsertSort(); @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); return bucketSort(arr, 5); } private int[] bucketSort(int[] arr, int bucketSize) throws Exception { if (arr.length == 0) { return arr; } int minValue = arr[0]; int maxValue = arr[0]; for (int value : arr) { if (value < minValue) { minValue = value; } else if (value > maxValue) { maxValue = value; } } int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1; int[][] buckets = new int[bucketCount][0]; // 利用映射函数将数据分配到各个桶中 for (int i = 0; i < arr.length; i++) { int index = (int) Math.floor((arr[i] - minValue) / bucketSize); buckets[index] = arrAppend(buckets[index], arr[i]); } int arrIndex = 0; for (int[] bucket : buckets) { if (bucket.length <= 0) { continue; } // 对每个桶进行排序,这里使用了插入排序 bucket = insertSort.sort(bucket); for (int value : bucket) { arr[arrIndex++] = value; } } return arr; } /** * 自动扩容,并保存数据 * * @param arr * @param value */ private int[] arrAppend(int[] arr, int value) { arr = Arrays.copyOf(arr, arr.length + 1); arr[arr.length - 1] = value; return arr; }}
/** * 基数排序 * 考虑负数的情况还可以参考: https://code.i-harness.com/zh-CN/q/e98fa9 */public class RadixSort implements IArraySort { @Override public int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int maxDigit = getMaxDigit(arr); return radixSort(arr, maxDigit); } /** * 获取最高位数 */ private int getMaxDigit(int[] arr) { int maxValue = getMaxValue(arr); return getNumLenght(maxValue); } private int getMaxValue(int[] arr) { int maxValue = arr[0]; for (int value : arr) { if (maxValue < value) { maxValue = value; } } return maxValue; } protected int getNumLenght(long num) { if (num == 0) { return 1; } int lenght = 0; for (long temp = num; temp != 0; temp /= 10) { lenght++; } return lenght; } private int[] radixSort(int[] arr, int maxDigit) { int mod = 10; int dev = 1; for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) { // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10) int[][] counter = new int[mod * 2][0]; for (int j = 0; j < arr.length; j++) { int bucket = ((arr[j] % mod) / dev) + mod; counter[bucket] = arrayAppend(counter[bucket], arr[j]); } int pos = 0; for (int[] bucket : counter) { for (int value : bucket) { arr[pos++] = value; } } } return arr; } /** * 自动扩容,并保存数据 * * @param arr * @param value */ private int[] arrayAppend(int[] arr, int value) { arr = Arrays.copyOf(arr, arr.length + 1); arr[arr.length - 1] = value; return arr; }}
参考地址 JS-Sorting-Algorithm
]]>阅读java api源码,可以学到很多的设计模式,优秀的算法实现
jdk8之前没有使用红黑树,出现哈希冲突采用哈希桶解决,当哈希冲突过多时会出现效率问题,jdk8采用红黑树,默认当哈希桶链表长度达到8时将链表转化成树,提高操作效率
红黑树是什么?有什么优势?
摘自百科
红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
红黑树不仅是二叉查找树,它还必须满足以下5个性质
相比于平衡二叉树(avl),红黑树追求的是 相对平衡
红黑树的效率相对较高,所以它被用来存储相关数据,基本的操作有增加/删除/查找,在这些操作之后可能会破坏红黑树的性质,所以需要相关操作来维护以让红黑树符合上面的性质要求。
对红黑树进行插入,删除等操作后,会导致不满足红黑树的条件,所以需要进行调整
将x的右子树绕x逆时针旋转,使得x的右子树成为x的父亲,并修改相关引用。
]]>是将x的左子树绕x顺时针旋转,使得x的左子树成为x的父亲,并修改相关的引用。
功能 | 快捷键 |
---|---|
快速打开 anything | C-, |
当前打开文件 | C-M-Down |
切换头/源文件 | C-ko |
返回 | C– |
前进 | C-S– |
跳转到定义 | F12 |
在新窗口展示定义 | M-F12 |
跳转到声明 | C-M-F12 |
上/下移动行 | M-Up/Down |
在上面插入行 | C-Enter |
在下面插入行 | C-S-Enter |
折叠/展开当前代码段 | C-mm |
注释 | C-kc |
取消注释 | C-ku |
选中单词大写 | C-S-u |
选中单词小写 | C-u |
调试 | F5 |
执行 | C-F5 |
设置断点 | F9 |
步过 | F10 |
步入 | F11 |
Make ctrl+click perform a “Go To Definition” on the identifier under the cursor.
Productivity tool for C/C++ and C# that improves IDE features related to navigation, refactoring, code generation, and coding assistance.
C –> Ctrl
S –> Shift
M –> Alt
Cmd –> Command
功能 | Windows | Mac OS X |
---|---|---|
高亮选中文本 | C-S-h 或 C-M-h | |
插入超链接 | C-k | Cmd-k |
复制选中文本的格式(格式刷) | C-S-c | Cmd-M-c |
应用格式刷到选中文本 | C-S-v | Cmd-M-v |
打开光标下的链接 | Enter | Enter |
加黑 | C-b | Cmd-b |
斜体 | C-i | Cmd-i |
下划线 | C-u | Cmd-u |
删除线 | C-Hyphen(-) | C-Cmd-Hyphen(-) |
上标 | C-S-= | |
下标 | C-= | |
无序列表标记 | C-Period(.) | Cmd-Period(.) |
有序列表标记 | C-Slash(/) | Cmd-Slash(/) |
标题一 | C-M-1 | Cmd-M-1 |
标题六 | C-M-6 | Cmd-M-6 |
正文,清除格式 | C-S-n | Cmd-S-n |
增加段落缩进 | M-S-Right | Cmd-] |
减少段落缩进 | M-S-Left | Cmd-[ |
左对齐 | C-l | Cmd-l |
右对齐 | C-r | Cmd-r |
增大选中文本字体 | C-S-> | |
减小选中文本字体 | C-S-< | |
在当前页面显隐基准线 | C-S-r |
功能 | Windows | Mac OS X |
---|---|---|
插入日期 | M-S-d | Cmd-d |
插入时间 | M-S-t | |
插入日期+时间 | M-S-f | Cmd-S-d |
功能 | Windows | Mac OS X |
---|---|---|
在当前行下面新建行 | C-Enter | Cmd-Enter |
在当前行上面新建行 | 光标移到行首,Enter | 光标移到行首,Enter |
在当前列右边新建列 | C-M-r | C-Cmd-r |
在当前列左边新建列 | C-Cmd-l | |
在当前单元格开始新段落 | M-Enter |
功能 | Windows | Mac OS X |
---|---|---|
移动光标到页面标题并选中 | C-S-t | Cmd-S-t |
将段落上移 | M-S-Up | Cmd-M-Up |
将段落下移 | M-S-Down | Cmd-M-Down |
返回上一个访问过的页面 | M-Left | C-Cmd-Left |
前进到后一个访问过的页面 | M-Right | C-Cmd-Right |
功能 | Windows | Mac OS X |
---|---|---|
新建页面到当前分区最后面 | C-n | Cmd-n |
在当前页面下新建同级页面 | C-M-n | Cmd-n |
在当前页面下新建子页面 | C-M-S-n | |
增加页面层级 | C-M-] | Cmd-M-] |
减少页面层级 | C-M-[ | Cmd-M-[ |
选中当前页面 | C-M-g | Cmd-S-a |
上移当前选中页面 | M-S-Up | Cmd-M-Up |
下移当前选中页面 | M-S-Down | Cmd-M-Down |
返回访问的上一页 | M-Left | C-Cmd-Left |
返回访问的下一页 | M-Right | C-Cmd-Right |
停靠到桌面 | C-M-d | |
显示上一个分区 | C-S-Tab | |
显示下一个分区 | C-Tab | |
显示当前分区的上一个页面 | C-Page Up | |
显示当前分区的下一个页面 | C-Page Down | |
显示当前分区的第一个页面 | M-Home | |
显示当前分区的最后一个页面 | M-End |
搜索带特殊字符如「空格」等,用 "
将搜索关键字括起来。
删除「我的模板」
在 Windows 下的 C:\Users\<username>\AppData\Roaming\Microsoft\Templates
里找到文件 我的模板.one
,双击打开将显示你所有的模板页面,删除你想要删除的然后关闭即可。
优雅简单的 HTTP 模块。
很好用的 HTML/XML 解析器。
JSON 编码解码器。
应用举例:
格式化 JSON 文件
python -m json.tool src.json > dst.json
在 Vim 里格式化 JSON:
:%!python -m json.tool
简单实用的 HTTP 服务器。
应用举例:
运行一个简易的 HTTP 服务器
python -m CGIHTTPServer 80
方便地进行 base64 编解码的模块。
应用举例:
解码 base64
echo aGVsbG93b3JsZA== | python -m base64 -d
则能看到输出
helloworld
参考:http://doc.qt.io/qtcreator/creator-keyboard-shortcuts.html
C –> Ctrl
S –> Shift
M –> Alt
Cmd –> Command
功能 | 快捷键 |
---|---|
自动完成 | C-Space |
显示/隐藏侧边栏 | Cmd-0 |
切换已打开的文件 | M-Tab |
上/下一行 | C-p/C-n |
前进/后退一个字符 | C-f/C-b |
删除一个单词 | M-Del |
构建 | Cmd-b |
运行 | Cmd-r |
调试 | Cmd-y |
注释 | Cmd-/ |
换行 | Cmd-Return |
跳到定义 | F2 |
切换头文件与源文件 | F4 |
前进/后退 | M-Cmd-Left/Right |
打开定位器 | Cmd-k |
C –> Ctrl
S –> Shift
A –> Alt
功能 | 快捷键 |
---|---|
显示所有快捷键 | C-S-l |
开/关注释 | C-/ |
显示 outline | C-o |
当前打开的文件列表 | C-e |
快速查找打开文件 | C-S-r |
查找 | C-h |
查找后跳到下一处 | C-. |
Undo | C-z |
Redo | C-y |
跳到指定行 | C-l |
自动补全 | A-/ |
自动解决导入包问题 | C-S-o |
返回 | A-Left |
反返回 | A-Right |
步进 | F5 |
单步 | F6 |
执行到返回 | F7 |
继续执行 | F8 |
删除当前行 | C-d |
删除前一个词 | C-Backspace |
删除后一个词 | C-Delete |
缩进 | Tab |
减少缩进 | S-Tab |
在下面新起一行 | S-Enter |
在上面新起一行 | C-S-Enter |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency>
- 配置``` yamlspring: jpa: hibernate: ddl-auto: update show-sql: true
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId></dependency>
mybatis: mapper-locations: classpath:mapper/*.xml # mapper文件位置 type-aliases-package: com.xxx.model # 实体类的位置
在spring boot启动类上加@MapperScan(basePackages = "com.xxx")
广泛使用的关系型数据库,在spring boot中配置方式为
spring: datasource: url: jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=utf8&useSSL=false username: password:
java实现的嵌入数据库,不需要安装,适合编写damo使用,在spring boot中的配置方式为
spring: h2: console: enabled: true # 启用自带的控制台界面访问/h2-console datasource: url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE driverClassName: org.h2.Driver username: password:
]]>C –> Ctrl
S –> Shift
M –> Alt
Cmd –> Command
功能 | Windows | Mac OS X |
---|---|---|
当前页面下新建页面 | C-Return | |
预览 | F5 | |
生成 HTML 文件 | F8 | |
置于顶层 | C-S-] | |
置于底层 | C-S-[ | |
上移一层 | C-] | |
下移一层 | C-[ | |
开关左侧功能栏 | C-M-[ | |
开关右侧功能栏 | C-M-] |
功能 | Windows | Mac OS X |
---|---|---|
左对齐 | C-M-l | |
左右居中 | C-M-c | |
右对齐 | C-M-r | |
顶部对齐 | C-M-t | |
上下居中 | C-M-m | |
底部对齐 | C-M-b | |
组合 | C-g |
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎,类似JSP,Velocity,FreeMaker等,它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。
Thymeleaf是spring boot推荐使用的模板引擎,最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用,但是总是看到说其效率有点低
在html页面中引入Thymeleaf命名空间,即<html lang="en" xmlns:th="http://www.thymeleaf.org">
此时在html模板文件中动态的属性使用th:某某某
命名空间修饰
<p th:text="'你是否读过,'+${book.name}+'!!'"> 同EL表达式有些相似的效果,如果有数据,被替换 完成前后端分离效果(这些是前端代码)</p>
<link th:href="@{/css/xx.css}"/><script th:src="@{/js/xx.js}"></script>
spring boot默认配置Thymeleaf的静态资源路径为resource/static,上面的代码将引入resource/static/css/ss.css文件
对$变量表达式的简化,直接看代码
<div th:object="${session.user}"> <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div><!--等价于--><div> <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p></div>
#{}
简单看一下就可以,文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用Key索引Value,还可以提供一组参数(可选).
<head><title th:test="#{main.title}"></title> <body> <table> <th th:text="#{header.address.city}"> <th th:text="#{header.address.country}"> </table></body>
字面值
文本操作(Text operations)
算术运算(Arithmetic operations)
布尔操作(Boolean operations)
比较和等价(Comparisons and equality)
条件运算符(Conditional operators)三元运算符
<a href="comments.html" th:href="@{/comments(prodId=${prod.id})}" th:unless="${#lists.isEmpty(prod.comments)}">view</a><a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"> <td th:text="${prod.name}">Onions</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}? #{true} : #{false}">yes</td></tr>
模板
定义模板
<div th:fragment="copy"></div>
使用模板
<div th:import="copy"></div>