SoftwareBasic
2021-02-20 14:55:30 5 举报
AI智能生成
开发
作者其他创作
大纲/内容
计算机理论
计算机网络
阅读书籍
计算机网络:自顶向下
图解http
相关知识点
应用层/dns、http
深入理解HTTP协议、HTTP协议原理分析
解读HTTP/2与HTTP/3 的新特
HTTP、DNS、DHCP
1) HTTP1.0 和 HTTP1.1 的区别
HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,
服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。
服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。
HTTP 1.1支持持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。
HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,
但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,
这样也显著地减少了整个下载过程所需要的时间。
HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,
但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,
这样也显著地减少了整个下载过程所需要的时间。
2) HTTP的长连接和短连接
HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议.
短连接
浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接
长连接:
当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,
如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
实现长连接要客户端和服务端都支持长连接
如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
实现长连接要客户端和服务端都支持长连接
TCP短连接
client向server发起连接请求,server接到请求,然后双方建立连接。
client向server发送消息,server回应client,然后一次读写就完成了,
这时候双方任何一个都可以发起close操作,不过一般都是client先发起 close操作.
短连接一般只会在 client/server间传递一次读写操作
client向server发送消息,server回应client,然后一次读写就完成了,
这时候双方任何一个都可以发起close操作,不过一般都是client先发起 close操作.
短连接一般只会在 client/server间传递一次读写操作
TCP长连接
client向server发起连接,server接受client连接,双方建立连接。
Client与server完成一次读写之后,它们之间的连接并不会主动关闭,
后续的读写操作会继续使用这个连接。
Client与server完成一次读写之后,它们之间的连接并不会主动关闭,
后续的读写操作会继续使用这个连接。
3) HTTP 和 HTTPS 的区别
超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,
HTTP协议以明文方式发送内容,不提供任何方式的数据加密,
如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,
因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息
HTTP协议以明文方式发送内容,不提供任何方式的数据加密,
如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,
因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息
为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,
为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,
并为浏览器和服务器之间的通信加密
为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,
并为浏览器和服务器之间的通信加密
4) DNS 的两种解析过程(迭代查询和递归查询)
迭代查询
递归查询:如果DNS 服务器本地没有存储查询DNS 信息,
那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。
(也就是说客户机发送请求后自己只用等待结果即可,中间具体过程交给服务器实现)
那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。
(也就是说客户机发送请求后自己只用等待结果即可,中间具体过程交给服务器实现)
例子
客户机A向DNS服务器B发送查询请求,B查询本地服务器发现没有记录,无法解析,
则它继续递归查询DNS服务器C,C查询本地服务器发现也没有记录,无法解析,
则C继续递归查询DNS服务器D,D查询本地服务器发现有记录,可以解析,
则D将解析后的结果传送给C,C将解析后的结果传送给B,B将解析后的结果传送给A,
自此,解析结束
则它继续递归查询DNS服务器C,C查询本地服务器发现也没有记录,无法解析,
则C继续递归查询DNS服务器D,D查询本地服务器发现有记录,可以解析,
则D将解析后的结果传送给C,C将解析后的结果传送给B,B将解析后的结果传送给A,
自此,解析结束
图解
递归查询
DNS 服务器会向客户机提供其他能够解析查询请求的DNS 服务器地址,当客户机发送查询请求时,
DNS 服务器并不直接回复查询结果,而是告诉客户机另一台DNS 服务器地址,
客户机再向这台DNS 服务器提交请求,依次循环直到返回查询的结果。
(也就是说客户机的请求需要自己挨个去查询才能得到结果,服务器没有结果时候只会给你提供其它服务器的地址,
而不会帮你去请求查询,这与递归截然相反)
DNS 服务器并不直接回复查询结果,而是告诉客户机另一台DNS 服务器地址,
客户机再向这台DNS 服务器提交请求,依次循环直到返回查询的结果。
(也就是说客户机的请求需要自己挨个去查询才能得到结果,服务器没有结果时候只会给你提供其它服务器的地址,
而不会帮你去请求查询,这与递归截然相反)
例子
客户机A向DNS服务器B发送查询请求,B查询本地服务器发现没有记录,无法解析,
则它会告诉客户机A,“我无法查询”,
但是会把其他可能有的服务器比如B的地址告诉客户机A,让客户机A去请求服务器C,
如果A接着查询C发现C也没有,但是C会把其他可能有的服务器比如D的地址告诉客户机A,
假设最终由D查询并解析出来,则直接将结果递交给客户机A
则它会告诉客户机A,“我无法查询”,
但是会把其他可能有的服务器比如B的地址告诉客户机A,让客户机A去请求服务器C,
如果A接着查询C发现C也没有,但是C会把其他可能有的服务器比如D的地址告诉客户机A,
假设最终由D查询并解析出来,则直接将结果递交给客户机A
图解
5) DHCP介绍
动态主机配置协议,使用UDP协议工作, 给内部网络自动分配IP地址
6) HTTPS和SSH方式的区别和使用
https每次get、push都需要输入用户名和密码
ssh只需要在本地维护一个sshkey的文件,不需要每次都输入信息。
7) POST和GET的区别
GET方法使用的时候,浏览器中会产生目标URL,而POST不会。
GET一般用于获取/查询资源信息,而POST一般用于更新资源信息
http3.0
网络常见面试题
2) 从输入网址到获得页面的过程
3) Session、Cookie 与 Application
4) SQL 注入
传输层/tcp、udp
一个 TCP 连接可以发多少个 HTTP 请求
TCP相关概念
1) TCP的流量控制、差错控制、拥塞控制
2) TCP协议是如何确保传输可靠性的
3) TCP的三次握手和四次挥手
4) TCP和UDP有什么区别
网络层/ip
IP地址划分
1) IP、ARP 和 RARP、ICMP 和 IGMP、IP地址和MAC地址、计算机网络中各层协议之间关系
2) 路由器、网桥器材等基本概念
3) IP地址划分、子网划分、构造超网、VPN和NAT、路由协议、IPV6
广播路由算法?如何解决广播风暴
广播路由算法?
如何解决广播风暴
生成树
数据链路层
网络校验方式
CRC(循环荣誉码)
海明码
物理层
网络问题集锦
网络性能问题
HttpWebRequest的GetResponse或GetRequestStream偶尔超时
+ 总结各种超时死掉的可能和相应的解决办法
+ 总结各种超时死掉的可能和相应的解决办法
1
将http的request的keepAlive设置为false
2
修改request的timeout:
8*60*1000【修改为8分钟】
修改request的ReadWriteTimeOut
8*60*1000【修改为8分钟】
3
关闭request和response
if (resp != null)
{
resp.Close();
}
if (req != null)
{
req.Abort();
}
{
resp.Close();
}
if (req != null)
{
req.Abort();
}
4
修改DefaultConnectionLimit的设置
System.Net.ServicePointManager.DefaultConnectionLimit = 500;
5
垃圾回收
在
req = (HttpWebRequest)WebRequest.Create(constSkydriveUrl);
setCommonHttpReqPara(ref req);
resp = (HttpWebResponse)req.GetResponse();
语句之前,添加
System.GC.Collect();
req = (HttpWebRequest)WebRequest.Create(constSkydriveUrl);
setCommonHttpReqPara(ref req);
resp = (HttpWebResponse)req.GetResponse();
语句之前,添加
System.GC.Collect();
UML
UML各种图以及继承、实现、依赖、关联、聚合、组合的联系与区别
关系
继承、实现
依赖、关联
聚合、组合
表示符号上的区别
九种图
用例视角
用例图
设计视角
类图
对象图
进程视角
序列图
协助图
状态图
活动图
实现视角
构建图
拓扑视角
部署图
操作系统
阅读书籍
操作系统-精髓与设计原理(第八版)
深入理解计算机操作系统(原书第三版)
程序是如何跑起来的
相关知识点
作业调度和空闲分区分配算法
PV操作
进程间通信方式
1、管道
无名管道
只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)
FIFO,也称为命名管道
FIFO可以在无关的进程之间交换数据,与无名管道不同
2、消息队列
3、信号量
信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据,若要在进程间传递数据需要结合共享内存
4、共享内存
5、套接字通信
Socket
借助于网络的进程间通信
绑定
监听
6、stream
输入输出通信
什么是句柄
系统为每个进程在内存中分配一定的区域,用来存放各个句柄,即一个个32位无符号整型值(32位操作系统中)。每个32位无符号整型值相当于一个指针,指向内存中的另一个区域(我们不妨称之为区域A)。而区域A中存放的正是对象在内存中的地址。当对象在内存中的位置发生变化时,区域A的值被更新,变为当前时刻对象在内存中的地址,而在这个过程中,区域A的位置以及对应句柄的值是不发生变化的。这种机制,用一种形象的说法可以表述为:有一个固定的地址(句柄),指向一个固定的位置(区域A),而区域A中的值可以动态地变化,它时刻记录着当前时刻对象在内存中的地址。这样,无论对象的位置在内存中如何变化,只要我们掌握了句柄的值,就可以找到区域A,进而找到该对象。而句柄的值在程序本次运行期间是绝对不变的,我们(即系统)当然可以掌握它。这就是以不变应万变,按图索骥,顺藤摸瓜
如何查看windows句柄
1.右键任务栏,打开任务管理器。
2.点击详细信息,打开详细面板。
3.切换到性能面板,点击资源监视器。
4.切换到cpu面板。
5.点击选中希望查看相关句柄的进程,然后点开下面的“关联的句柄”,就会看到关联句柄的相关内容。在搜索框输入文件或者进程名称,也会自动显示出相关联的句柄内容。
2.点击详细信息,打开详细面板。
3.切换到性能面板,点击资源监视器。
4.切换到cpu面板。
5.点击选中希望查看相关句柄的进程,然后点开下面的“关联的句柄”,就会看到关联句柄的相关内容。在搜索框输入文件或者进程名称,也会自动显示出相关联的句柄内容。
微机原理/组成原理
float类型与double类型数谁更大
程序传送方式
1、无条件传送方式,最简单的传送方式,所配置的硬件和软件最少。
2、查询传送方式,CPU的利用受到影响,陷于等待和反复查询、不能再作他用;而且,这种方法不能处理掉电、设备故障等突发事件。
3、中断传送方式,是计算机最常用的数据传送方式,可随时向CPU发中断请求信号,以便及时响应,及时处理,实现实时控制。
4、直接数据通道传送方式,不经过CPU中转,也不通过中断服务程序,既不需要保存、恢复断点和现场,所以传送数据的速度比中断方式更快。
2、查询传送方式,CPU的利用受到影响,陷于等待和反复查询、不能再作他用;而且,这种方法不能处理掉电、设备故障等突发事件。
3、中断传送方式,是计算机最常用的数据传送方式,可随时向CPU发中断请求信号,以便及时响应,及时处理,实现实时控制。
4、直接数据通道传送方式,不经过CPU中转,也不通过中断服务程序,既不需要保存、恢复断点和现场,所以传送数据的速度比中断方式更快。
DMA之理解
信息安全
对称加密与非对称加密
对称密钥加密
加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方。
非对称加密
站点/个人拥有属于自己的一套密码(公钥和私钥),公钥公开,私钥隐秘。任何其他站点发送给该站点的信息,需要使用该站点的公钥加密,只要私钥可以解密。
加密、认证、公钥、私钥
因为加密和认证的不同,
所以公私钥的作用的使用方式也不同
所以公私钥的作用的使用方式也不同
加密
加密是将数据资料加密,使得非法用户即使取得加密过的资料,
也无法获取正确的资料内容,所以数据加密可以保护数据,
防止监听攻击。其重点在于数据的安全 性
也无法获取正确的资料内容,所以数据加密可以保护数据,
防止监听攻击。其重点在于数据的安全 性
加密解密的过程如下
Bob将他的公开密钥传送给Alice。
Alice用Bob的公开密钥加密她的消息,然后传送给Bob。
Bob用他的私人密钥解密Alice的消息
Alice用Bob的公开密钥加密她的消息,然后传送给Bob。
Bob用他的私人密钥解密Alice的消息
认证
身份认证是用来判断某个身份的真实性,确认身份后,
系统才可以依不同的身份给予不同的权限。
其重点在于用户的真实性。两者的侧重点是不同的
系统才可以依不同的身份给予不同的权限。
其重点在于用户的真实性。两者的侧重点是不同的
身 份认证的过程如下
Alice用她的私人密钥对文件加密,从而对文件签名。
Alice将签名的文件传送给Bob。
Bob用Alice的公钥解密文件,从而验证签名
Alice将签名的文件传送给Bob。
Bob用Alice的公钥解密文件,从而验证签名
HTTP API 认证授权术
HTTP Basic
技术原理
把 username和 password 做成 username:password 的样子(用冒号分隔)
进行 Base64 编码。Base64("username:password") 得到一个字符串(如:把 haoel:coolshell 进行 base64 后可以得到 aGFvZW86Y29vbHNoZWxsCg )
把 aGFvZW86Y29vbHNoZWxsCg 放到 HTTP 头中 Authorization 字段中,形成 Authorization: Basic aGFvZW86Y29vbHNoZWxsCg,然后发送到服务端。
服务端如果没有在头里看到认证字段,则返回 401 错,以及一个个 WWW-Authenticate: Basic Realm='HelloWorld' 之类的头要求客户端进行认证。之后如果没有认证通过,则返回一个 401 错。如果服务端认证通过,那么会返回 200。
进行 Base64 编码。Base64("username:password") 得到一个字符串(如:把 haoel:coolshell 进行 base64 后可以得到 aGFvZW86Y29vbHNoZWxsCg )
把 aGFvZW86Y29vbHNoZWxsCg 放到 HTTP 头中 Authorization 字段中,形成 Authorization: Basic aGFvZW86Y29vbHNoZWxsCg,然后发送到服务端。
服务端如果没有在头里看到认证字段,则返回 401 错,以及一个个 WWW-Authenticate: Basic Realm='HelloWorld' 之类的头要求客户端进行认证。之后如果没有认证通过,则返回一个 401 错。如果服务端认证通过,那么会返回 200。
最大的问题
把用户名和口令放在网络上传
改进
一般要配合 TLS/SSL 的安全加密方式来使用
Digest Access
基本思路
请求方把用户名口令和域做一个 MD5 – MD5(username:realm:password) 然后传给服务器,这样就不会在网上传用户名和口令了,
但是,因为用户名和口令基本不会变,所以,这个 MD5 的字符串也是比较固定的,
因此,这个认证过程在其中加入了两个事,一个是 nonce 另一个是 qop
但是,因为用户名和口令基本不会变,所以,这个 MD5 的字符串也是比较固定的,
因此,这个认证过程在其中加入了两个事,一个是 nonce 另一个是 qop
过程
首先,调用方发起一个普通的 HTTP 请求。
比如:GET /coolshell/admin/ HTTP/1.1
服务端自然不能认证能过,
服务端返回 401 错误,
并且在 HTTP 头里的 WWW-Authenticate 包含如下信息
比如:GET /coolshell/admin/ HTTP/1.1
服务端自然不能认证能过,
服务端返回 401 错误,
并且在 HTTP 头里的 WWW-Authenticate 包含如下信息
WWW-Authenticate: Digest realm="testrealm@host.com",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
其中的 nonce 为服务器端生成的随机数,然后,
客户端做 HASH1=MD5(MD5(username:realm:password):nonce:cnonce) ,
其中的 cnonce 为客户端生成的随机数,这样就可以使得整个 MD5 的结果是不一样的
客户端做 HASH1=MD5(MD5(username:realm:password):nonce:cnonce) ,
其中的 cnonce 为客户端生成的随机数,这样就可以使得整个 MD5 的结果是不一样的
如果 qop 中包含了 auth ,那么还得做 HASH2=MD5(method:digestURI)
其中的 method 就是HTTP的请求方法(GET/POST…),digestURI 是请求的URL
其中的 method 就是HTTP的请求方法(GET/POST…),digestURI 是请求的URL
如果 qop 中包含了 auth-init ,那么,
得做 HASH2=MD5(method:digestURI:MD5(entityBody))
其中的 entityBody 就是HTTP请求的整个数据体
得做 HASH2=MD5(method:digestURI:MD5(entityBody))
其中的 entityBody 就是HTTP请求的整个数据体
然后,得到 response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)
如果没有 qop 则 response = MD5(HA1:nonce:HA2)
如果没有 qop 则 response = MD5(HA1:nonce:HA2)
最后,我们的客户端对服务端发起如下请求—— 注意HTTP头的 Authorization: Digest
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="%2Fcoolshell%2Fadmin",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="%2Fcoolshell%2Fadmin",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
优点和缺陷
优点
没有在网上传递用户的密码,而只是把密码的 MD5 传送过去,相对会比较安全,而且,其并不需要是否 TLS/SSL 的安全链接
缺陷
整个过程其实关键是用户的 password,这个 password 如果不够得杂,其实是可以被暴力破解的
整个过程是非常容易受到中间人攻击——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式(RFC2069)
App Secret Key + HMAC
HMAC
这个东西来自于 MAC – Message Authentication Code,是一种用于给消息签名的技术,也就是说,我们怕消息在传递的过程中被人修改,所以,我们需要用对消息进行一个 MAC 算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个 MAC 字符串,如果一致,则表明没有被修改过(整个过程参看下图)。而 HMAC – Hash-based Authenticsation Code,指的是利用 Hash 技术完成这一工作,比如:SHA-256算法
图示
App ID
这个东西跟验证没有关系,只是用来区分,是谁来调用 API 的,就像我们每个人的身份证一样,只是用来标注不同的人,不是用来做身份认证的。与前面的不同之处是,这里,我们需要用 App ID 来映射一个用于加密的密钥,这样一来,我们就可以在服务器端进行相关的管理,我们可以生成若干个密钥对(AppID, AppSecret),并可以有更细粒度的操作权限管理
把 AppID 和 HMAC 用于 API 认证
目前来说,玩得最好最专业的应该是 AWS 了,我们可以通过 S3 的 API 请求签名文档看到 AWS 是怎么玩的。整个过程还是非常复杂的,可以通过下面的图片流程看个大概。基本上来说,分成如下几个步骤
过程图示
最后,发出 HTTP Request 时,在 HTTP 头的 Authorization 字段中放入如下的信息
Authorization: AWS4-HMAC-SHA256
Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request,
SignedHeaders=content-type;host;x-amz-date,
Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request,
SignedHeaders=content-type;host;x-amz-date,
Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
AKIDEXAMPLE 是 AWS Access Key ID, 也就是所谓的 AppID,服务器端会根据这个 AppID 来查相关的 Secret Access Key,
然后再验证签名。如果,你对这个过程有点没看懂的话,你可以读一读这篇文章——
《Amazon S3 Rest API with curl》这篇文章里有好些代码,代码应该是最有细节也是最准确的了
然后再验证签名。如果,你对这个过程有点没看懂的话,你可以读一读这篇文章——
《Amazon S3 Rest API with curl》这篇文章里有好些代码,代码应该是最有细节也是最准确的了
不好的地方
这个东西没有标准 ,
所以,各家的实现很不一致。比如:Acquia 的 HMAC,微信的签名算法
(这里,我们需要说明一下,微信的 API 没有遵循HTTP 协议的标准,
把认证信息放在 HTTP 头的 Authorization 里,而是放在 body 里
所以,各家的实现很不一致。比如:Acquia 的 HMAC,微信的签名算法
(这里,我们需要说明一下,微信的 API 没有遵循HTTP 协议的标准,
把认证信息放在 HTTP 头的 Authorization 里,而是放在 body 里
JWT – JSON Web Tokens
简介
JWT 是一个比较标准的认证解决方案,
这个技术在 Java 圈里应该用的是非常普遍的。
JWT 签名也是一种 MAC(Message Authentication Code)的方法
这个技术在 Java 圈里应该用的是非常普遍的。
JWT 签名也是一种 MAC(Message Authentication Code)的方法
JWT 的签名流程
服务器端
1、用户使用用户名和口令到认证服务器上请求认证。
2、认证服务器验证用户名和口令后,以服务器端生成 JWT Token,这个 token 的生成过程如下:
2.1、认证服务器会生成一个 Secret Key(密钥)
2.2、对JWT Header和 JWT Payload 分别求 Base64。在 Payload 可能包括了用户的抽象 ID 和的过期时间。
2.3、用密钥对 JWT 签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));
3、然后把 base64(header).base64(payload).signature 作为 JWT token 返回客户端。
4、客户端使用 JWT Token 向应用服务器发送相关的请求。这个 JWT Token 就像一个临时用户权证一样。
2、认证服务器验证用户名和口令后,以服务器端生成 JWT Token,这个 token 的生成过程如下:
2.1、认证服务器会生成一个 Secret Key(密钥)
2.2、对JWT Header和 JWT Payload 分别求 Base64。在 Payload 可能包括了用户的抽象 ID 和的过期时间。
2.3、用密钥对 JWT 签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));
3、然后把 base64(header).base64(payload).signature 作为 JWT token 返回客户端。
4、客户端使用 JWT Token 向应用服务器发送相关的请求。这个 JWT Token 就像一个临时用户权证一样。
客户端
1、应用服务会检查 JWT Token,确认签名是正确的。
2、然而,因为只有认证服务器有这个用户的 Secret Key(密钥),所以,应用服务器得把 JWT Token 传给认证服务器。
3、认证服务器通过 JWT Payload 解出用户的抽象 ID,然后通过抽象 ID 查到登录时生成的 Secret Key,然后再来检查一下签名。
4、认证服务器检查通过后,应用服务就可以认为这是合法请求了。
2、然而,因为只有认证服务器有这个用户的 Secret Key(密钥),所以,应用服务器得把 JWT Token 传给认证服务器。
3、认证服务器通过 JWT Payload 解出用户的抽象 ID,然后通过抽象 ID 查到登录时生成的 Secret Key,然后再来检查一下签名。
4、认证服务器检查通过后,应用服务就可以认为这是合法请求了。
说明
上面的这个过程,是在认证服务器上为用户动态生成 Secret Key 的,
应用服务在验签的时候,需要到认证服务器上去签,这个过程增加了一些网络调用,
所以,JWT 除了支持 HMAC-SHA256 的算法外,还支持 RSA 的非对称加密的算法
应用服务在验签的时候,需要到认证服务器上去签,这个过程增加了一些网络调用,
所以,JWT 除了支持 HMAC-SHA256 的算法外,还支持 RSA 的非对称加密的算法
使用 RSA 非对称算法,在认证服务器这边放一个私钥,在应用服务器那边放一个公钥,
认证服务器使用私钥加密,应用服务器使用公钥解密,
这样一来,就不需要应用服务器向认证服务器请求了,
认证服务器使用私钥加密,应用服务器使用公钥解密,
这样一来,就不需要应用服务器向认证服务器请求了,
但是,RSA 是一个很慢的算法,所以,虽然你省了网络调用,但是却费了 CPU,
尤其是Header 和 Payload 比较长的时候。
尤其是Header 和 Payload 比较长的时候。
所以,一种比较好的玩法是,如果我们把 header 和 payload 简单地做 SHA256,这会很快,
然后,我们用 RSA 加密这个 SHA256 出来的字符串,这样一来,RSA 算法就比较快了,
而我们也做到了使用 RSA 签名的目的。
然后,我们用 RSA 加密这个 SHA256 出来的字符串,这样一来,RSA 算法就比较快了,
而我们也做到了使用 RSA 签名的目的。
最后,我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了
OAuth 1.0 –
3 legged & 2 legged
3 legged & 2 legged
起源
用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片,
但是,用户不想把自己的用户名和口令交给那个第三方的网络打印服务,
但又想让那个第三方的网络打印服务来访问自己的照片,
为了解决这个授权的问题, OAuth 这个协议就出来了
但是,用户不想把自己的用户名和口令交给那个第三方的网络打印服务,
但又想让那个第三方的网络打印服务来访问自己的照片,
为了解决这个授权的问题, OAuth 这个协议就出来了
过程
这个协议有三个角色
User(照片所有者-用户)
Consumer(第三方照片打印服务)
Service Provider(照片存储服务)
Consumer(第三方照片打印服务)
Service Provider(照片存储服务)
这个协义有三个阶段
Consumer 获取 Request Token
Service Provider 认证用户并授权 Consumer
Consumer 获取 Access Token 调用 API 访问用户的照片
Service Provider 认证用户并授权 Consumer
Consumer 获取 Access Token 调用 API 访问用户的照片
授权过程
Consumer(第三方照片打印服务)需要先上 Service Provider 获得开发的 Consumer Key 和 Consumer Secret
当 User 访问 Consumer 时,Consumer 向 Service Provide 发起请求请求Request Token (需要对HTTP请求签名)
Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后,使用 HTTP GET 请求把 User 切到 Service Provide 的认证页上(其中带上Request Token),让用户输入他的用户和口令。
Service Provider 认证 User 成功后,跳回 Consumer,并返回 Request Token (oauth_token)和 Verification Code(oauth_verifier)
接下来就是签名请求,用 Request Token 和 Verification Code 换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用 Access Token 访问用户授权访问的资源。
当 User 访问 Consumer 时,Consumer 向 Service Provide 发起请求请求Request Token (需要对HTTP请求签名)
Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后,使用 HTTP GET 请求把 User 切到 Service Provide 的认证页上(其中带上Request Token),让用户输入他的用户和口令。
Service Provider 认证 User 成功后,跳回 Consumer,并返回 Request Token (oauth_token)和 Verification Code(oauth_verifier)
接下来就是签名请求,用 Request Token 和 Verification Code 换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用 Access Token 访问用户授权访问的资源。
授权过程流程图
因为上面这个流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三脚流程
衍生
OAuth 1.0 也有不需要用户参与的,只有 Consumer 和 Service Provider 的, 也就是 2-legged flow 两脚流程,其中省掉了用户认证的事
过程
Consumer(第三方照片打印服务)需要先上 Service Provider 获得开发的 Consumer Key 和 Consumer Secret
Consumer 向 Service Provide 发起请求请求 Request Token (需要对 HTTP 请求签名)
Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后,直接换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用 Access Token 访问用户授权访问的资源。
Consumer 向 Service Provide 发起请求请求 Request Token (需要对 HTTP 请求签名)
Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret)
Consumer 收到 Request Token 后,直接换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
最后使用 Access Token 访问用户授权访问的资源。
OAuth 中的签名
我们可以看到,有两个密钥,一个是 Consumer 注册 Service Provider 时由 Provider 颁发的 Consumer Secret,另一个是 Token Secret。
签名密钥就是由这两具密钥拼接而成的,其中用 & 作连接符。假设 Consumer Secret 为 j49sk3j29djd 而 Token Secret 为 dh893hdasih9 那个,签名密钥为:j49sk3j29djd&dh893hdasih9
在请求 Request/Access Token 的时候需要对整个 HTTP 请求进行签名(使用 HMAC-SHA1 和 HMAC-RSA1 签名算法),请求头中需要包括一些 OAuth 需要的字段,如:
Consumer Key :也就是所谓的 AppID
Token:Request Token 或 Access Token
Signature Method :签名算法比如:HMAC-SHA1
Timestamp:过期时间
Nonce:随机字符串
Call Back:回调 URL
签名密钥就是由这两具密钥拼接而成的,其中用 & 作连接符。假设 Consumer Secret 为 j49sk3j29djd 而 Token Secret 为 dh893hdasih9 那个,签名密钥为:j49sk3j29djd&dh893hdasih9
在请求 Request/Access Token 的时候需要对整个 HTTP 请求进行签名(使用 HMAC-SHA1 和 HMAC-RSA1 签名算法),请求头中需要包括一些 OAuth 需要的字段,如:
Consumer Key :也就是所谓的 AppID
Token:Request Token 或 Access Token
Signature Method :签名算法比如:HMAC-SHA1
Timestamp:过期时间
Nonce:随机字符串
Call Back:回调 URL
签名的示意图
OAuth 2.0 –
Authentication Code & Client Credential
Authentication Code & Client Credential
起源
在前面,我们可以看到,从 Digest Access, 到 AppID+HMAC,再到 JWT,再到 OAuth 1.0,这些个 API 认证都是要向 Client发一个密钥(或是用密码)然后用 HASH 或是 RSA 来签 HTTP 的请求,这其中有个主要的原因是,以前的 HTTP 是明文传输,所以,在传输过程中很容易被篡改,于是才搞出来一套的安全签名机制,所以,这些个认证的玩法是可以在 HTTP 明文协议下玩的。
这种使用签名方式大家可以看到是比较复杂的,所以,对于开发者来说,也是很不友好的,在组织签名的那些 HTTP 报文的时候,各种,URLEncode 和 Base64,还要对 Query 的参数进行排序,然后有的方法还要层层签名,非常容易出错,另外,这种认证的安全粒度比较粗,授权也比较单一,对于有终端用户参与的移动端来说也有点不够。所以,在 2012 年的时候,OAuth 2.0 的 RFC 6749 正式放出。
这种使用签名方式大家可以看到是比较复杂的,所以,对于开发者来说,也是很不友好的,在组织签名的那些 HTTP 报文的时候,各种,URLEncode 和 Base64,还要对 Query 的参数进行排序,然后有的方法还要层层签名,非常容易出错,另外,这种认证的安全粒度比较粗,授权也比较单一,对于有终端用户参与的移动端来说也有点不够。所以,在 2012 年的时候,OAuth 2.0 的 RFC 6749 正式放出。
特性
OAuth 2.0 依赖于 TLS/SSL 的链路加密技术(HTTPS),完全放弃了签名的方式,
认证服务器再也不返回什么 token secret 的密钥了,所以,OAuth 2.0 是完全不同于 1.0 的,也是不兼容的。
认证服务器再也不返回什么 token secret 的密钥了,所以,OAuth 2.0 是完全不同于 1.0 的,也是不兼容的。
目前,Facebook 的 Graph API 只支持 OAuth 2.0协议,Google 和 Microsoft Azure 也支持Auth 2.0,国内的微信和支付宝也支持使用 OAuth 2.0
OAuth 2.0 的两个主要的 Flow
一个是 Authorization Code Flow, 这个是 3 legged 的
一个是 Client Credential Flow,这个是 2 legged 的
一个是 Client Credential Flow,这个是 2 legged 的
Authorization Code Flow
Authorization Code 是最常使用的 OAuth 2.0 的授权许可类型,它适用于用户给第三方应用授权访问自己信息的场景。
这个 Flow 也是 OAuth 2.0 四个 Flow 中我个人觉得最完整的一个 Flow,其流程图如下所示。
这个 Flow 也是 OAuth 2.0 四个 Flow 中我个人觉得最完整的一个 Flow,其流程图如下所示。
示意图
流程的一个细节上的解释
当用户(Resource Owner)访问第三方应用(Client)的时候,
第三方应用会把用户带到认证服务器(Authorization Server)上去,
主要请求的是 /authorize API,其中的请求方式如下所示。
第三方应用会把用户带到认证服务器(Authorization Server)上去,
主要请求的是 /authorize API,其中的请求方式如下所示。
https://login.authorization-server.com/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
&scope=read
&state=xcoiv98CoolShell3kch
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
&scope=read
&state=xcoiv98CoolShell3kch
client_id 为第三方应用的 App ID
response_type=code 为告诉认证服务器,我要走 Authorization Code Flow。
redirect_uri 意思是我跳转回第三方应用的 URL
scope 意是相关的权限
state 是一个随机的字符串,主要用于防 CSRF 攻击。
response_type=code 为告诉认证服务器,我要走 Authorization Code Flow。
redirect_uri 意思是我跳转回第三方应用的 URL
scope 意是相关的权限
state 是一个随机的字符串,主要用于防 CSRF 攻击。
当 Authorization Server 收到这个 URL 请求后,其会通过 client_id 来检查 redirect_uri 和 scope 是否合法,
如果合法,则弹出一个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。
如果合法,则弹出一个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。
当用户授权同意访问以后,Authorization Server 会跳转回 Client ,
并以其中加入一个 Authorization Code。如下所示:
并以其中加入一个 Authorization Code。如下所示:
https://example-client.com/callback?
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&state=xcoiv98CoolShell3kch
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&state=xcoiv98CoolShell3kch
请流动的链接是第 1)步中的 redirect_uri
其中的 state 的值也和第 1)步的 state一样。
其中的 state 的值也和第 1)步的 state一样。
接下来,Client 就可以使用 Authorization Code 获得 Access Token。
其需要向 Authorization Server 发出如下请求。
其需要向 Authorization Server 发出如下请求。
POST /oauth/token HTTP/1.1
Host: authorization-server.com
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&grant_type=code
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
&client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
Host: authorization-server.com
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&grant_type=code
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
&client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
如果没什么问题,Authorization 会返回如下信息
{
"access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
"refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
"token_type": "bearer",
"expires": 3600,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
}
"access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
"refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
"token_type": "bearer",
"expires": 3600,
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
}
access_token 就是访问请求令牌了
refresh_token 用于刷新 access_token
id_token 是 JWT 的 token,其中一般会包含用户的 OpenID
refresh_token 用于刷新 access_token
id_token 是 JWT 的 token,其中一般会包含用户的 OpenID
接下来就是用 Access Token 请求用户的资源了
GET /v1/user/pictures
Host: https://example.resource.com
Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI
Host: https://example.resource.com
Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI
Client Credential Flow
Client Credential 是一个简化版的 API 认证,主要是用于认证服务器到服务器的调用,也就是没有用户参与的的认证流程。下面是相关的流程图
流程图
本质上就是 Client 用自己的 client_id 和 client_secret 向Authorization Server 要一个 Access Token,然后使用 Access Token 访问相关的资源
请求示例
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=czZCaGRSa3F0Mzpn
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=czZCaGRSa3F0Mzpn
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
返回示例
{
"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope":"create"
}
"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope":"create"
}
微信示例
微信公从平台的开发文档中,使用了 OAuth 2.0 的 Client Credentials 的方式(参看文档“微信公众号获取 access token”),我截了个图如下所谓。我们可以看到,微信公众号使用的是 GET 方式的请求,把 AppID 和 AppSecret 放在了 URL中,虽然这也符合 OAuth 2.0,但是并不好,因为大多数网关代理会把整个 URI 请求记到日志中。我们只要脑补一下腾讯的网关的 Access Log,里面的日志一定会有很多的各个用户的AppID 和 AppSecret……
小结
两个概念和三个术语
区分两个概念:Authentication(认证) 和 Authorization (授权),前者是证明请求者是身份,就像身份证一样,后者是为了获得权限。身份是区别于别人的证明,而权限是证明自己的特权。Authentication 为了证明操作的这个人就是他本人,需要提供密码、短信验证码,甚至人脸识别。Authorization 则是不需要在所有的请求都需要验人,是在经过 Authorization 后得到一个 Token,这就是 Authorization。就像护照和签证一样
区分三个概念:编码 Base64Encode、签名 HMAC、加密 RSA。
编码是为了更的传输,等同于明文,
签名是为了信息不能被篡改,
加密是为了不让别人看到是什么信息。
编码是为了更的传输,等同于明文,
签名是为了信息不能被篡改,
加密是为了不让别人看到是什么信息。
初衷
使用复杂地 HMAC 哈希签名方式主要是应对当年没有 TLS/SSL 加密链路的情况。
JWT 把 uid 放在 Token 中目的是为了去掉状态,但不能让用户修改,所以需要签名。
OAuth 1.0 区分了两个事,一个是第三方的 Client,一个是真正的用户,其先拿 Request Token,再换 Access Token 的方法主要是为了把第三方应用和用户区分开来。
用户的 Password 是用户自己设置的,复杂度不可控,服务端颁发的 Serect 会很复杂,但主要目的是为了容易管理,可以随时注销掉。
OAuth 协议有比所有认证协议有更为灵活完善的配置,如果使用 AppID/AppSecret 签名的方式,又需要做到可以有不同的权限和可以随时注销,那么你得开发一个像 AWS 的 IAM 这样的账号和密钥对管理的系统。
JWT 把 uid 放在 Token 中目的是为了去掉状态,但不能让用户修改,所以需要签名。
OAuth 1.0 区分了两个事,一个是第三方的 Client,一个是真正的用户,其先拿 Request Token,再换 Access Token 的方法主要是为了把第三方应用和用户区分开来。
用户的 Password 是用户自己设置的,复杂度不可控,服务端颁发的 Serect 会很复杂,但主要目的是为了容易管理,可以随时注销掉。
OAuth 协议有比所有认证协议有更为灵活完善的配置,如果使用 AppID/AppSecret 签名的方式,又需要做到可以有不同的权限和可以随时注销,那么你得开发一个像 AWS 的 IAM 这样的账号和密钥对管理的系统。
注意事项
无论是哪种方式,我们都应该遵循 HTTP 的规范,把认证信息放在 Authorization HTTP 头中。
不要使用 GET 的方式在 URL 中放入 secret 之类的东西,因为很多 proxy 或 gateway 的软件会把整个 URL 记在 Access Log 文件中。
密钥 Secret 相当于 Password,但他是用来加密的,最好不要在网络上传输,如果要传输,最好使用 TLS/SSL 的安全链路。
HMAC 中无论是 MD5 还是 SHA1/SHA2,其计算都是非常快的,RSA 的非对称加密是比较耗 CPU 的,尤其是要加密的字符串很长的时候。
最好不要在程序中 hard code 你的 Secret,因为在 github 上有很多黑客的软件在监视各种 Secret,千万小心!这类的东西应该放在你的配置系统或是部署系统中,在程序启动时设置在配置文件或是环境变量中。
使用 AppID/AppSecret,还是使用 OAuth1.0a,还是 OAuth2.0,还是使用 JWT,我个人建议使用 TLS/SSL 下的 OAuth 2.0。
密钥是需要被管理的,管理就是可以新增可以撤销,可以设置账户和相关的权限。最好密钥是可以被自动更换的。
认证授权服务器(Authorization Server)和应用服务器(App Server)最好分开。
不要使用 GET 的方式在 URL 中放入 secret 之类的东西,因为很多 proxy 或 gateway 的软件会把整个 URL 记在 Access Log 文件中。
密钥 Secret 相当于 Password,但他是用来加密的,最好不要在网络上传输,如果要传输,最好使用 TLS/SSL 的安全链路。
HMAC 中无论是 MD5 还是 SHA1/SHA2,其计算都是非常快的,RSA 的非对称加密是比较耗 CPU 的,尤其是要加密的字符串很长的时候。
最好不要在程序中 hard code 你的 Secret,因为在 github 上有很多黑客的软件在监视各种 Secret,千万小心!这类的东西应该放在你的配置系统或是部署系统中,在程序启动时设置在配置文件或是环境变量中。
使用 AppID/AppSecret,还是使用 OAuth1.0a,还是 OAuth2.0,还是使用 JWT,我个人建议使用 TLS/SSL 下的 OAuth 2.0。
密钥是需要被管理的,管理就是可以新增可以撤销,可以设置账户和相关的权限。最好密钥是可以被自动更换的。
认证授权服务器(Authorization Server)和应用服务器(App Server)最好分开。
软件license/开源风险
开源软件License汇总
允许商业集成且没有开源风险的许可证
BSD license
X11 license 即MIT license
FreeBSD
MIT license
Apache license
Openssl license
Zope Public license
boost software license V1
Python license
Zlib license
OpenBSD license
Code Project Open License (CPOL)
X11 license 即MIT license
FreeBSD
MIT license
Apache license
Openssl license
Zope Public license
boost software license V1
Python license
Zlib license
OpenBSD license
Code Project Open License (CPOL)
修改后源代码需要公开的许可证
NOSL V1.0
IBM public license V 1.0
Sun Public License
Nokia Open Source License
NPL
CDDL
EPL V1
Common Public License V1
Mozilla Public License
Creative Commons Public Domain License
IBM public license V 1.0
Sun Public License
Nokia Open Source License
NPL
CDDL
EPL V1
Common Public License V1
Mozilla Public License
Creative Commons Public Domain License
传染性/商业不友好软件
GNU GPL
Qt Non Commercial License
LGPL
Agent++ license
HP Software license terms
Sun Microsystems,Inc.Software License Agreement
Qt Non Commercial License
LGPL
Agent++ license
HP Software license terms
Sun Microsystems,Inc.Software License Agreement
GPL
GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,
但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。
GPL 带有很强的传染性,那么如果一个库使用GPL发布,那么使用这个库的所有软件也必须使用GPL发布
但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。
GPL 带有很强的传染性,那么如果一个库使用GPL发布,那么使用这个库的所有软件也必须使用GPL发布
六种License直观图
waf证书(Web应用防火墙(WAF))
监控网站上的HTTP/HTTPS访问请求,
并通过自定义过滤规则和启用Web攻击防护等功能,
帮助您部署网站访问控制
并通过自定义过滤规则和启用Web攻击防护等功能,
帮助您部署网站访问控制
路径跳转
软件基础知识
SoftwareBasic
SoftwareBasic
Java学习笔记
Java_Practice
Java_Practice
Go学习笔记
GO_Practice
GO_Practice
高效开发工具
EfficientDevTools
EfficientDevTools
数据结构和算法
AlgorithmPractice
AlgorithmPractice
机器学习
高可用/高并发/高性能
解决方案(3H)
解决方案(3H)
数据库(原理和指令)
中间件(指令和原理)
踩坑记录(复盘和总结)
工作项目UML图汇总
开发语言
Linux
Linux环境
ubuntu的使用
VMware虚拟机
VMware虚拟机下载与安装
VMware10.0.0版本下载地址+安装教程+许可证密钥
镜像安装
VMware虚拟机安装Ubuntu16-18系统超详细过程(含下载地址)
win10上Hyper-V安装ubuntu18.04
不建议
ubuntu基础知识
update是更新软件列表,upgrade是更新软件
ubuntu安装问题集锦
VMware Player 与 Device/Credential Guard 不兼容。
在禁用 Device/Credential Guard 后,可以运行 VMware Player。
在禁用 Device/Credential Guard 后,可以运行 VMware Player。
以管理员身份运行命令提示符,运行以下命令:
bcdedit /set hypervisorlaunchtype off
重启系统后再次运行VMware Player,就不会再出现与Device/Credential Guard不兼容的错误提示了。
bcdedit /set hypervisorlaunchtype off
重启系统后再次运行VMware Player,就不会再出现与Device/Credential Guard不兼容的错误提示了。
VMware Workstation pro无法在Windows上运行,检查可在Windows上运行的此应用的更新版本
此条仅供参考,实际情况不一定
ubuntu提示Software database is broken解决办法
sudo apt-get install -f
注意
win10需要安装最新版的vmware,注册码使用KeyGen生成的注册码,切记
vmware tools 安装
Ubuntu提示:正在进行简易安装时 无法手动启动 vmware tools 安装
1、关闭虚拟机
2、设置虚拟机,把软驱和光驱都设置为物理软驱或者物理光驱,可自动检测
3、启动虚拟机
4、在vmware的虚拟机菜单下启动安装vmware tools
5、进入虚拟机,在光驱下有一个tar文件,把它解压到某个目录下tar -zxvf 文件名
6、运行安装程序 ./程序名字
安装过程会有提示,直接enter 就可以了
7、重启虚拟机,一切ok
2、设置虚拟机,把软驱和光驱都设置为物理软驱或者物理光驱,可自动检测
3、启动虚拟机
4、在vmware的虚拟机菜单下启动安装vmware tools
5、进入虚拟机,在光驱下有一个tar文件,把它解压到某个目录下tar -zxvf 文件名
6、运行安装程序 ./程序名字
安装过程会有提示,直接enter 就可以了
7、重启虚拟机,一切ok
第六条 sudo vmware-install.pl
输入法
VMware上Ubuntu安装搜狗输入法
Ubuntu如何安装搜狗输入法
Ubuntu输入法框架(Keyboard input method system)设置后无法保存
执行命令 rm ~/.xinputrc,再去选择保存就ok了
ubuntu怎么显示右上角没有小键盘?
运行命令:im-config后,点击OK
找出输入法设置
fcitx-config-gtk3
窗口全屏
vmware虚拟机屏幕如何适应窗口全屏
第一步: 安装好 vmware tools
第二步: VMware搭建的Ubuntu无法全屏
System Settings
Displays
1360x768
apply
Ubuntu 搭建Samba服务器
原理
Samba服务的配置总结
comment:配置描述
past:共享的文件路径
browseable:是否可以浏览
read only:是否只读
create mask:创建文件掩码,与的关系,因为Windows下创建文件默认带有可执行权限,
你不想一个文本文件也有可执行权限吧?所以这里配置为0664也就是-rw-rw-r--。
directory mask:创建文件夹掩码,文件夹需要有x权限,否则其他用户无法进入,这里配置为0775,也就是drwxrwxr-x。
valid users:有效用户是其所有者(valid users = %S)
past:共享的文件路径
browseable:是否可以浏览
read only:是否只读
create mask:创建文件掩码,与的关系,因为Windows下创建文件默认带有可执行权限,
你不想一个文本文件也有可执行权限吧?所以这里配置为0664也就是-rw-rw-r--。
directory mask:创建文件夹掩码,文件夹需要有x权限,否则其他用户无法进入,这里配置为0775,也就是drwxrwxr-x。
valid users:有效用户是其所有者(valid users = %S)
安装步骤
sudo apt-get install samba
samba
准备smb.conf
配置访问的用户名和显示的路径名
sudo smbpasswd -a 用户名
sudo service smbd restart
报错
debian samba出错:set_variable_helper(yes ): value is not boolean!
是配置参数中有问题。逐行删除确认即可
Samba配置,启动失败报错:Job for smb.service failed because the control process exited with error code.
安装了Samba之后,不能从Windows向linux拷贝文件,但能从liunx向Windows拷贝文件.提示没有权限!
在smb.conf里加上writable = yes
把共享目录加上写权限 chmod 777 xxx
把共享目录加上写权限 chmod 777 xxx
samba命令
service smb status
启动samba服务
/etc/init.d/samba start
需要找到init.d 这个文件,这下面有很多server
不同系统叫的samba名称不一样
service smbd start
开机启动samba
Redhat 或者 Centos 配置开机自启动samba的图形配置界面工具为 ntsysv
Ubuntu 没有这个命令,但是有替代的命令,功能类似,命令为 sysv-rc-conf
$ apt-get install -y sysv-rc-conf
$ sysv-rc-conf (必要时可以加sudo)
配置 smbd samba nmbd 服务 等级 1到5
在显示的页面上,用鼠标点或者空格键,将1-5的小方格设置为X,就开机自启动了
Ubuntu 没有这个命令,但是有替代的命令,功能类似,命令为 sysv-rc-conf
$ apt-get install -y sysv-rc-conf
$ sysv-rc-conf (必要时可以加sudo)
配置 smbd samba nmbd 服务 等级 1到5
在显示的页面上,用鼠标点或者空格键,将1-5的小方格设置为X,就开机自启动了
Ubuntu搭建httpd
ubuntu使用问题集锦
E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
杀死所有的apt进程
ps afx|grep apt
kill -9 xx
删除锁定文件
sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a
sudo apt update
sudo dpkg --configure -a
sudo apt update
网络
LINUX开启ssh服务,报错:
ssh: connect to host 192.168.6.129 port 22: Connection refused
ssh: connect to host 192.168.6.129 port 22: Connection refused
sudo apt-get install openssh-server
联网问题
网络重新连接
Ubuntu16.04设置静态IP后无法联网
/etc/network/interfaces
/etc/resolvconf/resolv.conf.d/base
/etc/NetworkManager/NetworkManager.conf
最后重启网络
方式一
service network-manager restart
方式二
/etc/init.d/networking restart
ubuntu固定IP
主机与ubuntu16.04,ping不通的解决方法
主机与ubuntu16.04,ping不通的解决方法
先设置主机网络
设置ubuntu网卡,重启网卡
sudo vi /etc/network/interfaces
sudo vim /etc/resolvconf/resolv.conf.d/base
设置dns
sudo /etc/init.d/networking restart
主机可以ping通虚拟机时,
但虚拟机ping不通主机 的问题
但虚拟机ping不通主机 的问题
1. 打开主机的(WIN10)防火墙
2. 选择高级设置
3.入站规则
4. 找到配置文件类型为“公用”的“文件和打印共享(回显请求 – ICMPv4-In)”规则,启用规
2. 选择高级设置
3.入站规则
4. 找到配置文件类型为“公用”的“文件和打印共享(回显请求 – ICMPv4-In)”规则,启用规
防火墙
Ubuntu18.04.2LTS 如何查看、打开及关闭防火墙
防火墙状态查看
ufw status verbose #非管理员需加sudo即可
激活防火墙:
ufw enable #非管理员需加sudo即可
关闭防火墙
ufw disable #非管理员需加sudo即可
vim
安装vim
sudo apt-get install vim-gtk
验证
输入vi 按tab键,显示有vim
VIM的配置
sudo vim /etc/vim/vimrc
syntax on
意思是语法高亮,如果您的被注释掉了,请“让它出来”。就像下图所示
意思是语法高亮,如果您的被注释掉了,请“让它出来”。就像下图所示
请在您的VIM的最后一行,输入他们,可以让您的VIM变得更漂亮、舒服。
set nu // 在左侧行号
set tabstop //tab 长度设置为 4
set nobackup //覆盖文件时不备份
set cursorline //突出显示当前行
set ruler //在右下角显示光标位置的状态行
set autoindent //自动缩进
set nu // 在左侧行号
set tabstop //tab 长度设置为 4
set nobackup //覆盖文件时不备份
set cursorline //突出显示当前行
set ruler //在右下角显示光标位置的状态行
set autoindent //自动缩进
vim鼠标不能右键粘贴、跨系统复制粘贴
方法一:
:set mouse-=a
方法二:
if has('mouse')
set mouse-=a
endif
set mouse-=a
endif
查看vim 版本
vim --version
vim快速移动光标至行首和行尾
shift+6
shift+4
系统
system program problem detected
sudo rm /var/crash/*
sudo gedit /etc/default/apport
将其中的enable=1改为enable=0即可
将其中的enable=1改为enable=0即可
ubuntu打开终端(方法5种)
Ctrl+Alt+T打开终端
Ubuntu16.04修改主机名和查看主机名的方法
如何在ubuntu的桌面上双击执行py脚本
system program problem detected
关闭pop up功能
sudo gedit /etc/default/apport
将其中的enable=1改为enable=0即可
sudo gedit /etc/default/apport
将其中的enable=1改为enable=0即可
Errors were encountered while processing
cd /var/lib/dpkg
sudo mv info info.bak
sudo mkdir info
然后重新安装,就ok了
sudo mv info info.bak
sudo mkdir info
然后重新安装,就ok了
权限
su: Authentication failure问题
su命令不能切换root,提示su: Authentication failure,
只要你sudo passwd root过一次之后,
下次再su的时候只要输入密码就可以成功登录了
只要你sudo passwd root过一次之后,
下次再su的时候只要输入密码就可以成功登录了
使用
ubuntu重启、关机命令
重启命令 :
1、reboot
2、shutdown -r now 立刻重启
3、shutdown -r 10 过10分钟自动重启
4、shutdown -r 20:35 在时间为20:35时候重启
如果是通过shutdown命令设置重启的话,可以用shutdown -c命令取消重启
1、reboot
2、shutdown -r now 立刻重启
3、shutdown -r 10 过10分钟自动重启
4、shutdown -r 20:35 在时间为20:35时候重启
如果是通过shutdown命令设置重启的话,可以用shutdown -c命令取消重启
关机命令 :
1、halt 立刻关机(一般加-p 关闭电源)
2、poweroff 立刻关机
3、shutdown -h now 立刻关机
4、shutdown -h 10 10分钟后自动关机
1、halt 立刻关机(一般加-p 关闭电源)
2、poweroff 立刻关机
3、shutdown -h now 立刻关机
4、shutdown -h 10 10分钟后自动关机
查看python版本
Ubuntu16.04系统查看已安装的python版本,
及Python2与Python3之间切换
及Python2与Python3之间切换
python --version
ubuntu 查看系统服务的列表
service --status-all
Centos7的使用
Centos 7.2基础安装和配置(含分区方案建议)
更详细版
修改yum源
关闭防火墙
systemctl status firewalld.service
systemctl stop firewalld.service
systemctl disable firewalld.service
Mac的使用
Linux指令
把当前用户加入root权限组,能解决很多莫名奇妙的问题
Linux语法
linux中bin与sbin目录的作用及区别介绍
bin
bin为binary的简写,主要放置系统的必备执行文件,例如:
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar等
cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar等
/usr/bin:
主要放置应用程序工具的必备执行文件,例如:
c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、 gzip、htpasswd、kfm、ktop、
last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb*、wget等。
c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、 gzip、htpasswd、kfm、ktop、
last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb*、wget等。
/sbin:
主要放置系统管理的必备程序,例如:
cfdisk、dhcpcd、dump、e2fsck、fdisk、halt、ifconfig、ifup、 ifdown、init、insmod、
lilo、lsmod、mke2fs、modprobe、quotacheck、reboot、rmmod、 runlevel、shutdown等。
cfdisk、dhcpcd、dump、e2fsck、fdisk、halt、ifconfig、ifup、 ifdown、init、insmod、
lilo、lsmod、mke2fs、modprobe、quotacheck、reboot、rmmod、 runlevel、shutdown等。
/usr/sbin
主要放置网路管理的必备程序,例如:
dhcpd、httpd、imap、in.*d、inetd、lpd、named、netconfig、
nmbd、samba、sendmail、squid、swap、tcpd、tcpdump等
dhcpd、httpd、imap、in.*d、inetd、lpd、named、netconfig、
nmbd、samba、sendmail、squid、swap、tcpd、tcpdump等
for循环
reponame=$(ls ./)
for line in $reponame
do
cd $line
echo "======"$line"===status"
git status
cd ../
done
for line in $reponame
do
cd $line
echo "======"$line"===status"
git status
cd ../
done
for((i=1;i<=10;i++));
do
echo $(expr $i \* 3 + 1);
done
do
echo $(expr $i \* 3 + 1);
done
等待输入
read
if判断
read ans
if ["$ans" -eq "liuj"]; then
echo ok1
fi
if [[ $ans = *[[:digit]:] ]];then
echo ok
fi
if ["$ans" -eq "liuj"]; then
echo ok1
fi
if [[ $ans = *[[:digit]:] ]];then
echo ok
fi
数学运算
let
变量自增
定义及for循环内自增,
计算 let 的用法
定义及for循环内自增,
计算 let 的用法
list=$(cat repo.txt)
i=0
for line in $list
do
let i++
echo $line
done
echo $i
i=0
for line in $list
do
let i++
echo $line
done
echo $i
let a+=b (不需要$)
双括号 ((i++))
i=0
echo $i
((i++))
echo $i
echo $i
((i++))
echo $i
expr
echo $(expr $a + $b)
$[]
echo $[$a+$b]
linux无法直接进行小数运算,
需要使用bc命令
需要使用bc命令
Linux小数数值计算之bc命令
快速查询历史输入指令
ctrl+r
history
字符串切割的几种方法
${parameter//pattern/string}
echo liujun | tr "liu" "pan"
linux中如何声明和使用变量
变量名=变量值 , 注意=左右不能有空格,使用$变量名调用变量
查看变量:使用set命令可以查看所有变量,包括普通变量和环境变量
删除变量:使用unset变量名即可
注意
变量名由数字,字母和下划线组成,不能以数字开头,变量之中有空格用引号括起来
变量的拼接
拼接变量时注意$变量名要放在""内才有效
或者用 / 分隔开,比如$path/bin
将命令的执行结果赋值给变量
方法一:使用``(tab键上的符号)包住执行的命令
方法二:将命令放入$()内执行
linux中$()和${}的区别
$( )与` `(反引号)都是用来作命令替换的。
命令替换与变量替换差bai不多,都是用来重组命令du行的,
先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行
先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行
${ }变量替换,$var与${var}
Linux添加别名
临时设置,重启失效。
alias 自定义命令='命令或脚本语句'
临时设置,重启失效。
alias 自定义命令='命令或脚本语句'
永久设置(非全局配置别名)
修改配置文件:~/.bashrc
alias sqlmap='python /root/tools/sqlmap-master/sqlmap.py'
Linux基础指令
文件/文本
创建、删除文件
创建文件
touch jj.kk
touch aa/bb/cc
创建文件夹
mkdir abc
mkdir -p
递归创建目录,即使上级目录不存在,会按目录层级自动创建目录
删除文件夹/文件:
rm -rf abc
-d:直接把欲删除的目录的硬连接数据删除成0,删除该目录;
-f:强制删除文件或目录;
-i:删除已有文件或目录之前先询问用户;
-r或-R:递归处理,将指定目录下的所有文件与子目录一并处理;
--preserve-root:不对根目录进行递归操作;
-v:显示指令的详细执行过程。
-f:强制删除文件或目录;
-i:删除已有文件或目录之前先询问用户;
-r或-R:递归处理,将指定目录下的所有文件与子目录一并处理;
--preserve-root:不对根目录进行递归操作;
-v:显示指令的详细执行过程。
文件遍历及删除
mkdir tmp
tar xvf *.tar -C tmp
rm -rf $(ls tmp)
tar xvf *.tar -C tmp
rm -rf $(ls tmp)
不小心将压缩包解压在当前文件夹,导致目录很乱,需要删除解压出来的文件
第一句创建文件夹; 第二句将tar包压缩到指定目录tmp;
第三句删除tmp中ls列出来的所有文件。(加上-r-f可以不用每次询问)
第三句删除tmp中ls列出来的所有文件。(加上-r-f可以不用每次询问)
注意
每个语句后是否有 ^M, ‘$’\r 等奇怪的后缀,导致语句无法执行
删除所有以123456结尾的文件
for i in $(find ../ -name "*.123456")
do
echo $i
read #防止误删
rm -rf $i
done
do
echo $i
read #防止误删
rm -rf $i
done
或者 find . -name "*.123456" -exec rm -rf {} \;
解压/压缩文件
解压文件
tar 指令 、zip指令 和 rar 指令
tar –xvf file.tar //解压 tar包
tar -xzvf file.tar.gz //解压tar.gz
tar -xjvf file.tar.bz2 //解压 tar.bz2
tar –xZvf file.tar.Z //解压tar.Z
unrar e file.rar //解压rar
unzip file.zip //解压zip
tar -xzvf file.tar.gz //解压tar.gz
tar -xjvf file.tar.bz2 //解压 tar.bz2
tar –xZvf file.tar.Z //解压tar.Z
unrar e file.rar //解压rar
unzip file.zip //解压zip
压缩文件
tar -zcvf a.tar.gz a/
查看、修改文件
vim/cat
查看文件内容
nl 显示的时候,顺道输出行号
相当于cat -n a.txt
cat 由第一行开始显示文件内容
tac 从最后一行开始显示,可以看出 tac 是 cat 的倒着写
more 一页一页的显示文件内容
less 与 more 类似,但是比 more 更好的是,他可以往前翻页!
head 只看头几行
-n 5: 显示头5行的文件
tail 只看尾巴几行
:set nu
显示文本行数
修改文件:
vi 或 vim
vi 或 vim
按下 i 进入修改模式
按下esc退出当前模式
:q! 强制退出不保存
:wq 保存并退出
yy + p 或者 Y +P —— yy复制光标所在行,p在其下一行粘贴
GG 或者shift g —— 表示跳至文本结尾;
GG 或者shift g —— 表示跳至文本结尾;
vim 文件刷新
:e 重新加载文件
:e! 强制丢掉本地修改,从磁盘加载文件
:e! 强制丢掉本地修改,从磁盘加载文件
/license —— 在文本中查找license;
vim查找关键字
在命令模式下敲斜杆( / ),这时在状态栏(也就是屏幕左下脚)就出现了 “/” 然后输入你要查找的关键字敲回车
继续查找此关键字,敲字符 n
敲字符N(大写N)就会向前查询
vim鼠标不能右键粘贴、跨系统复制粘贴
方法一:
:set mouse-=a
方法二:
if has('mouse')
set mouse-=a
endif
set mouse-=a
endif
vim /tools/1.txt +14
直接跳到14行看
gedit
查看文件的详细属性
stat a.txt
du -h --max-depth=4 文件名/
展示最大深度为4的文件及大小,--max-depth=4最大深度为4
换行
linux命令行如何换行
"\" +ENTER 就可以实现换行
shell脚本中,换行符号
echo -e "ab\ncd"
当与-e选项一起使用时,echo命令将解释反斜杠转义的字符,例如换行符\n
文本追加
Linux中将终端的打印结果输出 追加 到文本文件中
符号 ">" 代表重写要输出的文件
">>"代表要追加要输出的文件,不改变原文件的内容
">>"代表要追加要输出的文件,不改变原文件的内容
用命令tee,除了写入文本文件,终端仍然会有输出结果
git status | tee -a ./1.txt
git status | tee -a ./1.txt
清空
linux中快速清空文件内容的几种方法
$ : > filename
$ > filename
$ echo "" > filename
$ echo > filename
$ cat /dev/null > filename
上面3种方式,能将文件清空,而且文件大小为0
而下面两种方式,导致文本都有一个"\0",而使得文件大小为1
$ > filename
$ echo "" > filename
$ echo > filename
$ cat /dev/null > filename
上面3种方式,能将文件清空,而且文件大小为0
而下面两种方式,导致文本都有一个"\0",而使得文件大小为1
用vim编辑二进制文件
在Linux下查看二进制文件的软件:
xxd (2进制)
hexdump (16进制)
xxd (2进制)
hexdump (16进制)
find/grep
find指令
find ./ -iname "*.java"
find ./ -iname *.java | grep abc
查找目录
find /path/to/search -type d -name "name-of-dir"
精确查找 —w
查找路径下所有含有kk的文件和文件夹,并且把他们转移至 .hhh路径下
find . -name "*kk*" -exec mv {} ./hhh/ \;
find . -name "*kk*" -exec mv {} ./hhh/ \;
find查询符合两个匹配名字的写法
find / -name "*pp" -o -name "*po"
-o 是 -or 的意思
find ./ -regex ".*\.java\|.*\.xml"
上面这条命令会查找当前文件夹下面的所有java文件和xml文件,find默认采用emacs正,则,会比较罗嗦
find ./ -regextype posix-extended -regex ".*\.(java|xml)"
采用posix-extended正,则会比较简单
问题
find . -name "*.[h|c|sh]"
只能把 .c 和 .h 的文件查找出来,没有把 .sh 的文件找出来,这是为什么呢?
只能把 .c 和 .h 的文件查找出来,没有把 .sh 的文件找出来,这是为什么呢?
[sh]只能是s,或者是h
特别注意
表达式之间默认是与的关系,如-name .c -name path,符合条件的应该是path*.c的文件
find时,不显示Permission denied
find 路径 -name 文件名 2>/dev/null
find高级用法
查找特定大小或大于X的文件
搜索大于10MB的文件:
$ find /path/to/search -size +10M
$ find /path/to/search -size +10M
搜索小于10MB的文件:
$ find /path/to/search -size -10M
$ find /path/to/search -size -10M
搜索大小恰好为10MB的文件:
$ find /path/to/search -size 10M
$ find /path/to/search -size 10M
搜索大小在100MB到1GB之间的文件:
$ find /path/to/search -size +100M -size -1G
$ find /path/to/search -size +100M -size -1G
查找最大的目录或文件
如何显示目录中最大的文件:
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | tail -1
请注意,find命令已被排序到另外两个方便的Linux实用程序:sort和tail。 Sort将按文件的大小顺序排列文件列表,而tail将仅输出列表中的最后一个文件,该文件也是最大的
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | tail -1
请注意,find命令已被排序到另外两个方便的Linux实用程序:sort和tail。 Sort将按文件的大小顺序排列文件列表,而tail将仅输出列表中的最后一个文件,该文件也是最大的
如果您要输出例如最大的前5个文件,则可以调整tail命令。
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | tail -5
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | tail -5
使用head命令来确定最小的文件:
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | head -5
$ find /path/to/search -type f -printf "%s\t%p\n" | sort -n | head -5
如果要搜索目录而不是文件,只需在类型选项中指定“ d”即可。如何显示最大目录:
$ find /path/to/search -type d -printf "%s\t%p\n" | sort -n | tail -1
$ find /path/to/search -type d -printf "%s\t%p\n" | sort -n | tail -1
设置maxdepth,查找深度
find . -maxdepth 0 -name "myfile.txt"
仅搜索当前目录中的文件,而不递归搜索
仅搜索当前目录中的文件,而不递归搜索
$ find . -maxdepth 1 -name "myfile.txt"
仅在当前目录和更深的一个子目录中搜索文件:
仅在当前目录和更深的一个子目录中搜索文件:
查找并处理空文件(零长度)
搜索所有空文件:
$ find /path/to/search -type f -empty
$ find /path/to/search -type f -empty
搜索所有空目录:
$ find /path/to/search -type d -empty
$ find /path/to/search -type d -empty
如果希望自动删除find返回的空文件或目录,那么将此命令与-delete选项结合使用也非常方便。
删除目录(和子目录)中的所有空文件:
$ find /path/to/search -type f -empty -delete
删除目录(和子目录)中的所有空文件:
$ find /path/to/search -type f -empty -delete
查找最近 X天/xmin 内的修改文件
搜索最近30天内修改过的所有文件:
$ find /path/to/search -type f -mtime -30
$ find /path/to/search -type f -mtime -30
搜索超过30天之前已修改的所有文件:
$ find /path/to/search -type f -mtime +30
$ find /path/to/search -type f -mtime +30
搜索30天前刚修改过的所有文件:
$ find /path/to/search -type f -mtime 30
$ find /path/to/search -type f -mtime 30
如果希望find命令输出有关找到的文件的更多信息,
例如修改日期,则可以使用-exec选项并包含ls命令:
$ find /path/to/search -type f -mtime -30 -exec ls -l {} \;
例如修改日期,则可以使用-exec选项并包含ls命令:
$ find /path/to/search -type f -mtime -30 -exec ls -l {} \;
注意最后有个 \;
特别强调,对于不同的系统,直接使用分号可能会有不同的意义, 使用转义符 '\'在分号前明确说明
-l 只统计行数
-w 只统计单词数
-m 只统计字符数
-w 只统计单词数
-m 只统计字符数
查找最近30分钟修改的当前目录下的.php文件
find . -name '*.php' -mmin -30
find . -name '*.php' -mmin -30
atime:访问时间(access time),指的是文件最后被读取的时间,可以使用touch命令更改为当前时间;
ctime:变更时间(change time),指的是文件本身最后被变更的时间,变更动作可以使chmod、chgrp、mv等等;
mtime:修改时间(modify time),指的是文件内容最后被修改的时间,修改动作可以使echo重定向、vi等等;
ctime:变更时间(change time),指的是文件本身最后被变更的时间,变更动作可以使chmod、chgrp、mv等等;
mtime:修改时间(modify time),指的是文件内容最后被修改的时间,修改动作可以使echo重定向、vi等等;
列出文件未经允许被拒绝
没有权限尽管这可能发生在许多不同的目录中,但在搜索根目录时肯定会发生。
这意味着,当您尝试在整个硬盘上搜索文件时,find命令将产生大量错误消息。
为避免看到这些错误,您可以将find的stderr输出重定向到stdout,并将其通过管道传递到grep。
$ find / -name "myfile.txt" 2>%1 | grep -v "Permission denied"
此命令使用grep的-v(反向)选项来显示所有输出,除了显示“拒绝权限”之外的所有输出
这意味着,当您尝试在整个硬盘上搜索文件时,find命令将产生大量错误消息。
为避免看到这些错误,您可以将find的stderr输出重定向到stdout,并将其通过管道传递到grep。
$ find / -name "myfile.txt" 2>%1 | grep -v "Permission denied"
此命令使用grep的-v(反向)选项来显示所有输出,除了显示“拒绝权限”之外的所有输出
查找符合特定权限的文件
find -name ".*" -perm 755
grep指令
匹配
匹配多个单词-or操作
grep 'pattern1\|pattern2' filename
grep -E 'pattern1|pattern2' filename
egrep 'pattern1|pattern2' filename
grep -e pattern1 -e pattern2 filename
匹配多个单词-and操作
grep -E 'pattern1.*pattern2' filename
$ grep -E 'Dev.*Tech' employee.txt
200 Jason Developer Technology $5,500
200 Jason Developer Technology $5,500
grep -E 'pattern1.*pattern2|pattern2.*pattern1' filename
匹配多个单词-not操作
grep -v
egrep -v "#|^$" 1.txt >2.txt
去掉空格和#注释,生成新的文件
查找
grep -inr license --include="*.txt"
-i 或 --ignore-case : 忽略字符大小写的差别
-n 或 --line-number : 在显示符合样式的那一行之前,
标示出该行的列数编号
标示出该行的列数编号
-r 或 --recursive : 此参数的效果和指定"-d recurse"参数相同
递归查找
递归查找
-d <动作> 或 --directories=<动作> :
当指定要查找的是目录而非文件时,必须使用这项参数,
否则grep指令将回报信息并停止动作
当指定要查找的是目录而非文件时,必须使用这项参数,
否则grep指令将回报信息并停止动作
内容显示较多时,只想明显查看是否含有某个字符串,使用-o
-o 或 --only-matching : 只显示匹配PATTERN 部分
不查找某类文件
--exclude
demo
grep -nr -o --color Main --exclude="*.java" --exclude="*.cs" --exclude="*.xml"
grep -inro --color readme --exclude="*.log"
grep -rl xxxxxx /aaa
查找并列出 在aaa目录下 含有xxxxxx内容的文件
grep -rl xxxxxx /aaa | xargs sed -i 's/xxxxxx/9876543210/g'
查找并列出 在aaa目录下 含有xxxxxx内容的文件,
然后将xxxxxx内容替换成9876543210
然后将xxxxxx内容替换成9876543210
grep显示行内容
grep -An 关键字 文件 显示匹配的一行,并显示该行前的n行
grep -Bn 关键字 文件 显示匹配的一行,并显示该行后的n行
grep -Cn 关键字 文件 显示匹配的一行,并显示该行前后的n行
grep -n 关键字 文件同grep -Cn 关键字 文件
grep -v grep 搜索出来的内容排除grep 后显示
grep 30 -A 10 显示30行及之前的10行
grep 完全匹配:字符前后增加\b
grep -w "lll" a.log 完全匹配,等同上面
grep -i 忽略大小写
grep -o只显示匹配关键字
grep -Bn 关键字 文件 显示匹配的一行,并显示该行后的n行
grep -Cn 关键字 文件 显示匹配的一行,并显示该行前后的n行
grep -n 关键字 文件同grep -Cn 关键字 文件
grep -v grep 搜索出来的内容排除grep 后显示
grep 30 -A 10 显示30行及之前的10行
grep 完全匹配:字符前后增加\b
grep -w "lll" a.log 完全匹配,等同上面
grep -i 忽略大小写
grep -o只显示匹配关键字
* 重复0个或多个前面的字符
? 代表重复0个或一个前面的字符
.*匹配所有字符
+ 代表重复一个或多个字符
. 点号代表一个字符
? 代表重复0个或一个前面的字符
.*匹配所有字符
+ 代表重复一个或多个字符
. 点号代表一个字符
查找并对比文件
#!bin/bash
Count=1
for line in $( find -name readme.txt )
do
echo "find ${line}"
name0=$(printf "%05d" $Count)
name=${name0:0:5}
echo $name
cp ${line} "readme"$name".txtnew"
(( Count=Count+1))
echo $Count "Rename OK"
done
Count=1
for line in $( find -name readme.txt )
do
echo "find ${line}"
name0=$(printf "%05d" $Count)
name=${name0:0:5}
echo $name
cp ${line} "readme"$name".txtnew"
(( Count=Count+1))
echo $Count "Rename OK"
done
脚本释义
第一句表示由bin路径下的bash解释器来解释该脚本,
第二句定义count变量,
第三句for循环,括号内是查找搜索语句,$()表示执行括号内的语句,并把返回值保存,以供查找。
第三四do,
第五句打印line的值,
第六句 %05d 表示至少输出5位数字,取count的值赋给name0,
第七句name=${name0:0:5},将name0从左到右的0~5位数据赋值给name(列如name0数据为123456,那么name被赋值为12345,没有6),
第八句打印name;
第九句重命名;
第十句自增,等同于Count=$(( Count+1)) 或者 let Count++(Count和++之间不能有空格);
第十一句打印;
十二句结束。
第二句定义count变量,
第三句for循环,括号内是查找搜索语句,$()表示执行括号内的语句,并把返回值保存,以供查找。
第三四do,
第五句打印line的值,
第六句 %05d 表示至少输出5位数字,取count的值赋给name0,
第七句name=${name0:0:5},将name0从左到右的0~5位数据赋值给name(列如name0数据为123456,那么name被赋值为12345,没有6),
第八句打印name;
第九句重命名;
第十句自增,等同于Count=$(( Count+1)) 或者 let Count++(Count和++之间不能有空格);
第十一句打印;
十二句结束。
文件复制、远程复制、移动
mv(移动和重命名文件)
mv aflie bfile 将afile重命名为bfile
mv afile /tmp 把当前目录下的afile移动到/tmp/目录下
mv. a/b c/
把 b 文件夹移动到 c 文件夹下面
scp(远程移动文件)
scp ./a.txt liujun@10.10.10.10:/home/liujun/a/
注意:号
scp root@10.122.90.89:/tools/\{a.txt,b.excel,c.java} ./
scp到多台服务器。需要配置免密登录,写脚本同步
cp(拷贝和覆盖)
cp -r c/d a/b(将c目录下的d目录下的所有文件及其子目录文件全部拷贝到a目录下的b目录下)
cp test a/b/test1(将test文件拷贝到a目录下的b目录下,并且重新命名为test1)
复制所有图片文件到 /data/images 目录下
ls *.jpg | xargs -n1 -I {} cp {} /data/images
将某路径下 含有liujun的文件和文件夹名,改为love
for i in $(find ../ -name "*liujun*")
do
nn=$(echo $i |sed 's/liujun/love/')
mv $i $nn
done
do
nn=$(echo $i |sed 's/liujun/love/')
mv $i $nn
done
文件目录操作
pwd :显示当前路径
返回上一层: ../
ls:查看当前目录下内容
ls -l
ll
ls
ls -a
跳转到某个路径:cd abc
文本编辑 sed
文本替换
将当前sed目录下1.txt文件里的ccc替换成xxx
# sed -i 's/ccc/xxx/g' ./sed/1.txt
# sed -i 's/ccc/xxx/g' ./sed/1.txt
i:表示insert
s:表示search
g:表示global
s:表示search
g:表示global
文本查看
查看当前目录下1.txt的文件第三行
sed -n 3p ./1.txt
sed -n 3p ./1.txt
sed -n 3,5p ./1.txt
查找区间
显示包含"hhh"的行到包含"omc"的行之间的行
sed -n '/hhh/,/omc/p' yum.log
sed -n '/hhh/,/omc/p' yum.log
sed -n /5555/,/dddd/p kk.ll
, 这里没有空格
文本过滤/查看
sed -n /查找内容/p kk.ll
类似 grep -n 5555 kk.ll
插入行首行尾
sed -i -e '1i happy' -e '$a new year' yum.log
注意 1i 不是 li
全文搜索及批量替换
按目录全文搜索关键词keywords
grep -lri 'keywords' /home/work
grep -lri 'keywords' /home/work
按目录批量替换关键词keywords
sed -i "s/oldWords/newWords/g" `grep keywords -rl /home/work`
sed -i "s/oldWords/newWords/g" `grep keywords -rl /home/work`
注意符合: `
mac下如何使用Sed批量替换文件夹下的字符串
sed "s/bbb/aaa/g" test.txt > test2.txt
mv test2.txt test.txt
mv test2.txt test.txt
sed -i '' 's/old/new/g' file_name
文本处理 awk
AWK程序设计语言
awk 赋值给 shell 变量的方法
result='week(now(),-1) 49';
var=`echo $result|awk '{print substr($result,16,3)}'`;
echo $var;
SYSFILE_NAME=`echo "$CONTROL_FILE" | awk -F"." '{print $1}' | awk -F"/" '{print $NF}'`;
var=`echo $result|awk '{print substr($result,16,3)}'`;
echo $var;
SYSFILE_NAME=`echo "$CONTROL_FILE" | awk -F"." '{print $1}' | awk -F"/" '{print $NF}'`;
文本输出 seq
seq -w 1 10
输出1~10
文件格式转换
dos2unix
find ./ -iname "*" -exec dos2unix {} \;
将文件夹下面的所有文件从dos转化为Unix
文件内容后面有^M
1. 在windows下的文本文件的每一行结尾,都有一个回车('\n')和换行('\r')
2. 在linux下的文本文件的每一行结尾,只有一个回车('\n');
3. 在Mac下的文本文件的每一行结尾,只有一个换行('\r');
2. 在linux下的文本文件的每一行结尾,只有一个回车('\n');
3. 在Mac下的文本文件的每一行结尾,只有一个换行('\r');
解决办法
dos2unix
todos
dos2unix filename # 直接转换成unix格式 此方法在终端下可用。由于本人使用为ubuntu系统,没有该命令。
todos filename # ubuntu 终端使用, 如果未安装 sudo apt-get install tofrodos todos(相当于unix2dos),和fromdos(相当于dos2unix)
todos filename # ubuntu 终端使用, 如果未安装 sudo apt-get install tofrodos todos(相当于unix2dos),和fromdos(相当于dos2unix)
文件安装
rpm
Red Hat
Red Hat
安装
rpm -ivh foo-1.0-l.i386.rpm
-h 安装的进度条就有了
-i ,--install 安装软件包
-v, --verbose 提供更多的详细信息输出
卸载
rpm –e 软件名
升级软件包
rpm -Uvh foo-2.0-l.i386.rpm
-Uvh
-U:即升级的意思
rpm –qa
rpm -qa |grep vagrant
rpm –q vagrant
不要加入后缀之类的
dpkg
ubuntu
ubuntu
wget
yum
yum list
列出所有软件安装包
yum list | grep *** // 显示已安装,未安装的包
带@是已经安装的
base是linux自带的软件包
Centos7.4-BM 是安装BM时,BM提供的软件包
base是linux自带的软件包
Centos7.4-BM 是安装BM时,BM提供的软件包
yum clean all
在Centos7系统中执行yum clean all 之后,发现yum的其他执行都报错了
把/var/cache/yum/ 下面的文件删除了
yum repolist all
yum makecache
yum update
文本对比diff和排序sort
首先对文本进行排序 sort 1.txt. >./2.txt
注意到:sort后的文件不能写回原文件,会丢失数据
对比文件 diff 1.txt 2.txt
注意要先排序再对比
权限
修改文件权限
chmod u+x *.sh (给所有的sh文件加权限)
chmod -R u+x /root (给root及其子文件加权限)
chgrp
更改目录权限
chown
改变文件所有者及用户组
-R : 递归改变
查看test.txt具有的权限
ls -al test.txt
生效文件
source FileName 作用:在当前bash环境下读取并执行FileName中的命令。
注:该命令通常用命令“.”来替代。
如:source /etc/profile 与 . /etc/profile是等效的,比如 ./test.sh 可以直接执行
用户
新建用户
useradd 指令和 adduser 指令
adduser liujun
然后输入密码和确认密码,一路回车,记得最后选一个y(表示yes),搞定,大部分创建配置都在 /etc/adduser.conf 里面写好了,
系统会自动在/home路径下创建用户,如果需要加sudo权限,则使用 vim /etc/sudoers指令,
在# User privilege specification下方加入指令: liujun ALL=(ALL:ALL) ALL 即可。
系统会自动在/home路径下创建用户,如果需要加sudo权限,则使用 vim /etc/sudoers指令,
在# User privilege specification下方加入指令: liujun ALL=(ALL:ALL) ALL 即可。
推荐使用
useradd -d "/home/liujun" -m -s /bin/bash liujun
在使用useradd命令创建新用户时,不会为用户创建主目录,不会为用户指定shell版本,不会为用户创建密码。
-d "/home/liujun" 表示指定用户登录目录,
-m 表示没有这个目录则创建一个,
-s 表示用户登录后使用的shell,不写的话,会导致丢失 .Xauthority 文件,会给后续使用造成很大麻烦;
然后使用passwd liujun 指令给用户创建密码,否则无法登录和使用。
-d "/home/liujun" 表示指定用户登录目录,
-m 表示没有这个目录则创建一个,
-s 表示用户登录后使用的shell,不写的话,会导致丢失 .Xauthority 文件,会给后续使用造成很大麻烦;
然后使用passwd liujun 指令给用户创建密码,否则无法登录和使用。
修改用户密码
passwd 用户名
以管理员权限查看、执行
sodu
这样输入当前管理员用户密码就可以得到超级用户的权限。但默认的情况下5分钟root权限就失效了
切换用户
su 用户名,比如 su root
查看当前用户的权限
直接查看的命令,你可以通过一个文件或一个文件夹来查看它所拥有的属性就知道了,比如: ll
把当前用户加入root权限组
见新建用户
在Ubuntu中,可以修改的配置文件和目录主要有两个,分别是/etc/sudoers 和 /etc/suders.d/
前者是一个配置文件,后者是一个配置文件目录
前者是一个配置文件,后者是一个配置文件目录
退出用户
exit退出
定时任务
crontab -e
分 时 日 月 周 [用户] command
expect
Linux expect详解
send
send命令接收一个字符串参数,并将该参数发送到进程
spawn
启动新的进程
interact
允许用户交互
kinit -V
Linux机器之间免密登录设置
ssh-keygen -t rsa
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.9.110
ssh-add -K
进程
基本指令
基础
ps -a
显示现行终端机下的所有进程,包括其他用户的进程
ps -u
以用户为主的格式来显示进程状况
ps -u root
ps -ef
显示所有命令,连带命令行
full
jps -l
显示所有JAVA进程详情名
long
top命令
实时监控系统的状态
pgrep
用于根据特定条件查询进程PID信息
示例
pstree
用于查看进程树,以树形结构列出进程信息
示例
进程查询操作
mac oxs 上查看进程监听的端口号 lsof
ps aux |grep 应用名
lsof -p 应用名对应的ID | grep LISTEN
linux
netstat -nap | grep 端口
通过端口号查看占用的进程id
netstat -nap | grep 进程id
通过进程id查看占用的端口
ps aux | grep 进程名
(或者 ps -ef | grep 进程名)
(或者 ps -ef | grep 进程名)
查看进程id
可能出现的问题
netstat:ccommand not find
解决方案
yum install net-tools
net无法使用,需要配置已存在的yum源,即挂载磁盘
解决方案
mount /dev/sr0(磁盘) /mnt/cdrom(挂载地址)
如果出现pid为空的情况,这种时候用:lsof -i:端口号
解决方案
lsof -i:端口号
(list open files)是一个列出当前系统打开文件的工具
必须以 root 用户的身份运行它
windows
netstat -ano | findstr 8007端口
根据上面查出来的端口最后一行占用的进程号进一步查询是那个服务占用的
tasklist | findstr 进程号
tasklist | findstr 进程号
进程的终止
kill -1 PID 重启服务
kill -9 PID 强行杀死进程
kill -15 PID 正常结束
kill -9 PID 强行杀死进程
kill -15 PID 正常结束
pkill nginx
杀进程
语句解析
ps -ef | grep cusip_full_is | grep -v grep | wc -l | awk '{ print $1; }'
ps -ef | 全格式显示当前所有进程
grep cusip_full_is 滤出''cusip_full_is''的进程
grep -v grep 把''grep''这个进程忽略掉
wc -l 看看有多少个进程
awk '{ print $1; }' 输出第一列
kill -signal %jobnumber(进程号)
signal :代表给予后面接的那个工作什么样的指示啰!用 man 7 signal 可知:
-1 :重新读取一次参数的设定档 (类似 reload);
-2 :代表与由键盘输入 [ctrl]-c 同样的动作;
-9 :立刻强制删除一个工作;
-15:以正常的程序方式终止一项工作。与 -9 是不一样的。
[root@linux ~]# jobs
[1]+ Stopped vim bashrc
[root@linux ~]# kill -9 %1
[1]+ 已砍掉 vim bashrc
网络
连通性测试
curl www.baidu.com
curl
ping
ping www.baidu.com;
ping localhost
ping localhost
ping IP -n 20:执行特定次数(此处是 20)的 ping 命令
ping IP -l 2000:指定 ping 命令中的特定数据长度(此处为 2000 字节),而不是缺省的 32 字节。
注意:
随着防火墙功能在网络中的广泛使用,
当你 ping 其他主机或其他主机 ping 你的主机时,
而显示主机不可达的时候,不要草率地下结论。
最好与对某台 “设置良好” 主机的 ping 结果进行对比。
当你 ping 其他主机或其他主机 ping 你的主机时,
而显示主机不可达的时候,不要草率地下结论。
最好与对某台 “设置良好” 主机的 ping 结果进行对比。
ipconfig
ipconfig /all
ipconfig /release 和 ipconfig /renew
输入 ipconfig /release,那么所有接口的租用 IP 地址便重新交付给 DHCP 服务器(归还 IP 地址)
输入 ipconfig /renew,那么本地计算机便设法与 DHCP 服务器取得联系,并租用一个 IP 地址。
大多数情况下网卡将被重新赋予和以前所赋予的相同的 IP 地址
大多数情况下网卡将被重新赋予和以前所赋予的相同的 IP 地址
arp
arp –a:用于查看高速缓存中的所有项目
② arp -a IP:如果有多个网卡,那么使用 arp -a 加上接口的 IP 地址,就可以只显示与该接口相关的 ARP 缓存项目。
③ arp -s IP 物理地址:向 ARP 高速缓存中人工输入一个静态项目。
该项目在计算机引导过程中将保持有效状态,或者在出现错误时,
人工配置的物理地址将自动更新该项目。
④ arp -d IP:使用本命令能够人工删除一个静态项目。
③ arp -s IP 物理地址:向 ARP 高速缓存中人工输入一个静态项目。
该项目在计算机引导过程中将保持有效状态,或者在出现错误时,
人工配置的物理地址将自动更新该项目。
④ arp -d IP:使用本命令能够人工删除一个静态项目。
traceroute
tracert www.baidu.com
显示数据包到达目的主机所经过的路径
route
route print
显示路由表中的当前项目
route add
使用本命令,可以将路由项目添加给路由表。
例如,如果要设定一个到目的网络 209.99.32.33 的路由,其间要经过 5 个路由器网段,
首先要经过本地网络上的一个路由器 IP 为 202.96.123.5,子网掩码为 255.255.255.224,
那么用户应该输入以下命令:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.5 metric 5
例如,如果要设定一个到目的网络 209.99.32.33 的路由,其间要经过 5 个路由器网段,
首先要经过本地网络上的一个路由器 IP 为 202.96.123.5,子网掩码为 255.255.255.224,
那么用户应该输入以下命令:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.5 metric 5
route change
可以使用本命令来修改数据的传输路由,
不过,用户不能使用本命令来改变数据的目的地。
下面这个例子将上例路由改变采用一条包含 3 个网段的路径:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.250 metric 3
不过,用户不能使用本命令来改变数据的目的地。
下面这个例子将上例路由改变采用一条包含 3 个网段的路径:
route add 209.99.32.33 mask 255.255.255.224 202.96.123.250 metric 3
route delete: 使用本命令可以从路由表中删除路由。例如:route delete 209.99.32.33
nslookup
查询任何一台机器的 IP 地址和其对应的域名
nbtstat
查看计算机上网络配置的一些信息
netstat
netstat 命令能够显示活动的 TCP 连接、计算机侦听的端口、
以太网统计信息、IP 路由表、IPv4 统计信息(对于 IP、ICMP、TCP 和 UDP 协议)
以及 IPv6 统计信息(对于 IPv6、ICMPv6、通过 IPv6 的 TCP 以及 UDP 协议)。
使用时如果不带参数,netstat 显示活动的 TCP 连接
以太网统计信息、IP 路由表、IPv4 统计信息(对于 IP、ICMP、TCP 和 UDP 协议)
以及 IPv6 统计信息(对于 IPv6、ICMPv6、通过 IPv6 的 TCP 以及 UDP 协议)。
使用时如果不带参数,netstat 显示活动的 TCP 连接
netstat -lnp
打印当前系统启动哪些端口
netstat -an
打印网络连接状况
netstat -an |grep 80
如果你所管理的服务器是一台提供web服务(80端口)的服务器,
可以使用 netstat -an |grep 80 查看当前连接web服务的有哪些IP
可以使用 netstat -an |grep 80 查看当前连接web服务的有哪些IP
windows上是 netstat -n | findstr 80
-a 选项显示所有的有效连接信息列表,包括已建立的连接(ESTABLISHED),也包括监听连接请求(LISTENING)的那些连接
–n:以点分十进制的形式列出 IP 地址,而不是象征性的主机名和网络名
-e 选项用于显示关于以太网的统计数据。
它列出的项目包括传送的数据包的总字节数、错误数、删除数、数据包的数量和广播的数量。
这些统计数据既有发送的数据包数量,也有接收的数据包数量。使用这个选项可以统计一些基本的网络流量
它列出的项目包括传送的数据包的总字节数、错误数、删除数、数据包的数量和广播的数量。
这些统计数据既有发送的数据包数量,也有接收的数据包数量。使用这个选项可以统计一些基本的网络流量
-r 选项可以显示关于路由表的信息,类似于 route print 命令时看到的信息。除了显示有效路由外,还显示当前有效的连接。
-s 选项能够按照各个协议分别显示其统计数据。
这样就可以看到当前计算机在网络上存在哪些连接,以及数据包发送和接收的详细情况等等。
如果应用程序(如 Web 浏览器)运行速度比较慢,或者不能显示 Web 页之类的数据,那么可以用本选项来查看一下所显示的信息。
仔细查看统计数据的各行,找到出错的关键字,进而确定问题所在
这样就可以看到当前计算机在网络上存在哪些连接,以及数据包发送和接收的详细情况等等。
如果应用程序(如 Web 浏览器)运行速度比较慢,或者不能显示 Web 页之类的数据,那么可以用本选项来查看一下所显示的信息。
仔细查看统计数据的各行,找到出错的关键字,进而确定问题所在
net
NET 命令可以在一个地方提供所有信息,并可以把结果重定向到打印机或一个标准的文本文件中。
许多服务所使用的网络命令都以 net 开头,这些 net 命令有一些公用属性。要看到所有可用的 net 命令的列表,可以在命令提示符窗口键入 net/? 得到
许多服务所使用的网络命令都以 net 开头,这些 net 命令有一些公用属性。要看到所有可用的 net 命令的列表,可以在命令提示符窗口键入 net/? 得到
路径类查找
查看使用者: who 或 who i am
java 执行路径:which Java(当前执行的是哪个Java)
找Java 路径:whereis java
找 JAVA_HOME:echo $JAVA_HOME
找PATH::echo $PATH
操作系统
查看内存
free
查看内存使用多少,剩余多少请看第二行的数据。
加-m或-h 或者-g选项分别以M或G为单位打印内存使用状况:
磁盘使用
iostat -x
将服务做成系统服务
方式一
查看该配置模式下的开机启动项:chkconfig --list
1、将启动文件或启动配置文件复制到 /etc/init.d 或 /etc/rc.d/init.d , 并配置好相关参数
2、chmod +x /etc/rc.d/init.d/你的启动文件
3、以下两者任选其一
chkconfig --add 你的服务名
chkconfig 你的服务名 on
4、service 你的服务名 start/stop/restart
2、chmod +x /etc/rc.d/init.d/你的启动文件
3、以下两者任选其一
chkconfig --add 你的服务名
chkconfig 你的服务名 on
4、service 你的服务名 start/stop/restart
方式二
(建议)
查看该配置模式下的开机启动项:systemctl list-unit-files | grep enable
vim /etc/systemd/system/yourServiceName.service
[Unit]
Description=yourServiceName
After=syslog.target
[Service]
User=myapp
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
Description=yourServiceName
After=syslog.target
[Service]
User=myapp
ExecStart=/var/myapp/myapp.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
3、systemctl daemon-reload
4、systemctl enable yourServiceName.service 【注册为自启动服务】
相对应的: systemctl disable yourServiceName.service 【撤销开机启动】
5、systemctl start yourServiceName.service
systemctl status yourServiceName.service
systemctl stop yourServiceName.service
service yourServiceName restart ----也可以用第一种方式的命令
【systemctl 和 service 命令可看成等效】
4、systemctl enable yourServiceName.service 【注册为自启动服务】
相对应的: systemctl disable yourServiceName.service 【撤销开机启动】
5、systemctl start yourServiceName.service
systemctl status yourServiceName.service
systemctl stop yourServiceName.service
service yourServiceName restart ----也可以用第一种方式的命令
【systemctl 和 service 命令可看成等效】
开机自启的三种方式
将脚本添加到 /etc/rc.d/rc.local 文件最后一行
使用 crontab + @reboot
使用 systemd 服务
其他
cal
显示日历
管道
2>&1
标准错误输出和标准输出都定向输出
Linux组合用法
统计项目代码行数
find ./ -iname *.java |xargs nl| wc -l
find 查出所有的java代码
xargs命令
xargs是给命令传递参数的一个过滤器
wc -l 统计行数
衍生
统计当前目录下,py文件数量:
find . -name "*.py" |wc -l
统计当前目录下,所有py文件行数:
find . -name "*.py" |xargs cat|wc -l
统计当前目录下,所有py文件行数,并过滤空行:
find . -name "*.py" | xargs cat| grep -v ^$| wc -l
统计文件行数
find ./ -type f | xargs cat |wc -l
git统计代码提交行数
git代码统计
统计指定用户提交代码情况
git log --author="你的名字" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
统计每个人的代码提交情况
git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
addnum=`git log --all --since=$time.day.ago --pretty=tformat: --numstat |awk '{add += $1} END {printf "%.f\n",add}'`
let addsumTotal +=addnum
let addsumTotal +=addnum
注意这个符号 ————> `
注意tformat后面的 ————> :
注意printf后面的 ————> "
注意%和f之间的 ————> .
注意加和的方法 ————> let
注意加和 ————> 不需要加$
注意tformat后面的 ————> :
注意printf后面的 ————> "
注意%和f之间的 ————> .
注意加和的方法 ————> let
注意加和 ————> 不需要加$
Windows下使用git bash
wget
Windows下git bash中添加wget
tree
Git Bash下安装使用tree(在windows)
用法
tree -L 2
tree 命令打印2层
tree -C
带颜色
tree -d
打印至目录级别
ssh
ssh user@host 如:ssh pika@192.168.0.111
git(repo)
git 安装
二进制安装
总是提示“git命令需要使用命令行开发者工具”
注意M1系统进入恢复模式的方式是按住电源键
git指令
git 和 repo的用法
(基础指令)
(基础指令)
git commit -am "first init"
相当于git add 和 git commit合体
git log的高级用法
git log -2 查看最近2次的提交历史记录
git log --author=gbyukg
查看某文件,某一行的修改记录
git blame xx.java -L 3,3
查看第3行的修改记录,
3.3表示起始位置,结束位置
3.3表示起始位置,结束位置
git log查看符合条件的日志
git log --pretty=online 可以显示完整的commit id
GIT 获取指定历史版本代码(根据时间获取commit或者查看git log)
git rev-list --all -1 --since=3.month.ago --until=2.month.ago
s='git rev-list --all -1 --since=3.month.ago --until=2.month.ago'
$s
$s
获取指定版本的信息,并回退至该版本,
liuj=$(git rev-list --all -1 --since=3.month.ago --until=2.month.ago)
git reset $liuj
liuj=$(git rev-list --all -1 --since=3.month.ago --until=2.month.ago)
git reset $liuj
git log --decorate
知道commit是和哪一个分支或tag关联的
设置别名的时候注意,不要对已有的指令进行设置
错误
git config --global alias.log 'log --decorate'
正确
git config --global alias.l 'log --decorate'
git log --stat
列出简略的统计数据
git log --numstat
列出代码新增和减少的数量
配合 --pretty=tformat去掉冗余信息,只显示文件的变化量
Git log pretty
详解
git log --pretty=format:'%H' -n 1
%H: commit hash
%h: 缩短的commit hash
%T: tree hash
%t: 缩短的 tree hash
%P: parent hashes
%p: 缩短的 parent hashes
%d: ref名称
%e: encoding
%s: commit信息标题
%f: sanitized subject line, suitable for a filename
%b: commit信息内容
%N: commit notes
%gD: reflog selector, e.g., refs/stash@{1}
%gd: shortened reflog selector, e.g., stash@{1}
%gs: reflog subject
%m: left, right or boundary mark
%n: 换行
%%: a raw %
%x00: print a byte from a hex code
%w([[,[,]]]): switch line wrapping, like the -w option of git-shortlog(1).
%H: commit hash
%h: 缩短的commit hash
%T: tree hash
%t: 缩短的 tree hash
%P: parent hashes
%p: 缩短的 parent hashes
%d: ref名称
%e: encoding
%s: commit信息标题
%f: sanitized subject line, suitable for a filename
%b: commit信息内容
%N: commit notes
%gD: reflog selector, e.g., refs/stash@{1}
%gd: shortened reflog selector, e.g., stash@{1}
%gs: reflog subject
%m: left, right or boundary mark
%n: 换行
%%: a raw %
%x00: print a byte from a hex code
%w([[,[,]]]): switch line wrapping, like the -w option of git-shortlog(1).
%cd: 提交日期 (--date= 制定的格式)
%cD: 提交日期, RFC2822格式
%cr: 提交日期, 相对格式(1 day ago)
%ct: 提交日期, UNIX timestamp
%ci: 提交日期, ISO 8601 格式
%ad: 日期 (--date= 制定的格式)
%aD: 日期, RFC2822格式
%ar: 日期, 相对格式(1 day ago)
%at: 日期, UNIX timestamp
%ai: 日期, ISO 8601 格式
%cD: 提交日期, RFC2822格式
%cr: 提交日期, 相对格式(1 day ago)
%ct: 提交日期, UNIX timestamp
%ci: 提交日期, ISO 8601 格式
%ad: 日期 (--date= 制定的格式)
%aD: 日期, RFC2822格式
%ar: 日期, 相对格式(1 day ago)
%at: 日期, UNIX timestamp
%ai: 日期, ISO 8601 格式
%Cred: 切换到红色
%Cgreen: 切换到绿色
%Cblue: 切换到蓝色
%Creset: 重设颜色
%C(...): 制定颜色, as described in color.branch.* config option
%Cgreen: 切换到绿色
%Cblue: 切换到蓝色
%Creset: 重设颜色
%C(...): 制定颜色, as described in color.branch.* config option
%an: 作者名字
%aN: mailmap的作者名字 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%ae: 作者邮箱
%aE: 作者邮箱 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%cn: 提交者名字
%cN: 提交者名字 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%ce: 提交者 email
%cE: 提交者 email (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%aN: mailmap的作者名字 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%ae: 作者邮箱
%aE: 作者邮箱 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%cn: 提交者名字
%cN: 提交者名字 (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
%ce: 提交者 email
%cE: 提交者 email (.mailmap对应,详情参照[git-shortlog(1)](http://linux.die.net/man/1/git-shortlog)或者[git-blame(1)](http://linux.die.net/man/1/git-blame))
范例
git log --graph --data=relative --pretty=tformat:'%Cred%h %C(auto)%d %s'
如何查找某个镜像属于那个分支打包出来的
根据commit描述 查找commitID
git log --all |grep -C 10 "change to order asc"
根据commitID 查找分支
git branch --contains 06cce0f9dc8e3 --all
git diff
查看已经暂存起来的文件(staged)
和上次提交时的快照之间(HEAD)的差异
和上次提交时的快照之间(HEAD)的差异
git diff --cached
git diff --staged
git diff --staged
查看简单的diff结果,
可以加上--stat参数
可以加上--stat参数
git diff --stat
git diff 和 git log
分支文件的差异对比
分支文件的差异对比
1. 显示出branch1和branch2中差异的部分
git diff branch1 branch2 --stat
2. 显示指定文件的详细差异
git diff branch1 branch2 具体文件路径
3. 显示出所有有差异的文件的详细差异
git diff branch1 branch2
git diff branch1 branch2 --stat
2. 显示指定文件的详细差异
git diff branch1 branch2 具体文件路径
3. 显示出所有有差异的文件的详细差异
git diff branch1 branch2
4. 查看branch1分支有,而branch2中没有的log
git log branch1 ^branch2
5. 查看branch2中比branch1中多提交了哪些内容
git log branch1..branch2
注意,列出来的是两个点后边(此处即dev)多提交的内容。
6. 不知道谁提交的多谁提交的少,单纯想知道有是吗不一样
git log branch1...branch2
7. 在上述情况下,在显示出没个提交是在哪个分支上
git log --lefg-right branch1...branch2
注意 commit 后面的箭头,根据我们在 –left-right branch1…branch2 的顺序,
左箭头 < 表示是 branch1 的,右箭头 > 表示是branch2的。
git log branch1 ^branch2
5. 查看branch2中比branch1中多提交了哪些内容
git log branch1..branch2
注意,列出来的是两个点后边(此处即dev)多提交的内容。
6. 不知道谁提交的多谁提交的少,单纯想知道有是吗不一样
git log branch1...branch2
7. 在上述情况下,在显示出没个提交是在哪个分支上
git log --lefg-right branch1...branch2
注意 commit 后面的箭头,根据我们在 –left-right branch1…branch2 的顺序,
左箭头 < 表示是 branch1 的,右箭头 > 表示是branch2的。
git tag
git标签分为两种类型:轻量标签和附注标签。
轻量标签是指向提交对象的引用,
附注标签则是仓库中的一个独立对象。
建议使用附注标签
轻量标签是指向提交对象的引用,
附注标签则是仓库中的一个独立对象。
建议使用附注标签
创建轻量标签
git tag v0.1.2
git tag v0.1.2
创建附注标签
$ git tag -a v0.1.2 -m “0.1.2版本”
$ git tag -a v0.1.2 -m “0.1.2版本”
在之前的版本上打
$ git tag -a v0.1.1 9fbc3d0
$ git tag -a v0.1.1 9fbc3d0
查看标签
git tag
git tag
创建标签
git tag 标签名字 -m '消息内容'
git tag 标签名字 -m '消息内容'
推送标签
git push origin 标签名字
git push origin 标签名字
删除标签
git tag -d 标签名字
git tag -d 标签名字
git stash用法
保存修改内容至栈
git stash
存于栈中,可以在任意分支弹出
git stash save “command”
作用等同于git stash,区别是可以加一些注释
查看stash
git stash list
git stash show
查看堆栈中最新保存的stash和当前目录的差异
git stash show stash@{1}查看指定的stash和当前目录差异
git stash show -p 查看详细的不同
栈内容弹出
git stash pop
将当前stash中的内容弹出,并应用到当前分支对应的工作目录上。
注:该命令将堆栈中最近保存的内容删除(栈是先进后出)
注:该命令将堆栈中最近保存的内容删除(栈是先进后出)
git stash pop + stash名字(如stash@{1})指定恢复哪个stash到当前的工作目录
git stash apply
不同于git stash pop,该命令不会将内容从堆栈中删除
git stash apply + stash名字(如stash@{1})指定恢复哪个stash到当前的工作目录
清除栈
git stash drop + 名称
从堆栈中移除某个指定的stash
git stash clear
清除堆栈中的所有 内容
栈内容冲突
git stash branch
通过新建分支来解决
注意
git stash 无法跟踪新添加的文件,需要先 git add
git clean
git怎样删除未监视的文件untracked files
# 删除 untracked files
git clean -f
# 连 untracked 的目录也一起删掉
git clean -fd
# 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd
# 在用上述 git clean 前,墙裂建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd
git clean -f
# 连 untracked 的目录也一起删掉
git clean -fd
# 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd
# 在用上述 git clean 前,墙裂建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd
git管理
git 仓管理
查看远程仓库地址
git remote -v
git remote show
git remote show origin
远程仓库更名
先删除
git remote rm origin
再添加
git remote add origin xxxxxxurl
本地仓改名
mv aaa bbb
初始化跟随远程仓库
git remote add origin remote_address(远程url)
分支管理
branch
branch
远程分支管理
origin: 默认的远程仓库的名字
初始化跟随远程仓库
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:ljfirst/MiddleSoftwarePractice.git
git push -u origin master
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:ljfirst/MiddleSoftwarePractice.git
git push -u origin master
git pull origin branch --allow-unrelated-histories
查看
git远程分支查看
git branch
可选参数有 -a -v -vv ,
可选参数有 -a -v -vv ,
git branch -a看不到远程分支
先用git fetch命令更新remote索引
或者 git remote # 列出所有远程主机
git remote update origin --prune # 更新远程主机origin 整理分支
git remote update origin --prune # 更新远程主机origin 整理分支
跟随
关联本地仓库分支
与远程仓库分支
与远程仓库分支
git branch --set-upstream-to=origin/develop develop
若遇报错: branch 'develop' does not exist
解决办法:git pull origin branch --allow-unrelated-histories
切换并跟随远程分支
git checkout -b dev origin/dev
作用是checkout远程的dev分支,在本地起名为dev分支,并切换到本地的dev分支
git checkout -b 本地分支名x origin/远程分支名x
拉取远程分支并同时创建对应的本地分支
项目中git地址修改了怎么办
git remote set-url origin http://192.168.100.235:9xxxxxxxx
推送
推送远程仓库
git push [remoteName] [localBranchName]
git 更新远程分支列表,切换并合并已有分支到当前分支
现在远程有两个分支:remote_a和remote_b;
本地有两个分支:local_a和local_b;
跟随关系是a跟随a,b跟随b;
本地有两个分支:local_a和local_b;
跟随关系是a跟随a,b跟随b;
如果想要把local_a提交到remote_a;使用git push origin local_a:remote_a;
如果想要把local_b提交到remote_b;使用git push origin local_b:remote_b;
如果想要把local_a提交到remote_b;使用git push origin local_a:remote_b;
如果想要把local_b提交到remote_a;使用git push origin local_b:remote_a;
特例
新建
git push origin local_a:remote_b
local_a是本地分支,remote_b是远程分支
如果远程没有remote_branch_b,,会自动创建
删除
git push origin :remote_b
冒号前不写,就删除远程分支remote_b
缩写
git push origin master
上传 本地当前分支 代码到 master分支
git本地创建新分支并推送到远程
1.本地创建并切换分支
git checkout -b dev
2.将dev分支推送到远程
git push origin dev:dev //推送本地的dev(冒号前面的)分支到远程origin的dev(冒号后面的)分支(没有会自动创建)
3.建立本地到上游(远端)仓的链接,这样代码才能提交上去
git branch --set-upstream-to=origin/dev
4.取消对master分支的跟踪
git branch --unset-upstream master
git checkout -b dev
2.将dev分支推送到远程
git push origin dev:dev //推送本地的dev(冒号前面的)分支到远程origin的dev(冒号后面的)分支(没有会自动创建)
3.建立本地到上游(远端)仓的链接,这样代码才能提交上去
git branch --set-upstream-to=origin/dev
4.取消对master分支的跟踪
git branch --unset-upstream master
拉取
git pull origin branchName(远程分支名)
基于当前所在分支,拉取远程branchName分支的代码
如何把远程 dev 上的代码拉取到本地dev上,
而不影响其他分支
而不影响其他分支
git pull origin dev:dev
dev更新至最新,master不变
这条指令只能在dev上执行,如果在master上执行,master分支也会更新
如何把远程 dev1 上的代码拉取到本地dev2上
1、本地切换分支到dev2
2、git pull origin dev1
这样可以保证本地其他分支不受影响,
仅把远程dev1上的代码拉取到本地dev2上
仅把远程dev1上的代码拉取到本地dev2上
git fatch/git pull/git pull --rebase的关系
git fetch : 只是将远程的文件拉下来,不会与本地的分支进行合并
git pull = git fetch + git merge
git pull --rebase = git fetch + git rebase
git pull --rebase = git fetch + git rebase
git merge和git rebase的区别
删除
删除远程 分支
git push origin --delete 远程分支名 //先删除远程
git branch -D 本地分支名 //再删除本地
git branch -D 本地分支名 //再删除本地
git push origin :远程分支名 //先删除远程,注意origin后的空格,空格后是:
git branch -D 本地分支名 //再删除本地
git branch -D 本地分支名 //再删除本地
删除远程 文件,并添加到.gitignore
git rm --cached xx.tet
vim .gitignore //进入.gitignore文件,加入需要屏蔽的文件
vim .gitignore //进入.gitignore文件,加入需要屏蔽的文件
出现错误:fatal: not removing 'bin/' recursively without -r
解决办法:修改指令:git rm -r -cached bin/
创建
创建远程分支
git push origin [name]
刷新
git remote prune origin
git remote update origin --prune
本地分之管理
分支合并
git patch
git diff
git diff 【commit sha1 id】 【commit sha2 id】 > 【diff文件名】
区间左开右闭
这个diff是从 commit sha1 id 到 commit sha2 id,除非特殊需求,否则顺序不要反了
用git apply xx.diff 之后,不生产comit,只会生成和修改变化的文件
git format-patch
某两次提交之间的所有patch
git format-patch 【commit sha1 id】..【commit sha1 id】
git format-patch 【commit sha1 id】..【commit sha1 id】
区间左开右闭
某次提交(含)之前的几次提交
git format-patch 【commit sha1 id】-n
git format-patch 【commit sha1 id】-n
git apply
检查patch/diff是否能正常打入
git apply --check 【path/to/xxx.patch】
git apply --check 【path/to/xxx.diff】
git apply --check 【path/to/xxx.patch】
git apply --check 【path/to/xxx.diff】
git apply 【path/to/xxx.patch】
git apply 【path/to/xxx.diff】
git apply 【path/to/xxx.diff】
git am
git am 【path/to/xxx.patch】
patch 冲突
git apply --reject <patch_name>
打patch注意事项
patch 和 diff 的使用
git diff生成的UNIX标准补丁.diff文件
.diff文件只是记录文件改变的内容,
不带有commit记录信息,
多个commit可以合并成一个diff文件
不带有commit记录信息,
多个commit可以合并成一个diff文件
git format-patch生成的Git专用.patch 文件
.patch文件带有记录文件改变的内容,
也带有commit记录信息,
每个commit对应一个patch文件
也带有commit记录信息,
每个commit对应一个patch文件
apply和am的区别
apply不恢复commit信息,am恢复commit信息
am失败,可以使用apply来处理
使用git patch 生成带commit信息的文件,再用git am 恢复commit信息,
使用git diff 生成不带commit信息的文件,再用git apply 将所有commit作为一次提交
这两者都需要用 --check 来验证
使用git diff 生成不带commit信息的文件,再用git apply 将所有commit作为一次提交
这两者都需要用 --check 来验证
merge
普通 Merge
先切换到目标分支(master)
执行命令:git merge devel
删除旧分支(可以在上面一同做):git branch -D devel
提交到远程分支:git push origin master
执行命令:git merge devel
删除旧分支(可以在上面一同做):git branch -D devel
提交到远程分支:git push origin master
以 squash 的形式 merge:git merge --squash devel
devel上的提交合并为一个
rebase merge
先切换到 devel 分支(不一样咯):git checkout devel
变基:git rebase -i master
切换回目标分支:git checkout master
合并: git merge devel
变基:git rebase -i master
切换回目标分支:git checkout master
合并: git merge devel
先merge,然后rebase
视情况而定
git rebase
推荐这种,变基,主干清晰
分支合并其他分支的某次commit
git cherry-pick 用法。
查看分支1的git log,获取commitID
切换到分支2,使用 git cherry-pick 分支1的某个commitID
切换到分支2,使用 git cherry-pick 分支1的某个commitID
git rebase
git合并多个本地commit作为一次提交。
(1)git rebase -i commitId(不写commitId,默认记录与远程相差的所有commit)
(2)修改需要合并的id,修改pick为squash,退出
(3)进入commit页面,填写信息
(4)push
(2)修改需要合并的id,修改pick为squash,退出
(3)进入commit页面,填写信息
(4)push
注意
一定要留下一个pick作为commit点
对于merge生成的点,本身是没有pick的,显示的pick是源commit,
如果rebase merge的内容,需要注意是否只有一个pick,那么不要将pick该为squash
如果rebase merge的内容,需要注意是否只有一个pick,那么不要将pick该为squash
分支改名
git branch -m old_branch new_branch
改完名需要再跟随一下
版本回退
reset/revert
reset/revert
reset:本地库
git reset --hard ID
版本回退到该ID已经commit的状态,但是ID跟最新版本之间的内容 消失了
git reset HEAD~2
将HEAD从顶端的commit往下移动两个更早的commit
抢救
git reflog进行版本回退
git reset --soft ID
版本回退到 该ID已经commit的状态, 但是ID跟最新版本之间的内容还在,
在缓存区,这些内容的状态是 已修改、已添加、未提交
在缓存区,这些内容的状态是 已修改、已添加、未提交
git reset ID
不加参数表示默认,相当于git reset --mixed ID
版本回退到该ID已经commit的状态,但是该ID跟最新版本之间的内容还在,
在工作区,这些内容的状态是 已修改、未添加、未提交
在工作区,这些内容的状态是 已修改、未添加、未提交
相关问题
git reset --hard 后如何修复。
(1)git reflog
(2)git reset 到某个ID
(3)git log 查看恢复的commitID
(4)git checkout . 清除所有修改。
(2)git reset 到某个ID
(3)git log 查看恢复的commitID
(4)git checkout . 清除所有修改。
revert:远程库
删除某个提交
git revert commitID
只删除某次提交,直接commit
git revert -n commitID
git revert --continue
git revert --continue
只删除某次提交,不进入commit,进入REVERTING界面
进行选择性操作后,再提交
进行选择性操作后,再提交
git撤销merge,彻底学会git revert的用法
git revert -m 1 xxxxx
git revert 之前的revert
删除某连续提交
git revert --no-commit commitID2..commitID4
注意:这是一个前开后闭区间,
即不包括 commitID2,但包括 commitID3~4
即revert保留ID2,回退ID3,4
即不包括 commitID2,但包括 commitID3~4
即revert保留ID2,回退ID3,4
注意: revert 命令会对每个撤销的 commit 进行一次提交,
--no-commit 后可以最后一起手动提交,不加会导致多个提交
--no-commit 后可以最后一起手动提交,不加会导致多个提交
git revert -m
遇见合并分之的时候,会使用-m参数,
-m参数包括1和2,1是当前你所在的分支为主,不一定是master,
2是合并来的分支,使用的时候一定要先使用git show 看清楚,
-m参数包括1和2,1是当前你所在的分支为主,不一定是master,
2是合并来的分支,使用的时候一定要先使用git show 看清楚,
dailybuild
echo "是否执行检查"
read //用于确认
//获取用户输入的查询时间
read -p "查询时间:" time
cd /d/dailybuild
listname=$(ls ./)
for line in $listname //判断循环
do
cd $line
echo "========"$line"========"
git pull --rebase
git log --after="date -d -"$time"hour +%Y-%m-%d"
cd ../
done
cd /d/dailybuild //结束返回
echo "检查结束"
read
read //用于确认
//获取用户输入的查询时间
read -p "查询时间:" time
cd /d/dailybuild
listname=$(ls ./)
for line in $listname //判断循环
do
cd $line
echo "========"$line"========"
git pull --rebase
git log --after="date -d -"$time"hour +%Y-%m-%d"
cd ../
done
cd /d/dailybuild //结束返回
echo "检查结束"
read
date -d -2hour +%Y-%m-%d
date是时间
-2hour是前2小时
注意:大写的Y ,小写的m和d
git --after 表示某个时间点之前
date是时间
-2hour是前2小时
注意:大写的Y ,小写的m和d
git --after 表示某个时间点之前
git log --after="date -d -"$time"hour +%Y-%m-%d"
可以被
git log --since=$time.day.ago 代替
可以被
git log --since=$time.day.ago 代替
git错误问题集锦
add
git add permission denied
ide 编辑器占用,没有保存等可能相关的操作
关闭ide
ide 编辑器占用,没有保存等可能相关的操作
关闭ide
子主题
子主题
update
Updates were rejected because the tip of your current branch is behind
首先新建分支跟随想要提交的远程分支,
解决可能存在的跟随问题,
1、git pull --rebase
2、解决冲突,
3、git push origin test:DEV_TEST
解决可能存在的跟随问题,
1、git pull --rebase
2、解决冲突,
3、git push origin test:DEV_TEST
pull
git pull报错:
There is no tracking information for the current branch
There is no tracking information for the current branch
是因为本地分支和远程分支没有建立联系
(使用git branch -vv 可以查看本地分支和远程分支的关联关系)
(使用git branch -vv 可以查看本地分支和远程分支的关联关系)
git branch --set-upstream-to=origin/远程分支的名字 本地分支的名字
error
error: failed to push some refs to 'xxxx'
原因是远程仓库中的文件和我们本地的仓库有差异
解决办法
git pull --rebase
git pull --rebase origin master
这里的master根据需要,替换成本地的提交分支
(注意需要对应远程分支)
(注意需要对应远程分支)
git pull origin master --allow-unrelated-histories
还存在一种原因:推代码的人不具备代码仓的创建分支权限
error: the requested upstream branch 'origin/master' does not exist
1、git fetch 或者 git pull origin master --allow-unrelated-histories
2、git branch --set-upstream-to=origin/master master
fatal
fatal: Authentication failed for
1、配置用户信息
git config --global user.name [username]
git config --global user.email [email]
2、查询用户信息
git config --list
3、如果push遇到在输入密码是熟错后,就会报这个错误fatal: Authentication failed for
解决办法:
git config --system --unset credential.helper
之后push操作就会提示输入名称和密码
git config --global user.name [username]
git config --global user.email [email]
2、查询用户信息
git config --list
3、如果push遇到在输入密码是熟错后,就会报这个错误fatal: Authentication failed for
解决办法:
git config --system --unset credential.helper
之后push操作就会提示输入名称和密码
fatal: unable to access 'https:/xxxxxxxdge.git/':
Failed to connect to github.com port 443: Timed out
Failed to connect to github.com port 443: Timed out
网络问题,切换一下网络
输入“git remote add origin”提示
fatal:remote origin already exists
fatal:remote origin already exists
先删除,再重新添加
fatal: could not read Username
fatal: could not read Username for 'https://xx.xx.xxx.com': Device not configured
或者遇见需要HTTPS输入用户名和密码的,一律是因为代理配置的问题
解决办法
打开全局配置文件
git config --global -e
添加配置信息
[url "git@git.xxx.xxx.com:"]
insteadOf = https://git.xxx.xxx.com/
[url "git@git.cxxx.xxx.com:"]
insteadOf = http://git.xxx.xxx.com/
insteadOf = https://git.xxx.xxx.com/
[url "git@git.cxxx.xxx.com:"]
insteadOf = http://git.xxx.xxx.com/
could not read Username for 'https:/xx.xx.xx.com': terminal prompts disa
配置 git 邮箱和用户名
repository
fatal: Could not read from remote repository
权限问题,找人开通权限
fatal: 'xxxxl.git':repository not found: xxxxx.git
权限问题,找人开通权限
fatal: refusing to merge unrelated histories
$git pull origin master --allow-unrelated-histories
其他问题
git am --show-current-patch,查看需要合并的代码
Git 忽略文件名大小写导致项目运行失败
查看目前状态
git config --get core.ignorecase
git config core.ignorecase false
Your configuration specifies to merge with the ref 'refs/heads/master'from t
需该.git/config 文件,加上 master
linux 远程连接ssh提示
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY解决
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY解决
远程仓库存在地址映射问题,
访问受限,类似于更换库,锁库
访问受限,类似于更换库,锁库
ssh: Could not resolve hostname github.com: No address associated with hostn
查看并修改host文件,在C:\Windows\System32\drivers\etc,
是否有重复。混乱的DNS
是否有重复。混乱的DNS
git 配置
代理问题
vim .gitconfig 或者git config --global -e
修改成
[user]
name = jove.23
email = jove.23@aaa.com
[url "xx@git.xx.com:"]
insteadOf = https://git.xx.com
[url "ssh://github@git.xx.com:1234"]
insteadOf = https://git.xx.com
name = jove.23
email = jove.23@aaa.com
[url "xx@git.xx.com:"]
insteadOf = https://git.xx.com
[url "ssh://github@git.xx.com:1234"]
insteadOf = https://git.xx.com
将本地代码上传github
免密ssh
cd ~/.ssh
检查是否已经存在 id_rsa.pub 或 id_dsa.pub 文件
创建一个 SSH key
ssh-keygen -t rsa -C "your_email@example.com"
ssh-keygen -t rsa -C "your_email@example.com"
git remote add origin xxxxxxxxxx
git branch --set-upstream-to=origin/<分支名称> master
git pull origin master --allow-unrelated-histories
git pull --rebase
git push
git push
GitHub设置ssh key后push还要输入用户名和密码
git remote remove origin
git remote add origin git@gitxxxxxxxxxme/Your_Repo_Name.git
git branch --set-upstream-to=origin/master master
git 添加commit 模板
新建一个.gitcommitmessage.txt
使用指令:git config --global commit.template /d/.gitcommitmessage.txt
指令后面是.txt的文件路径,全局声明,以后可以直接git commit然后修改后使用
Git 别名
git config --global alias.ci commit
当要输入 git commit 时,只需要输入 git ci
设置别名的时候注意,不要对已有的指令进行设置
错误
git config --global alias.log 'log --decorate'
正确
git config --global alias.l 'log --decorate'
git学习网站(趣味)
git配置邮箱
查看邮箱
git config user.email
git config --global user.name "username"
git config --global user.email "email"
git config --global user.email "email"
git查看配置信息
//查看仓库级的 config,命令:
git config –local -l
//查看全局级的 config,命令:
git config –global -l
//查看系统级的 config,命令:
git config –system -l
//查看当前生效的配置, 命令:
git config -l
git config –local -l
//查看全局级的 config,命令:
git config –global -l
//查看系统级的 config,命令:
git config –system -l
//查看当前生效的配置, 命令:
git config -l
查看 git 的版本和安装路径
git version
which git
mac下git自动补全
注意事项:根据mac的执行脚本zsh还是bash,修改git-completion.zsh还是git-completion.bash
最佳实践
r
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
github
github搜索指南
# 项目名字(name)里有 python 的
in:name python
# 名字(name)里有 python 的并且 stars 大于 3000 的
in:name python starts:>3000
# 名字(name)里有 python 的并且 stars 大于 3000 、forks 大于 200 的
in:name python starts:>3000 forks:>200
# 详情(readme)里面有 python 的并且 stars 大于 3000 的
in:readme python starts:>3000
# 描述(description)里面有 python 的并且 stars 大于 3000 的
in:description python starts:>3000
# 描述(description)里面有 python 的并且是 python 语言的
in:description python language:python
# 描述(description)里面有 python 的并且 2019-12-20 号之后有更新过的
in:description python pushed:>2019-12-20
in:name python
# 名字(name)里有 python 的并且 stars 大于 3000 的
in:name python starts:>3000
# 名字(name)里有 python 的并且 stars 大于 3000 、forks 大于 200 的
in:name python starts:>3000 forks:>200
# 详情(readme)里面有 python 的并且 stars 大于 3000 的
in:readme python starts:>3000
# 描述(description)里面有 python 的并且 stars 大于 3000 的
in:description python starts:>3000
# 描述(description)里面有 python 的并且是 python 语言的
in:description python language:python
# 描述(description)里面有 python 的并且 2019-12-20 号之后有更新过的
in:description python pushed:>2019-12-20
Github无法加载或不显示图片问题
github速度慢
http://tool.chinaz.com/dns/?type=1&host=github.com&ip=
正则表达式
练习网站:RegexOne
测试网站:RegExr
json
C#指令
C#基础指令
C#组合用法
C# 解析key值动态的json数据
C#高级
task
C# Task用法
C#异步编程看这篇就够了
DI
依赖注入在 dotnet core 中实现与使用:1 基本概念
依赖注入在 dotnet core 中实现与使用:2 使用 Extensions DependencyInjection
网关
.Net Core使用Ocelot网关(一) -负载,限流,熔断,Header转换
powershell
powershell基础指令
定义数组、哈希表、函数
powershell 传参/脚本执行顺序
字符串判空
[String]::IsNullOrEmpty
注意String首字母大写
查找指令
| where 属性名 -Like 参数名
注意管道符
where 可以用 ? 代替
| ? 属性名 -Like 参数名
文件相关
读取文件
$file = Get-Content -path "d:\1.txt" -Raw
加上raw读取更快
打印输出
Get-Process | Out-File D:\1.txt
输出powershell查询结果到D盘1.txt
时间
格式化时间
$today = Get-Date
$today.ToString('yyyy-MM-dd')
$today.ToString('yyyy-MM-dd')
计算执行时间
$start = Get-Date
Get-HotFix
$end = Get-Date
$exetime = $end - $start
write-host $exetime
Get-HotFix
$end = Get-Date
$exetime = $end - $start
write-host $exetime
注意$exetime 可以有多种时间提取格式:Seconds、min等
求和运算
| Measure-Object -Sum
注意管道符 |
查找结果使用.sum得到
样例
(get-process | | Measure-Object -Sum).sum
生成随机数
PS C:\Users\os> $Ran = New-Object System.Random
PS C:\Users\os> $Ran.Next(num 最大值)
PS C:\Users\os> $Ran.Next(num 最大值)
powershell组合用法
powershell远程登录并调用脚本
语句执行
执行由变量组成的语句
执行一行语句
$a = get-process
$result = & $a
$result = & $a
执行代码块
$multipleLine = @"
ping pstips.net
get-Date
"@
$code = [scriptblock]::Create($multipleLine)
& $code
ping pstips.net
get-Date
"@
$code = [scriptblock]::Create($multipleLine)
& $code
注意:[scriptblock]::Create
base64编码
编码
# Convert string to base64 string
function ConvertTo-Base64String([string]$string){
$byteArray = [System.Text.UnicodeEncoding]::Unicode.GetBytes($string)
[Convert]::ToBase64String( $byteArray )}
function ConvertTo-Base64String([string]$string){
$byteArray = [System.Text.UnicodeEncoding]::Unicode.GetBytes($string)
[Convert]::ToBase64String( $byteArray )}
解码
# Convert base64 string to string
function ConvertFrom-Base64String([string]$string){
$byteArray = [Convert]::FromBase64String($string)
[System.Text.UnicodeEncoding]::Unicode.GetString($byteArray)}
function ConvertFrom-Base64String([string]$string){
$byteArray = [Convert]::FromBase64String($string)
[System.Text.UnicodeEncoding]::Unicode.GetString($byteArray)}
cmd bash
bat基础指令
查询
查询端口占用
netstat -ano | findstr 80
基本语法
set
set X=5,就是令X=5的意思
set /p commit=please input commit:
rem
注释命令,在C语言中相当于/*----------*/,它并不会被执行,
只是起到一个注释的作用,便于别人及自己将来阅读和维护脚本
只是起到一个注释的作用,便于别人及自己将来阅读和维护脚本
::命令与rem是一样
@echo off
表示执行了这条命令后关闭所有命令(包括本身这条命令)的回显
echo off命令则表示关闭其他所有命令(不包括本身这条命令)的回显,@的作用就是关闭紧跟其后的一条命令的回显
格式:
FOR /F ["options"] %%i IN (file) DO command
FOR /F ["options"] %%i IN ("string") DO command
FOR /F ["options"] %%i IN ('command') DO command
FOR /F ["options"] %%i IN ("string") DO command
FOR /F ["options"] %%i IN ('command') DO command
for循环
bat for /f用法
FOR /F %%i IN (file) DO command
在cmd窗口中:for %I in (command1) do command2
在批处理文件中:for %%I in (command1) do command2
在批处理文件中:for %%I in (command1) do command2
tokens skip /f delims eol 的用法
timeout
实现sleep功能
timeout /T 延迟秒数
单位是s
findstr
类似grep的命令
| findstr xxx
start
直接打开某应用
start http://spdbclacs01.spdbcl.com
tree
if
telnet
如何开启telnet
“启动或关闭Windows功能”界面,勾选telnet客户端,点击确定
如何退出telnet
CTRL+]键
然后按 q
如何退出telnet
CTRL+]键
然后按 q
windows如何打开22port
点击“启用或关闭Windows Defender 防火墙”,将Windows Defender 防火墙启用
点击“高级设置”,→“入站规则”→“新建规则”→“端口”→ “下一步”
测试端口是否能用
脚本
rem display echo
setlocal enabledelayedexpansion
rem file location
set pathip=C:\Users\Administrator\Desktop\iptxt.txt
set pathport=C:\Users\Administrator\Desktop\porttxt.txt
rem judge file is exist
if not exist %pathip% (
echo iptxt.txt file not exist
goto end
)
if not exist %pathport% (
echo porttxt.txt file not exist
goto end
)
rem read file, read line at once
for /f %%i in (%pathport%) do (
for /f %%j in (%pathip%) do (
start /min cmd.exe /k "echo q|telnet -e 'q' %%j %%i & exit"
ping -n 3 127.1>nul
tasklist /fi "windowtitle eq telnet %%j" | find "cmd.exe" >nul && (
set portfail=%%j:%%i--------!strfail!
) || (
set portpass=%%j:%%i--------!strpass!
)
)
rem delay 15s
timeout /T 15
)
echo !portpass!>pass.txt
echo !portfail!>fail.txt
:end
pause
setlocal enabledelayedexpansion
rem file location
set pathip=C:\Users\Administrator\Desktop\iptxt.txt
set pathport=C:\Users\Administrator\Desktop\porttxt.txt
rem judge file is exist
if not exist %pathip% (
echo iptxt.txt file not exist
goto end
)
if not exist %pathport% (
echo porttxt.txt file not exist
goto end
)
rem read file, read line at once
for /f %%i in (%pathport%) do (
for /f %%j in (%pathip%) do (
start /min cmd.exe /k "echo q|telnet -e 'q' %%j %%i & exit"
ping -n 3 127.1>nul
tasklist /fi "windowtitle eq telnet %%j" | find "cmd.exe" >nul && (
set portfail=%%j:%%i--------!strfail!
) || (
set portpass=%%j:%%i--------!strpass!
)
)
rem delay 15s
timeout /T 15
)
echo !portpass!>pass.txt
echo !portfail!>fail.txt
:end
pause
释义
每次新建一个cmd窗口,通过telnet测试ip和port是否联通,
通的IP和PORT会直接退出并关闭窗口,
不通的窗口会持续一段时间,
每次循环通过判断仍然存在的窗口数量,找出不同的端口。
ping -n 3 127.1>nul 用于延时,数字3越大时间延续越长
通的IP和PORT会直接退出并关闭窗口,
不通的窗口会持续一段时间,
每次循环通过判断仍然存在的窗口数量,找出不同的端口。
ping -n 3 127.1>nul 用于延时,数字3越大时间延续越长
注意点
为什么外层循环是port,内层循环是ip
因为判断依据是测试依旧存在的cmd窗口的名称,
而这个名称只显示到ip,所以存在通的端口因为之前存在的不通的端口,而被误判为不通,
例如,IP1:20不通,IP1:30通,但是存在IP1的窗口,因此误判30端口不通。
而这个名称只显示到ip,所以存在通的端口因为之前存在的不通的端口,而被误判为不通,
例如,IP1:20不通,IP1:30通,但是存在IP1的窗口,因此误判30端口不通。
解决办法:内层先循环IP,测试的是同一个端口,这样IP不会引起误判,
只要内层循环持续时间超过1分钟左右,窗口关闭,不会印象测试下一个端口
只要内层循环持续时间超过1分钟左右,窗口关闭,不会印象测试下一个端口
为什么不通的端口,也会闪退
端口存在三中状态:
未开通
服务拒绝
开通未占用
连接超时
开通
进入telnet内部
Windows测试TCP
Linux下测试其他端口
Windows测试UDP
bat组合用法
使用bat执行git命令
echo "是否开始同步"
read
cd /d/workspace
reponame=$(ls ./)
for line in $reponame
do
cd $line
echo "========"$line"=========status===="
git status
cd ../
done
cd /d/workspace
read
read
cd /d/workspace
reponame=$(ls ./)
for line in $reponame
do
cd $line
echo "========"$line"=========status===="
git status
cd ../
done
cd /d/workspace
read
直接在桌面上sh脚本
BAT读取文件
BAT命令大全
bat输出重定向
将正常异常输出到同一文件,dir file.c 1> output.msg 2>&1 用“2>&1”将标准错误输出重定向到标准输出
%%i拼接字符串
延时
rem read file, read line at once
for /f %%i in (%path1%) do (
set str1=%%i!str1!
)
echo !str1!>1.txt
for /f %%i in (%path1%) do (
set str1=%%i!str1!
)
echo !str1!>1.txt
非延时
str=%str1%%str2%
bash高级使用,github
问题
ipconfig显示不是命令
修改环境变了的path,
一般在C:\Windows\System32;
一般在C:\Windows\System32;
查看电脑内存的型号
cmd 中输入 wmic
memorychip
cpu get
md语法
代码块
~~~Java
public static void main(){}
~~~
public static void main(){}
~~~
单行代码
~public static void main~
添加链接
[www.abidu.com](百度网址)
标题
大标题
#
井号和空格
小标题
+/-
符号前置空格
MarkDown中实现目录页面内跳转
锚点:
[跳转到个人简介](#个人简介)
[跳转到个人简介](#个人简介)
锚点目标:
<p id="个人简介">这是个人简介</p>
<p id="个人简介">这是个人简介</p>
Python
python基础指令
jupyter notebook
python组合用法
代码练习,在线运行
开发、部署
开发
实际解决方案集锦
校验json文件是否符合目标模板
1.读取json文件的所有key
2.递归遍历其中value为json的key,拼接字符串
Ablock实现原理
Ablock实现原理
修改/etc/hosts
本地域名解析规则
本机要访问某个域名,首先会到这个文件里查找,
有没有这个域名对应的IP地址,如果有直接就访问这个IP地址了,
如果没有才会去向DNS服务器发出域名解析请求
有没有这个域名对应的IP地址,如果有直接就访问这个IP地址了,
如果没有才会去向DNS服务器发出域名解析请求
屏蔽广告原理
广告都是后台从网络服务器上下载广告资源,然后再在本机显示
下载过程就必须要涉及到域名解析过程
在域名解析阶段,返回错误的IP地址,那么广告资源也就无法下载
做法
修改了系统的/etc/hosts文件
让广告域名都指向本机IP地址
页面注入去除广告
ksremote.jar
ksremote.dex
adclose.png
libksrootclient.so
ksremote.dex
KsRemoteCtrl类
SetAppHook()中去执行
1) 加载图片资源:adclose.png
2) 反射得到ActivityThread类的currentActivityThread()获得当前ActivityThread对象
3) 加载/data/data/com.ijinshan.duba/app_ctrl/libksrootclient.so,并调用native方法StartSocketHook()。这里是在Native层对网络操作函数加Hook。
4) 反射获得当前ActivityThread对象的mInitialApplication成员(Application 类型),从而获得当前包名(Context.getPackageName())
5) 如果发现自己不是"com.ijinshan.duba",就开始更新广告规则
6) 替换当前ActivityThread中的mH(Handler类型)的mCallback,用金山自定义的一个callback对象来包裹过原callback并且替换原callback,从而起到hook作用。
拦截的消息有
ACTIVITYBIND_APPLICATION
RESUME_ACTIVITY
PAUSE_ACTIVITY
RECEIVER
过程
在广告应用中某个Activity的onResume()触发前,注入的代码会先遍历当前Activity里的所有View,
一旦找到广告View,就会调用addView()将添加到广告View的附近。
一旦找到广告View,就会调用addView()将添加到广告View的附近。
如何找到view
判断当前View是不是ViewGroup
1. 非ViewGroup情况下,只用类名规则匹配
2. ViewGroup情况下,以此用类名规则和WebView规则匹配,如果还未匹配上,则用排除规则排除PhoneWindow的情况。接下来就是遍历当前ViewGroup的所有子View,然后递归调用a()。
黑名单是通过IPC从手机毒霸的主进程哪里获得的
广告匹配过程中一共使用了3种规则
1. 类名规则:当前View对象的类名如果与黑名单中的类名匹配,那么就认定为广告。
2. WebView规则:当前View对象是WebView并且满足一定的规则,那么就认定为广告。
3. 排除规则:当ViewGroup对象的类名如含有” com.android.internal.policy.impl.PhoneWindow”, 那么就肯定不是广告。
在Activity的onPause()触发前,会做一些清理工作。
规则
①大部分都是通过后缀来区别的,后缀好像都是高于8个的那种,通过在后缀中选取8个连续的字符当键,
然后把对应的规则写到哈希表的值里面,查的时候算法很简单,就是连续抽取8个连续的字符串查找哈希表;
然后把对应的规则写到哈希表的值里面,查的时候算法很简单,就是连续抽取8个连续的字符串查找哈希表;
②这部分一般是使用者自己书写的规则,就是在规则前面加上@@…………,
表示遇到对应这些规则的链接不阻挡,至于查询算法同上。
表示遇到对应这些规则的链接不阻挡,至于查询算法同上。
③这部分就是通配符匹配(我所需要的就是这部分的算法),这部分主要处理的就是类似于域名的通配符规则,
例如:www.baidu.*.com 然后对这些规则通过域名切割,切割成一个一个subdomain:
www ,baidu ,com ,subdomain是“ * ”的或者含有的都删除,然后再把第一个域名还有最后一个域名去掉,
我给的例子只剩下一个“baidu”然后用这个字符串当做键,查询对应的哈希表中是否已经含有,
如果含有查看其一个键对应的N个值得N是否已经达到上限,如果没有直接写入,如果达到上限了就用下一个字符串
当做键继续按照上述查找(所给的例子通过域名切割之后只剩下一个字符串当做键,正常情况下可能有多于一个的情况),
如果所得到的字符串当做键所对应的N都已经达到上限,那就把这条规则写入特殊规则处理的地方,这个可以因人而异,
我的处理是写入一个动态数组到时候遍历查找(Adblock plus和我的实现差不多,区别是它使用js实现这些的)。
例如:www.baidu.*.com 然后对这些规则通过域名切割,切割成一个一个subdomain:
www ,baidu ,com ,subdomain是“ * ”的或者含有的都删除,然后再把第一个域名还有最后一个域名去掉,
我给的例子只剩下一个“baidu”然后用这个字符串当做键,查询对应的哈希表中是否已经含有,
如果含有查看其一个键对应的N个值得N是否已经达到上限,如果没有直接写入,如果达到上限了就用下一个字符串
当做键继续按照上述查找(所给的例子通过域名切割之后只剩下一个字符串当做键,正常情况下可能有多于一个的情况),
如果所得到的字符串当做键所对应的N都已经达到上限,那就把这条规则写入特殊规则处理的地方,这个可以因人而异,
我的处理是写入一个动态数组到时候遍历查找(Adblock plus和我的实现差不多,区别是它使用js实现这些的)。
Rabin-Karp算法
判断字符串A中是否有字符串B的一个算法,不过由于js或者别的字符串匹配效率不高的语言处理可能复杂度比较高,所以这个算法是通过把字符串匹配转换成整数比较大小的形式来判断的。例如ABCDEFGHJ转换成123456789,然后进行比较。例如字符串a为“ABCFE”转换成整数12365 字符串b为“BC”转换成23,匹配的时候依次从第一个整数最低位取得两位数和23比较,当然,我的这种举例是最简单最基础的形式,这个算法还有改进的空间就是通过用记忆算法来实现,具体就补多少了。
开发工具
部署
项目部署问题集锦
SDN中的OverLay与UnderLay技术
UnderLay
物理网络,
它由物理设备和物理链路组成。
常见的物理设备有交换机、路由器、防火墙、负载均衡、入侵检测、行为管理等,
这些设备通过特定的链路连接起来形成了一个传统的物理网络,
这样的物理网络,我们称之为UnderLay网络
它由物理设备和物理链路组成。
常见的物理设备有交换机、路由器、防火墙、负载均衡、入侵检测、行为管理等,
这些设备通过特定的链路连接起来形成了一个传统的物理网络,
这样的物理网络,我们称之为UnderLay网络
OverLay
windows2008服务器设置系统启动时程序自动运行
高可用、双活、灾备的区别和联系
服务端高并发分布式架构演进之路
1、单机架构
图解
架构方案
以淘宝作为例子。在网站最初时,应用数量与用户数都较少,
可以把Tomcat和数据库部署在同一台服务器上。
浏览器往www.taobao.com发起请求时,
首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,
浏览器转而访问该IP对应的Tomcat。
可以把Tomcat和数据库部署在同一台服务器上。
浏览器往www.taobao.com发起请求时,
首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,
浏览器转而访问该IP对应的Tomcat。
存在的问题
随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务
改进方案
Tomcat与数据库分开部署
涉及的技术点
Tomcat
数据库
2、Tomcat与数据库
分开部署
分开部署
图解
架构方案
Tomcat和数据库分别独占服务器资源,
两者通过restful方式访问,
显著提高两者各自性能
两者通过restful方式访问,
显著提高两者各自性能
存在的问题
随着用户数的增长,并发读写数据库成为瓶颈
改进方案
引入本地缓存和分布式缓存
涉及的技术点
restful风格的编码和访问方式
3、引入本地缓存
和分布式缓存
和分布式缓存
图解
架构方案
在Tomcat同服务器上或同JVM中增加本地缓存,
并在外部增加分布式缓存
并在外部增加分布式缓存
缓存热门商品信息或热门商品的html页面等。
通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。
通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。
其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,
还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题
还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题
存在的问题
缓存抗住了大部分的访问请求,随着用户数的增长,
并发压力主要落在单机的Tomcat上,响应逐渐变慢
并发压力主要落在单机的Tomcat上,响应逐渐变慢
解决方案
引入反向代理实现负载均衡
涉及的技术点
memcached
Redis
4、引入反向代理
实现负载均衡
实现负载均衡
图解
架构方案
在多台服务器上分别部署Tomcat,
使用反向代理软件(Nginx)
把请求均匀分发到每个Tomcat中
使用反向代理软件(Nginx)
把请求均匀分发到每个Tomcat中
此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,
那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。
那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。
其中涉及的技术包括:Nginx、HAProxy,
两者都是工作在网络第七层的反向代理软件,
主要支持http协议,还会涉及session共享、文件上传下载的问题
两者都是工作在网络第七层的反向代理软件,
主要支持http协议,还会涉及session共享、文件上传下载的问题
存在的问题
反向代理使应用服务器可支持的并发量大大增加,
但并发量的增长也意味着更多请求穿透到数据库,
单机的数据库最终成为瓶颈
但并发量的增长也意味着更多请求穿透到数据库,
单机的数据库最终成为瓶颈
解决方案
数据库读写分离
涉及的技术点
Nginx
HAProxy
session共享
文件上传下载
5、数据库读写分离
图解
架构方案
把数据库划分为读库和写库
读库可以有多个,通过同步机制把写库的数据同步到读库
对于需要查询最新写入数据场景,
可通过在缓存中多写一份,通过缓存获得最新数据
可通过在缓存中多写一份,通过缓存获得最新数据
其中涉及的技术包括:Mycat,它是数据库中间件,
可通过它来组织数据库的分离读写和分库分表,
客户端通过它来访问下层数据库,
还会涉及数据同步,数据一致性的问题
可通过它来组织数据库的分离读写和分库分表,
客户端通过它来访问下层数据库,
还会涉及数据同步,数据一致性的问题
存在的问题
业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能
解决方案
数据库按业务分库
涉及的技术点
Mycat
数据同步
数据一致性
消息队列
6、数据库按业务分库
图解
架构方案
把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,
对于访问量大的业务,可以部署更多的服务器来支撑。
对于访问量大的业务,可以部署更多的服务器来支撑。
存在的次要问题
这样同时导致跨业务的表无法直接做关联分析,
需要通过其他途径来解决,
但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案
需要通过其他途径来解决,
但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案
存在的问题
随着用户数的增长,单机的写库会逐渐会达到性能瓶颈
解决方案
把大表拆分为小表
涉及的技术点
数据库分库分表
分库分表关联分析
7、把大表拆分为小表
图解
架构方案
同类业务
拆分成小表
拆分成小表
针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;
针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据
只要实时操作的表数据量足够小,
请求能够足够均匀的分发到多台服务器上的小表,
那数据库就能通过水平扩展的方式来提高性能。
其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。
请求能够足够均匀的分发到多台服务器上的小表,
那数据库就能通过水平扩展的方式来提高性能。
其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。
这种做法显著的增加了数据库运维的难度,对DBA的要求较高。
数据库设计到这种结构时,已经可以称为分布式数据库,
但是这只是一个逻辑的数据库整体,
数据库里不同的组成部分是由不同的组件单独来实现的,
这种架构其实是MPP(大规模并行处理)架构的一类实现。
数据库设计到这种结构时,已经可以称为分布式数据库,
但是这只是一个逻辑的数据库整体,
数据库里不同的组成部分是由不同的组件单独来实现的,
这种架构其实是MPP(大规模并行处理)架构的一类实现。
分布式数据库
MPP数据库
开源和商用
开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等
商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等
不同的MPP数据库的侧重点也不一样,
如TiDB更侧重于分布式OLTP场景,
Greenplum更侧重于分布式OLAP场景
支持能力
这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,
能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,
最终由数据库本身汇总数据进行返回
能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,
最终由数据库本身汇总数据进行返回
也提供了诸如权限管理、分库分表、事务、数据副本等能力
集群
并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,
并且使数据库也能够实现水平扩展
并且使数据库也能够实现水平扩展
MPP(大规模并行处理)架构的实现
如分库分表的管理和请求分发,由Mycat实现,
SQL的解析由单机的数据库实现,
读写分离可能由网关和消息队列来实现,
查询结果的汇总可能由数据库接口层来实现等等,
存在的问题
数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈
解决方案
使用LVS或F5来使多个Nginx负载均衡
涉及的技术点
MPP数据库
8、使用LVS或F5
来使多个Nginx负载均衡
来使多个Nginx负载均衡
图解
架构方案
由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。
图中的LVS和F5是工作在网络第四层的负载均衡解决方案
图中的LVS和F5是工作在网络第四层的负载均衡解决方案
LVS
LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,
因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;
因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;
F5
F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵
VIP
由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点
可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,
浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器
浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器
当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,
把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果
把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果
此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat,
在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,
其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加
在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,
其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加
存在的问题
由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,
用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同
用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同
解决方案
通过DNS轮询实现机房间的负载均衡
涉及的技术点
LVS
F5
9、通过DNS轮询
实现机房间的负载均衡
实现机房间的负载均衡
图解
架构方案
在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。
当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡
至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题
存在的问题
随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求
解决方案
引入NoSQL数据库和搜索引擎等技术
涉及的技术点
10、引入NoSQL数据库
和搜索引擎等技术
和搜索引擎等技术
图解
架构方案
当数据库中的数据多到一定规模时,
数据库就不适用于复杂的查询了,
往往只能满足普通查询的场景
数据库就不适用于复杂的查询了,
往往只能满足普通查询的场景
数据库的缺点
统计报表场景
在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢
全文检索、可变数据结构等场景
数据库天生不适用
本方案的优点
海量文件存储
可通过分布式文件系统HDFS解决
key value类型的数据
可通过HBase和Redis等方案解决
全文检索场景
可通过搜索引擎如ElasticSearch解决
多维分析场景
可通过Kylin或Druid等方案解决
引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,
需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等
需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等
存在的问题
引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难
解决方案
大应用拆分为小应用
涉及的技术点
11、大应用拆分为小应用
图解
架构方案
按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。
这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决
存在的问题
不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级
解决方案
复用的功能抽离成微服务
涉及的技术点
12、复用的功能
抽离成微服务
抽离成微服务
图解
架构方案
如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,
这样的服务就是所谓的微服务
这样的服务就是所谓的微服务
应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理
可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性
存在的问题
不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,
此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱
此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱
解决方案
引入企业服务总线ESB屏蔽服务接口的访问差异
涉及的技术点
13、引入企业服务总线ESB
屏蔽服务接口的访问差异
屏蔽服务接口的访问差异
图解
架构方案
通过ESB统一进行访问协议转换,
应用统一通过ESB来访问后端服务,
服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。
应用统一通过ESB来访问后端服务,
服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。
这种单个应用拆分为多个应用,
公共服务单独抽取出来来管理,
并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,
公共服务单独抽取出来来管理,
并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,
这种架构与微服务架构容易混淆,因为表现形式十分相似。个人理解,
微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,
而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想
微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,
而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想
存在的问题
业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,
同一台服务器上部署多个服务还要解决运行环境冲突的问题,
此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,
就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难
同一台服务器上部署多个服务还要解决运行环境冲突的问题,
此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,
就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难
解决方案
引入容器化技术实现运行环境隔离与动态服务管理
涉及的技术点
14、引入容器化技术
实现运行环境隔离
与动态服务管理
实现运行环境隔离
与动态服务管理
图解
架构方案
目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),
应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。
Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,
运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,
直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单
应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。
Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,
运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,
直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单
在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,
对机器上的其他服务不造成影响
(在此之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。
对机器上的其他服务不造成影响
(在此之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。
存在的问题
使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,
在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低
在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低
解决方案
以云平台承载系统
涉及的技术点
15、以云平台承载系统
图解
架构方案
系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,
在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,
在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本
在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,
在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本
所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,
在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,
提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,
用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)
在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,
提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,
用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)
云平台
IaaS
基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面
PaaS
平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;
SaaS
软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费
架构设计的原则
N+1设计。系统中的每个组件都应做到没有单点故障;
CI/CD
CI
CD
实用分支管理策略
实用小技巧
视频下载
如何下载网页上的视频?
You-get
密钥
Windows 7旗舰版产品密钥
Professional
HWRFF-2FFYX-XFXP2-DYFC3-BX3B7
YT9K9-4R938-3TVXX-3Q3QT-9HBXM
C3X7Y-R6WWH-BRXRD-FY84C-FXWHK
XJBR4-M42Q4-QPJ9C-BRDRJ-KHPVY
TF3Q7-YYP8R-D78R7-W9Q9M-DXVBK
J8D39-J2WM3-6368H-JV8G9-BYJJQ
P3H89-V3P2R-JVBTF-YM2J2-FTMT3
RGM4T-3VT6B-GTYPY-3FHP2-HV2YJ
TTY4D-RDKK9-TYB2T-68WJW-M69KJ
BWPX2-XK2T8-3GV2W-KHQVP-QXCDV
YT9K9-4R938-3TVXX-3Q3QT-9HBXM
C3X7Y-R6WWH-BRXRD-FY84C-FXWHK
XJBR4-M42Q4-QPJ9C-BRDRJ-KHPVY
TF3Q7-YYP8R-D78R7-W9Q9M-DXVBK
J8D39-J2WM3-6368H-JV8G9-BYJJQ
P3H89-V3P2R-JVBTF-YM2J2-FTMT3
RGM4T-3VT6B-GTYPY-3FHP2-HV2YJ
TTY4D-RDKK9-TYB2T-68WJW-M69KJ
BWPX2-XK2T8-3GV2W-KHQVP-QXCDV
Ultimate
J783Y-JKQWR-677Q8-KCXTF-BHWGC
C4M9W-WPRDG-QBB3F-VM9K8-KDQ9Y
2VCGQ-BRVJ4-2HGJ2-K36X9-J66JG
MGX79-TPQB9-KQ248-KXR2V-DHRTD
FJHWT-KDGHY-K2384-93CT7-323RC
THHH2-RKK9T-FX6HM-QXT86-MGBCP
KH2J9-PC326-T44D4-39H6V-TVPBY
D8BMB-BVGMF-M9PTV-HWDQW-HPCXX
TFP9Y-VCY3P-VVH3T-8XXCC-MF4YK
6MHH-TRRPT-74TDC-FHRMV-XB88W
C4M9W-WPRDG-QBB3F-VM9K8-KDQ9Y
2VCGQ-BRVJ4-2HGJ2-K36X9-J66JG
MGX79-TPQB9-KQ248-KXR2V-DHRTD
FJHWT-KDGHY-K2384-93CT7-323RC
THHH2-RKK9T-FX6HM-QXT86-MGBCP
KH2J9-PC326-T44D4-39H6V-TVPBY
D8BMB-BVGMF-M9PTV-HWDQW-HPCXX
TFP9Y-VCY3P-VVH3T-8XXCC-MF4YK
6MHH-TRRPT-74TDC-FHRMV-XB88W
Home Basic
P4DBR-8YPT6-KHRB8-6T7RW-GMXGV
FGTCF-8JBG2-4BK4G-36JWB-PFQXB
CW4KD-MK47X-JYQ7Y-DKKTR-86TH7
37X8Q-CJ46F-RB8XP-GJ6RK-RHYT7
GDK6B-87QP9-F9WYK-PP327-BQ622
72C8D-KQ9Y4-FGBCD-WY9WG-BD92C
GV7X4-92M4D-6F69V-RFGP9-3FBBD
4JCWB-FVHJJ-XCPKC-CTWDP-QQQ9M
WXM3Y-H2GDY-TKFQH-6GQQF-7VG8P
V6V3G-9DB2T-BD4VC-44JVQ-6BVR2
FGTCF-8JBG2-4BK4G-36JWB-PFQXB
CW4KD-MK47X-JYQ7Y-DKKTR-86TH7
37X8Q-CJ46F-RB8XP-GJ6RK-RHYT7
GDK6B-87QP9-F9WYK-PP327-BQ622
72C8D-KQ9Y4-FGBCD-WY9WG-BD92C
GV7X4-92M4D-6F69V-RFGP9-3FBBD
4JCWB-FVHJJ-XCPKC-CTWDP-QQQ9M
WXM3Y-H2GDY-TKFQH-6GQQF-7VG8P
V6V3G-9DB2T-BD4VC-44JVQ-6BVR2
windows操作系统镜像
0 条评论
下一页