11月17日,网传万科在内部下发一份《关于万科集团总部“节衣缩食”和打造“战时氛围”的倡议》,提出要转变黄金时代的惯性思维方式,将经营理念贯穿始终,对不产生价值的动作和开支做减法,花小钱办大事等工作要求。为减少不必要的开支,万科要求管理层以身作则,勤俭节约,杜绝浪费。比如,按费用最优原则合理规划差旅出行、人走关灯等。同时,避免内部交往过度接待,拒绝不必要的接送、宴请或伴手礼等,杜绝一线单位逢年过节给总部馈赠礼品或特产。万科没有回应记者的求证,有公司员工称:属实,以邮件发送。
万科内部发文:节衣缩食筹备过冬
部分内容如下:

一、主要工作要求

1、转变黄金时代的惯性思维方式,在工作和决策中将经营理念贯穿始终;

2、对不产生价值的动作和开支做减法,花小钱办大事;

3、明确要实现的目标,形成行动方案并坚决执行;

4、不给一线添乱,助力业务打赢;

5、培养当家人意识,以身作则,自上而下形成示范效应;

6、把握尺度,内外有别,既要让利益相关方感受舒服,也要关注员工的情绪;

7、长期坚持,过程中及时复盘反思。

二、具体行动

1、打铁还需自身硬,坚决不当二传手。落实在实际工作场景中的对照行为:

(1)评估工作中外包业务的必要性,自己力所能及的事项自己完成,拒绝当“甩手掌柜”和“二传手”,杜绝只做表面文章和纸上谈兵;

(2)通过多学习,勤思考,深实践,不断提升个人能力。眼睛向下、身子下沉,真正了解业务痛点难点,帮助一线解决问题,助力业务打赢;

(3)工作上的关注点不能只是“我做了”的执行层面,更要聚焦“做的结果如何”对过程质量负责,对结果收效负责,鼓励“捞过界”“勤补位”;

2、严于律己勇担当,以身作则做表率。管理层要严于律己,以身作则,主动担当,为员工做好表率。员工个人也要从自己的一言一行做起,关注细节,谦虚谨慎,敢于涌现挑重担。

落实在实际工作场景中的对照行为:

(1)基于费用最优原则合理规划差旅出行,例如:提前订票降低购票成本;综合价格优惠和时段选择舱位;依据性价比最优原则选择住宿酒店;

(2)日常工作厉行节约、拒绝浪费,在细节中做到勤俭节约,如文件是否需要打印,根据天气温度调整室温,人走关灯等;

(3)避免内部交往过度接待,总部与一线单位交流轻车简从,拒绝不必要的接送、宴请或伴手礼等,杜绝一线单位逢年过节给总部馈赠礼品或特产;

(4)乘承合适原则对外交往,必要产品不可缺,其他产品在同等标准下优选性价比更高的,既保证合适得体,又不铺张浪费。

nmap 技巧
基本nmap 命令
0x01:无任何附加参数

nmap IP地址

分情况。如果是超级用户,无参数扫描等价于 sS 参数扫描(SYN,半连接);否则,无参数扫描等价于 sT 参数扫描(TCP,完整连接)。

0x02:冗余

nmap -vv IP地址

按照基本法,v 参数通常表示冗余。我们使用两个 v 参数表示将侦测过程原原本本的打印输出出来。

0x03:指定端口号

nmap -p端口号 IP地址

这里 p 参数表示端口,标准写法后面跟的端口号之间没有空格。但是如果写一个空格也并无妨。

0x04:操作系统侦测

nmap -O IP地址
nmap -A IP地址

操作系统侦测有两个参数选项,其一是参数 O,其二是参数 A,后者乃前者的冗余版本。我更多的使用 A 参数,以得到更多的信息。

0x05:只进行主机发现

nmap -sn IP地址

主机发现的手段不下几十种,但是最常用的却是 sn 参数,它表示 “使用 ping 扫描来侦测存活的主机,而不进行端口扫描”。

0x06:跳过主机发现

nmap -Pn IP地址

有时候对方主机开启了防火墙(这是很自然的事情),可能过滤掉了你发送的 ICMP 协议数据包,这样如果想要使用 sn 参数来进行主机发现就不管用了,产生的结果也不可靠。于是你不得不使用 Pn 参数,它假设所有的目标 IP 均为存活,并一个一个主机的进行端口扫描,你懂的这样会牺牲一些时间作为代价。

0x07:扫描和版本号侦测

nmap -sV IP地址

该选项通过侦测开放的端口来判断开放的服务,并试图检测它的版本。虽然 A 选项也能做到,但是要检测开放的服务版本,sV 一定是最合适的。

0x08:UDP 扫描

nmap -sU IP地址

之前我们的扫描都是针对 TCP 的,而有些服务却是建立在 UDP 协议上的。比如 NTP(123端口)、SNMP(161端口)等服务,就必须使用 UDP 协议进行扫描。

nmap隐藏IP地址

namp -D IP地址1,IP地址2... IP地址

你可以使用 D 选项(英文 decoy)跟一些 IP 地址,IP 和 IP 之间用逗号隔开。这样看起来用来侦测而发送的数据包不仅来自于你的 IP 地址,还来自于这些掩体 IP。这就叫做 “混入其中”。

nmap禁用 ping

nmap -P0 IP地址

在 2010 年之后,该选项和 PN 选项被一起合并到 Pn 选项之中。但是如果你愿意,你仍然可以使用 P0 选项(P 后面跟的是零)。

nmap IP 地址伪装

sudo proxychains nmap ...

伪装 IP 地址的方法也有很多,比如你可以使用 prxychains 这款工具来实现匿名代理。

nmap 空闲扫描

nmap -sI 僵尸IP地址[:开放的僵尸端口] IP地址

和 D 选线不同的是,sI 根本不使用你自己的 IP 地址,而是使用空闲的网络资源。这样隐蔽性就更强了。开放的僵尸端口为选填,默认等于 80 端口。具体原理请参考 Nmap 文档。根据这个理论,你不能使用空闲扫描来扫描你自己的主机 IP。在 msfconsole 中,你可以使用 auxiliary/scanner/ip/ipidseq 来完成这个工作。

nmap 指定网卡进行扫描

nmap -e 网卡 IP地址

当你拥有不止一个网卡的时候,这很有用。

nmap 限制扫描时间

nmap --host-timeout 时间 IP地址

限制每个 IP 地址的扫描时间(单位为秒),当要扫描大量的主机 IP 时这很有用。

nmap 指定源 IP 地址

nmap -S 源IP地址 IP地址

使用冒充的 IP 地址进行扫描以增强隐蔽性。这里伪装成的 IP 也可以来自于下线状态的主机地址。

nmap 指定源主机端口

nmap -g 53 IP地址

使用 g 参数,或者 source-port 参数,来手动设定用来扫描的端口。常用的,如 20、53、67 端口。

nmap 数据包分片技术

nmap -f IP地址
nmap --mtu mtu单元大小 IP地址

上面两种方法都可以利用数据包分片技术,某些防火墙为了加快处理速度而不会进行重组处理,这样从而逃脱防火墙或闯入检测系统的检测。注意,mtu 的值必须是 8 的倍数(如 8、16、24、32 等)。

nmap 添加垃圾数据

nmap --data-length 垃圾数据长度 IP地址

一些常见的扫描之数据包是有特定的数据长度的,通过在发送的数据包末尾添加随机的垃圾数据,以达到混淆视听的作效果。

nmap 随机选择扫描对象

nmap --randomize-hosts IP地址

如果你要扫描大量的,比如成百上千的主机 IP,这很有效。它会打乱扫描顺序,以规避检测系统的检测。

nmap 伪装 MAC 地址

nmap --spoof-mac 伪造MAC IP地址

你可以通过指定供应商的名字来伪装 MAC 地址。可选的名字有 Dell、Apple、3Com。当然也可以手动指定 MAC 地址的值。或者为了简单起见,可以在上面 “伪造IP” 的地方填写数字 0,这将生成一个随机的 MAC 地址。

nmap 伪造检验值

nmap --badsum IP地址

这将使用伪造的 TCP / UDP / SCTP 校验和发送数据。

nmap 扫描速度

nmap -T0 IP地址

T后面跟的数字代表扫描速度,数字越大则速度越快。

Nmap 脚本引擎
Nmap 脚本引擎内置在 Nmap 中,使用 script 参数进行调用。它的英文名是 Nmap Script Engine,简称 NSE。

Nmap 内置了一些已经写好的脚本,在 Kali 等主流渗透系统中被保存在 /usr/share/nmap/scripts/ 文件夹下。文件后缀名是 .nse。使用 sC(等价于 script=default)或者 script 参数对 Nmap 脚本进行调用。

nmap 按类别扫描

nmap --script=类别 IP地址

Nmap 的脚本类别分为以下几类。

  • auth:负责处理鉴权证书、绕开鉴权的脚本。
  • broadcast:处理在局域网内探查更多服务开启的状况,如 dhcp / dns / sqlserver 等服务。
  • brute:提供暴力破解方式,针对常见的应用如 http / snmp 等。
  • default:使用 sC 或 A 选项时默认的脚本,提供基本脚本扫描能力。
  • discovery:挖掘更多的网络服务信息,如 smb 枚举、snmp 查询等。
  • dos:用于进行拒绝服务攻击。
  • exploit:利用已知的漏洞入侵系统。
  • external:利用第三方的数据库或资源,如进行 whois 解析。
  • fuzzer:模糊测试脚本,发送异常的包到目标主机,探测出潜在的漏洞。
  • malware:探测目标是否感染了病毒,是否开启了后门。
  • safe:与 fuzzer 功能相反,属于安全性脚本。
  • version:负责增强信性服务与版本扫描功能的脚本。
  • vuln:负责检查目标主机是否有常见的漏洞,如 ms08_067。

你可以使用 vuln 脚本对目标主机进行简单的漏洞勘查。

可以看到,就这么轻而易举的扫描出来靶机的两个漏洞,ms08_067 和 ms17_010 漏洞。对漏洞熟悉的同学知道,ms17_010 漏洞,它有一个名字好听的衍生版本,曰之 “永恒之蓝(externalblue)”。该攻击手段便是利用了这个漏洞。经检验,这两个靶机上的漏洞都可以被直接利用。

nmap 使用特定的脚本进行扫描

nmap --script=特定的脚本名字 IP地址

所谓 Nmap 脚本的名字,就是 /usr/share/nmap/scripts/ 文件夹下的那些文件,去掉后缀后的东西。
原文链接:https://blog.csdn.net/abc_12366/article/details/82807108

Nmap 網路診斷工具基本使用技巧與教學
掃描多台主機
如果要一次掃描多台主機,就直接把所有的主機名稱都放進 nmap 的參數中即可:

nmap www.hinet.net tw.yahoo.com www.google.com.tw

您也可以直接使用萬用字元,一次掃描整個子網域:

nmap 192.168.0.*

或是

nmap 192.168.0.0/24

如果您想要掃描 192.168.0.123、192.168.0.124、192.168.0.125 這三台主機,可以寫成這樣:

nmap 192.168.0.123,124,125

如果要掃描子網域中連續的某一段,可以這樣寫:

nmap 192.168.0.123-140

以檔案列表指定主機
nmap 也可以直接從檔案讀取要掃描的主機,假設我們有一個主機列表檔案 hostlist.txt,其內容為:

www.hinet.net
192.168.0.123
www.google.com.tw
然後我們就可以使用 nmap 直接讀取這個檔案內容來進行掃描:

nmap -iL hostlist.txt

排除指定的主機
如果要掃描整個網域,但是要排除某些機器,可以使用 --exclude 參數:

nmap 192.168.0.* --exclude 192.168.0.100

若以檔案方式指定主機,也可以使用 --excludefile 指定排除的列表:

nmap -iL hostlist.txt --excludefile excludelist.txt

偵測作業系統版本
如果要偵測主機的作業系統與各種服務的版本,可以加上 -A 參數:

nmap -A scanme.nmap.org

如果只需要作業系統資訊,可以使用 -O 參數:

nmap -O scanme.nmap.org

若只需要各種服務的版本:

nmap -sV scanme.nmap.org

測試主機是否有防火牆
Nmap 可以透過 TCP ACK 掃描,偵測主機是否有啟用防火牆:

nmap -sA scanme.nmap.org

掃描有防火牆的主機
掃描在防火牆保護下的主機:

nmap -PN scanme.nmap.org

偵測有開機的主機
掃描整個網路,偵測所有有開機的主機(ping scan):

nmap -sP 140.115.35.0/24

快速掃描
加快掃描的速度:

nmap -F www.hinet.net
nmap -T5 192.168.1.0/24

指定掃描的連接埠
掃描連接埠 80:

nmap -p 80 192.168.1.1

指定 TCP 連接埠 80:

nmap -p T:80 192.168.1.1

指定 UDP 連接埠 53:

nmap -p U:53 192.168.1.1

掃描兩個連接埠:

nmap -p 80,443 192.168.1.1

指定連接埠範圍:

nmap -p 80-200 192.168.1.1

結合各種參數:

nmap -p U:53,111,137,T:21-25,80,139,8080 192.168.1.1
nmap -p U:53,111,137,T:21-25,80,139,8080 server1.cyberciti.biz
nmap -v -sU -sT -p U:53,111,137,T:21-25,80,139,8080 192.168.1.254

掃描前 10 個常用的連接埠:

nmap --top-ports 10 192.168.1.1

查詢主機名稱
只查詢網域中所有的主機名稱,不做任何主機與連接埠的偵測:

nmap -sL 192.168.1.0/24

Referenced from:https://blog.gtwang.org/linux/nmap-command-examples-tutorials/

Nmap 扫描实例
nmap提供了10种模板供用户进行选择,下面分别介绍:

1、Intense scan 速度最快、最常见的TCP端口扫描,主要用于确定操作系统类型。

nmap –T4 –A –v 192.168.123.57

2、Intense scan plus UDP:除Intense scan 功能外,还扫描UDP端口。

nmap –sS –sU –T4 –A –v 192.168.123.57

3、Intense scan,all TCP ports:扫描所有的TCP端口

nmap –p 1-65535 –T4 –A –v 192.168.123.57

4、Ping scan:只进行ping扫描,不进行端口扫描

nmap –sP –PE 192.168.123.57

5、Quick scan:

nmap –T4 –F 192.168.123.57

6、Quick scan plus

nmap –sV –T4 –O –F 192.168.123.57

7、Quick traceroute

nmap –sP –PE –traceroute 192.168.123.57

8、Regular scan

nmap 192.168.123.57

9、Intense scan ,no ping

nmap –T4 –A –v –PN 192.168.123.57

Referenced from:https://www.cnblogs.com/aq-ry/p/9021801.html
攻防:Nmap原理及使用技巧
Nmap提供这几种功能:主机发现、端口扫描、操作系统识别、服务识别、脚本扫描。在图 1的入侵中就使用了其中三个功能:主机发现、端口扫描、操作系统识别。本文将主要介绍Nmap的主机发现、端口扫描的原理和使用技巧。

一、Nmap原理
1、主机发现技术
主机发现是为了发现网段内的活跃主机(活跃主机是指这台主机是运行的),这是黑客扫描的第一步。主机发现原理如表 1所示。

扫描方式发送报文活跃响应不活跃响应被过滤响应备注
ARP扫描ARP请求报文ARP响应报文超时不会被过滤精准
ICMP扫描ICMP type=8 code=0 type=13 code=0 type=17 code=0ICMP type=0 code=0 type=14 code=0 type=18 code=0超时超时可能会被防火墙过滤
端口扫描TCP synTCP syn+ack超时超时或ICMP type=3 code=3或13可能会被过滤

表 1 主机发现原理

ARP扫描是最精准的扫描方式,并且不会被过滤,因此会被强制有限使用。ICMP扫描是最常见的扫描方式,和ping命令原理一致,但现有很多防火墙和IPS设备会禁用ICMP扫描,使得ICMP主机发现失败。端口扫描技术是另一种发现主机的方式,准确且不容易被防火墙过滤,这种方式被经常使用,端口扫描原理见2.2节。

2、端口扫描技术
端口扫描可以分为TCP端口扫描和UDP端口扫描,由于TCP能够提供更多的协议字段,因此TCP端口扫描更准确,如今的端口扫描技术主要是指TCP端口扫描。

在TCP端口扫描中,探测报文组合不同flags位,不同的flags在端口开放、端口关闭、防火墙过滤时的响应是不一样的,通过区分不同的响应来检测端口的状态,具体如2所示。(tcp flags有syn、ack、rst、fin、psh、urg共6种置位)

扫描方式TCP Flag开放时响应关闭时响应过滤时响应特征
-sT,TCP全连接扫描报文1:syn 报文2:acksyn+ack,完成3次握手rst+ack一般不会被过滤精准、费时、可能会被日志记录
-sS,TCP半连接扫描synsyn+ackrst+ackICMP type=3 code=3、13或超时精准、快速、无日志记录,最常见且默认的扫描方式
-sA,TCP ack扫描ackrstrstICMP type=3 code=3、13或超时不准确、只能判断是否加防火墙filtered或unfiltered
-sW,TCP window扫描ackrst,window不为0rst,window为0ICMP type=3 code=3、13或超时不准确、有的情况下window都为0
-sMfin+ack未收到rstrstICMP type=3 code=3、13或超时不准确
-sN无置位无响应rstICMP type=3 code=3或13或超时无响应不准确,只能判断closed和open/filtered
-sFfin无响应rstICMP type=3 code=3或13或超时无响应不准确,只能判断closed和open/filtered
-sXfin+psh+urg无响应rstICMP type=3 code=3或13或超时无响应不准确,只能判断closed和open/filtered
-sU,UDP端口扫描不相关有UDP响应或无响应icmp type=3 code=3ICMP type=3 code=1,2,9,10,13不准确,通常只能判断closed和open/filtered

表 2 端口扫描原理
由表 2可以看到,最常用的扫描方式是-sS扫描,快速、精准,且无日志;-sT方式最精准,但有日志记录;-sA/-sW能够识别是否有防火墙,对于识别windows防火墙非常有效。

3、其他技术
Nmap还包括操作系统指纹识别技术、服务识别技术、防火墙绕过技术以及NSE(Nmap Scripting Engine)脚本引擎。

二、Nmap使用技巧
1、Nmap命令组成
Nmap命令主要由以下几个部分组成:

nmap 主机发现参数 端口扫描参数 其他参数 目标主机/网段

例如:nmap –PS80 –sS –sV –v 1.1.1.1/24,其中主机发现使用-PS参数;端口扫描使用-sS参数;另外使用-sV参数探测服务版本,-v参数开启可视模式;目标网段为1.1.1.1/24。

需要注意的是:

m 如果在同一网段,则无论主机发现参数如何指定,Nmap都使用ARP扫描完成主机发现。

m 如果未指定主机发现方式,则使用-sn参数进行主机发现。

m 如果未指定端口扫描方式,则使用-sS参数进行端口扫描。

m -Pn参数可忽略主机发现阶段。

2、防火墙对Nmap扫描的影响
了解防火墙相关技术会帮助更好的使用Nmap,防火墙的以下技术会影响Nmap的使用。

m ICMP探测阻断,现有的很多防火墙都会阻止ICMP探测。因此主机发现尽量使用-Pn、-PS参数试试。

m 首包非TCP syn丢弃,防火墙检测每个会话的首包是否是TCP syn报文,如果不是则丢弃,此种技术普遍存在于硬件防火墙中但不经常开启。防火墙的这种技术会造成除-sT、-sS之外的其他大多数端口扫描方式无效,因此请尽量使用-sT、-sS参数进行端口扫描。

m 限速功能,防火墙会限制单IP连接速率,如果Nmap频繁扫描可能会触发这种策略。通过—scan-delay方法绕过。

m TCP Proxy。有的TCP Proxy会先和Nmap完成所有TCP三次握手,然后才向真正的目标主机发起TCP连接,在这种情况下,Nmap可能完全失效,但该情况非常少见。

a) 3、主要参数
主机发现的主要参数如表 3所示。

参数描述是否常用
-sL仅列出指定参数有哪些IP,并反向查询域名信息。一般
-sn组合表 1中3种技术进行主机发现,默认的主机发现方式。常用
-Pn不进行主机发现,直接进入端口扫描阶段,能绕过ICMP过滤,老版本为-P0。常用
-PS/-PA/-PU/-PY分别用TCP SYN/TCP ACK/UDP/SCTP技术进行主机发现。常用
-PE/-PP/-PM分别用ICMP ECHO/ICMP TIMESTAMP/ICMP MASK技术进行主机发现。一般
-PO使用参数指定的协议(如TCP)对主机进行扫描。不常用

表 3 主机发现主要参数

端口扫描的主要参数如表4 所示。
参数描述是否常用
-sS/-sTTCP全连接/半连接扫描。常用
-sA/-sWTCP ACK/TCP WINDOW扫描,用于确定目标主机是否有开启防火墙。一般
-sMFIN、ACK同时置位的扫描一般
-sN/-sF/-sXNULL/FIN/XMAS扫描。一般
-sUUDP扫描。常用
-sI盲扫描。不常用
-sO协议扫描,用于确定目标主机支持那些协议。一般
-sY/-sZSCTP/COOKIE-ECHO扫描。不常用
-bFTP代理扫描。不常用

表 4端口扫描主要参数

其他主要参数如下:
参数描述是否常用
-OOS指纹识别扫描常用
-AOS指纹识别、服务版本扫描、脚本扫描、traceroute常用
-sA/-sW探测主机是否开启防火墙常用
-f使用分片报文扫描目标主机常用
-sC使用脚本对目标主机进行安全扫描一般
-sV探测主机提供的服务的版本一般

表 6 其他重要参数

b)4、Nmap常用语句
Nmap的一些常见扫描语句如下:

nmap –sn 192.168.0.1/24

扫描目标网段中有哪些主机是活跃的,可以快速收集到活跃主机。

nmap –Pn –p 80,443,8080 192.168.0.1/24

快速定位目标网段中的WEB服务器。

nmap –sS –sU –A –v –T4 host

对目标host进行完整扫描,包括知名端口的TCP、UDP扫描,操作系统探测,服务探测,常见漏洞探测。

nmap –sS –p 1-65535 –v –T4 host

对目标host所有端口进行TCP端口扫描。

nmap –sA/-sW host

识别目标host是否在防火墙之后。

from:http://www.h3c.com/cn/d_201508/888907_30008_0.htm

nmap arp快速扫描

nmap 常用参数

nmap -T4 -A -v -Pn IP

最常用的一种扫描

-T4        #设置时序,越高扫描越快
-A        #启用操作系统检测,版本检测,脚本扫描和跟踪路由
-v        #增加详细级别(使用-vv或更高级别以获得更好的效果)
-Pn        #无ping扫描

nmap 主机发现

nmap [Scan Type(s)] [Options] {target specification} #指令格式

Scan Types 指探测类型:

-PS 指 TCP SYN Ping,
-PA 指 TCP ACK Ping,
-PU 指 UDP Ping
-PE 

ICMP Ping,现在很多主机封锁这些报文,适用于管理员监视内部网络

Options 指探测选项:

-n 指不对活动的 IP 地址进行反向域名解析,用以提高扫描速度
-R 指对活动的 IP 进行反向域名解析

target specification 指探测的目标地址或 IP 地址范围
192.168.0.1-255

默认主机发现扫描

nmap 192.168.0.1-255

Nmap 会发送一个 ICMP echo 请求,一个 TCP SYN 包给 443 端口,一个 TCP ACK 包给 80 端口和一个 ICMP 时间戳请求

这就等价于使用命令 nmap -PE -PS443 -PA80 -PP 192.168.0.1-255

此命令返回一个 IP 地址段中活动的主机,及其 IP 地址,主机域名,开启的服务以及相应的端口,MAC 地址等信息

其它命令

nmap -sP 192.168.0.1-255    #ping扫描,只列出存活主机,速度最快
nmap -Pn 192.168.0.1-255    #无ping扫描,结果和默认主机发现一样

其它参数

-O    #启用操作系统检测
-sV    #探测服务版本信息
-v    #增加详细级别(使用-vv或更高级别以获得更好的效果)
--script=script_name    #使用nse脚本

nmap 端口扫描

nmap -p 1-65535 192.168.0.8        # -p选项,只扫描指定的端口

Nmap所识别的6个端口状态

  • open(开放的)

应用程序正在该端口接收TCP 连接或者UDP报文。

  • closed(关闭的)

关闭的端口对于Nmap也是可访问的(它接受Nmap的探测报文并作出响应), 但没有应用程序在其上监听。

  • filtered(被过滤的)

由于包过滤阻止探测报文到达端口, Nmap无法确定该端口是否开放。

过滤可能来自专业的防火墙设备,路由器规则 或者主机上的软件防火墙。

  • unfiltered(未被过滤的)

未被过滤状态意味着端口可访问,但Nmap不能确定它是开放还是关闭。

只有用于映射防火墙规则集的ACK扫描才会把端口分类到这种状态。

用其它类型的扫描如窗口扫描,SYN扫描,或者FIN扫描来扫描未被过滤的端口可以帮助确定 端口是否开放。

  • open|filtered(开放或者被过滤的)

当无法确定端口是开放还是被过滤的,Nmap就把该端口划分成 这种状态。

开放的端口不响应就是一个例子。没有响应也可能意味着报文过滤器丢弃 了探测报文或者它引发的任何响应。

因此Nmap无法确定该端口是开放的还是被过滤的。 UDP,IP协议, FIN,Null,和Xmas扫描可能把端口归入此类。

  • closed|filtered(关闭或者被过滤的)

该状态用于Nmap不能确定端口是关闭的还是被过滤的。 它只可能出现在IPID Idle扫描中。

端口扫描技术
只列举常见的,详细可参考官方文档

  -sT    #TCP连接扫描
  -sS    #SYN扫描
  -sU    #UDP扫描
  -sA    #ACK扫描
  -sF    #FIN扫描

TCP连接扫描

使用操作系统的网络连接系统调用 connect(),对目标主机发起 TCP 三路握手,待完成后 Nmap 立即中断此次连接。

Nmap 通过获取每个尝试连接的状态信息来判定侦测端口的状态

SYN扫描

Nmap 产生一个 SYN 数据报文,如果侦测端口开放并返回 SYN-ACK 响应报文

Nmap 据此发送 RST 报文给侦测端口结束当前连接,这样做的好处在于缩短了端口扫描时间

UDP扫描

UDP 本身是无连接的协议,Nmap 向目标主机的端口发送 UDP 探测报文

如果端口没有开放,被侦测主机将会发送一个 ICMP 端口不可到达的消息

Nmap 根据这个消息确定端口闭合(closed)或者被过滤 (unfiltered)

通常没有回复意味着端口是开放(open)状态 。

ACK扫描

这种扫描比较特殊,它不能确切知道端口的基本状态,而是主要用来探测防火墙是否存在以及其中设定的过滤规则

FIN扫描

和 SYN 扫描相比,这种方式更为隐蔽,因此能够穿过防火墙的过滤

关闭(closed)端口将会返回合适的 RST 报文,而开放端口将忽略这样的侦测报文

具备类似防火墙不敏感特性的还有 -sN NULL 扫描,-sX X-mas 扫描。

防火墙/IDS逃逸

nmap -f --mtu=16 192.168.0.8

报文分段,mtu必须是8的倍数

nmap -sI www.0day.com:80 192.168.0.8

源IP欺骗

nmap --source-port 53 192.168.0.8

    

源端口欺骗
防火墙对服务器的设置会根据端口选择是否信任数据流
管理员可能会认为这些端口不会有攻击发生,所以可以利用这些端口扫描

nmap --data-length 30 192.168.0.8

在原来报文基础上,附加随机数据,达到规避防火墙的效果

nmap --spoof-mac 0 192.168.0.8

指定一个随机的MAC地址
Referenced from:https://wiki.wgpsec.org/knowledge/tools/nmap.html

nmap 主机发现 任何网络探测任务的最初几个步骤之一就是把一组IP范围(有时该范围是巨大的)缩小为
一列活动的或者您感兴趣的主机。扫描每个IP的每个端口很慢,通常也没必要。
当然,什么样的主机令您感兴趣主要依赖于扫描的目的。网管也许只对运行特定服务的
主机感兴趣,而从事安全的人士则可能对一个马桶都感兴趣,只要它有IP地址:-)。一个系统管理员
也许仅仅使用Ping来定位内网上的主机,而一个外部入侵测试人员则可能绞尽脑汁用各种方法试图 突破防火墙的封锁。

由于主机发现的需求五花八门,Nmap提供了一箩筐的选项来定制您的需求。
主机发现有时候也叫做ping扫描,但它远远超越用世人皆知的ping工具
发送简单的ICMP回声请求报文。用户完全可以通过使用列表扫描(-sL)或者 通过关闭ping
(-P0)跳过ping的步骤,也可以使用多个端口把TCP SYN/ACK,UDP和ICMP
任意组合起来玩一玩。这些探测的目的是获得响应以显示某个IP地址是否是活动的(正在被某 主机或者网络设备使用)。
在许多网络上,在给定的时间,往往只有小部分的IP地址是活动的。 这种情况在基于RFC1918的私有地址空间如10.0.0.0/8尤其普遍。
那个网络有16,000,000个IP,但我见过一些使用它的公司连1000台机器都没有。 主机发现能够找到零星分布于IP地址海洋上的那些机器。

如果没有给出主机发现的选项,Nmap 就发送一个TCP ACK报文到80端口和一个ICMP回声请求到每台目标机器。
一个例外是ARP扫描用于局域网上的任何目标机器。对于非特权UNIX
shell用户,使用connect()系统调用会发送一个SYN报文而不是ACK 这些默认行为和使用-PA -PE选项的效果相同。
扫描局域网时,这种主机发现一般够用了,但是对于安全审核,建议进行 更加全面的探测。

-P选项(用于选择 ping的类型)可以被结合使用。 您可以通过使用不同的TCP端口/标志位和ICMP码发送许多探测报文 来增加穿透防守严密的防火墙的机会。另外要注意的是即使您指定了其它 -P选项,ARP发现(-PR)对于局域网上的
目标而言是默认行为,因为它总是更快更有效。

下列选项控制主机发现。

-sL (列表扫描) 列表扫描是主机发现的退化形式,它仅仅列出指定网络上的每台主机, 不发送任何报文到目标主机。默认情况下,Nmap仍然对主机进行反向域名解析以获取 它们的名字。简单的主机名能给出的有用信息常常令人惊讶。例如,
fw.chi.playboy.com是花花公子芝加哥办公室的
防火墙。Nmap最后还会报告IP地址的总数。列表扫描可以很好的确保您拥有正确的目标IP。
如果主机的域名出乎您的意料,那么就值得进一步检查以防错误地扫描其它组织的网络。

既然只是打印目标主机的列表,像其它一些高级功能如端口扫描,操作系统探测或者Ping扫描
的选项就没有了。如果您希望关闭ping扫描而仍然执行这样的高级功能,请继续阅读关于 -P0选项的介绍。

-sP (Ping扫描) 该选项告诉Nmap仅仅 进行ping扫描 (主机发现),然后打印出对扫描做出响应的那些主机。 没有进一步的测试 (如端口扫描或者操作系统探测)。 这比列表扫描更积极,常常用于 和列表扫描相同的目的。它可以得到些许目标网络的信息而不被特别注意到。
对于攻击者来说,了解多少主机正在运行比列表扫描提供的一列IP和主机名往往更有价值。

系统管理员往往也很喜欢这个选项。 它可以很方便地得出 网络上有多少机器正在运行或者监视服务器是否正常运行。常常有人称它为
地毯式ping,它比ping广播地址更可靠,因为许多主机对广播请求不响应。

-sP选项在默认情况下, 发送一个ICMP回声请求和一个TCP报文到80端口。如果非特权用户执行,就发送一个SYN报文 (用connect()系统调用)到目标机的80端口。 当特权用户扫描局域网上的目标机时,会发送ARP请求(-PR),
,除非使用了--send-ip选项。 -sP选项可以和除-P0)之外的任何发现探测类型-P* 选项结合使用以达到更大的灵活性。
一旦使用了任何探测类型和端口选项,默认的探测(ACK和回应请求)就被覆盖了。 当防守严密的防火墙位于运行Nmap的源主机和目标网络之间时,
推荐使用那些高级选项。否则,当防火墙捕获并丢弃探测包或者响应包时,一些主机就不能被探测到。

-P0 (无ping) 该选项完全跳过Nmap发现阶段。 通常Nmap在进行高强度的扫描时用它确定正在运行的机器。 默认情况下,Nmap只对正在运行的主机进行高强度的探测如 端口扫描,版本探测,或者操作系统探测。用-P0禁止
主机发现会使Nmap对每一个指定的目标IP地址 进行所要求的扫描。所以如果在命令行指定一个B类目标地址空间(/16), 所有 65,536
个IP地址都会被扫描。 -P0的第二个字符是数字0而不是字母O。 和列表扫描一样,跳过正常的主机发现,但不是打印一个目标列表,
而是继续执行所要求的功能,就好像每个IP都是活动的。

-PS [portlist] (TCP SYN Ping) 该选项发送一个设置了SYN标志位的空TCP报文。 默认目的端口为80 (可以通过改变nmap.h) 文件中的DEFAULT-TCP-PROBE-PORT值进行配置,但不同的端口也可以作为选项指定。
甚至可以指定一个以逗号分隔的端口列表(如 -PS22,23,25,80,113,1050,35000),
在这种情况下,每个端口会被并发地扫描。

SYN标志位告诉对方您正试图建立一个连接。 通常目标端口是关闭的,一个RST (复位) 包会发回来。
如果碰巧端口是开放的,目标会进行TCP三步握手的第二步,回应 一个SYN/ACK
TCP报文。然后运行Nmap的机器则会扼杀这个正在建立的连接, 发送一个RST而非ACK报文,否则,一个完全的连接将会建立。
RST报文是运行Nmap的机器而不是Nmap本身响应的,因为它对收到 的SYN/ACK感到很意外。

Nmap并不关心端口开放还是关闭。 无论RST还是SYN/ACK响应都告诉Nmap该主机正在运行。

在UNIX机器上,通常只有特权用户 root 能否发送和接收 原始的TCP报文。因此作为一个变通的方法,对于非特权用户,
Nmap会为每个目标主机进行系统调用connect(),它也会发送一个SYN
报文来尝试建立连接。如果connect()迅速返回成功或者一个ECONNREFUSED
失败,下面的TCP堆栈一定已经收到了一个SYN/ACK或者RST,该主机将被 标志位为在运行。
如果连接超时了,该主机就标志位为down掉了。这种方法也用于IPv6 连接,因为Nmap目前还不支持原始的IPv6报文。

-PA [portlist] (TCP ACK Ping) TCP ACK ping和刚才讨论的SYN ping相当类似。 也许您已经猜到了,区别就是设置TCP的ACK标志位而不是SYN标志位。 ACK报文表示确认一个建立连接的尝试,但该连接尚未完全建立。
所以远程主机应该总是回应一个RST报文, 因为它们并没有发出过连接请求到运行Nmap的机器,如果它们正在运行的话。

-PA选项使用和SYN探测相同的默认端口(80),也可以 用相同的格式指定目标端口列表。如果非特权用户尝试该功能, 或者指定的是IPv6目标,前面说过的connect()方法将被使用。 这个方法并不完美,因为它实际上发送的是SYN报文,而不是ACK报文。

提供SYN和ACK两种ping探测的原因是使通过防火墙的机会尽可能大。
许多管理员会配置他们的路由器或者其它简单的防火墙来封锁SYN报文,除非 连接目标是那些公开的服务器像公司网站或者邮件服务器。
这可以阻止其它进入组织的连接,同时也允许用户访问互联网。 这种无状态的方法几乎不占用防火墙/路由器的资源,因而被硬件和软件过滤器
广泛支持。Linux Netfilter/iptables 防火墙软件提供方便的 --syn选项来实现这种无状态的方法。
当这样的无状态防火墙规则存在时,发送到关闭目标端口的SYN ping探测 (-PS)
很可能被封锁。这种情况下,ACK探测格外有闪光点,因为它正好利用了 这样的规则。

另外一种常用的防火墙用有状态的规则来封锁非预期的报文。 这一特性已开始只存在于高端防火墙,但是这些年类它越来越普遍了。 Linux
Netfilter/iptables 通过 --state选项支持这一特性,它根据连接状态把报文
进行分类。SYN探测更有可能用于这样的系统,由于没头没脑的ACK报文 通常会被识别成伪造的而丢弃。解决这个两难的方法是通过即指定
-PS又指定-PA来即发送SYN又发送ACK。

-PU [portlist] (UDP Ping) 还有一个主机发现的选项是UDP ping,它发送一个空的(除非指定了--data-length
UDP报文到给定的端口。端口列表的格式和前面讨论过的-PS和-PA选项还是一样。
如果不指定端口,默认是31338。该默认值可以通过在编译时改变nmap.h文件中的
DEFAULT-UDP-PROBE-PORT值进行配置。默认使用这样一个奇怪的端口是因为对开放端口 进行这种扫描一般都不受欢迎。

如果目标机器的端口是关闭的,UDP探测应该马上得到一个ICMP端口无法到达的回应报文。 这对于Nmap意味着该机器正在运行。
许多其它类型的ICMP错误,像主机/网络无法到达或者TTL超时则表示down掉的或者不可到达的主机。
没有回应也被这样解释。如果到达一个开放的端口,大部分服务仅仅忽略这个 空报文而不做任何回应。这就是为什么默认探测端口是31338这样一个
极不可能被使用的端口。少数服务如chargen会响应一个空的UDP报文, 从而向Nmap表明该机器正在运行。

该扫描类型的主要优势是它可以穿越只过滤TCP的防火墙和过滤器。 例如。我曾经有过一个Linksys
BEFW11S4无线宽带路由器。默认情况下, 该设备对外的网卡过滤所有TCP端口,但UDP探测仍然会引发一个端口不可到达
的消息,从而暴露了它自己。

-PE; -PP; -PM (ICMP Ping Types) 除了前面讨论的这些不常见的TCP和UDP主机发现类型, Nmap也能发送世人皆知的ping 程序所发送的报文。Nmap发送一个ICMP type 8 (回声请求)报文到目标IP地址,
期待从运行的主机得到一个type 0 (回声响应)报文。 对于网络探索者而言,不幸的是,许多主机和
防火墙现在封锁这些报文,而不是按期望的那样响应, 参见RFC 1122。因此,仅仅ICMP扫描对于互联网上的目标通常是不够的。
但对于系统管理员监视一个内部网络,它们可能是实际有效的途径。 使用-PE选项打开该回声请求功能。

虽然回声请求是标准的ICMP ping查询, Nmap并不止于此。ICMP标准 (RFC 792)还规范了时间戳请求,信息请求
request,和地址掩码请求,它们的代码分别是13,15和17。 虽然这些查询的表面目的是获取信息如地址掩码和当前时间,
它们也可以很容易地用于主机发现。 很简单,回应的系统就是在运行的系统。Nmap目前没有实现信息请求报文, 因为它们还没有被广泛支持。RFC
1122 坚持 “主机不应该实现这些消息”。 时间戳和地址掩码查询可以分别用-PP和-PM选项发送。
时间戳响应(ICMP代码14)或者地址掩码响应(代码18)表示主机在运行。 当管理员特别封锁了回声请求报文而忘了其它ICMP查询可能用于
相同目的时,这两个查询可能很有价值。

-PR (ARP Ping) 最常见的Nmap使用场景之一是扫描一个以太局域网。 在大部分局域网上,特别是那些使用基于 RFC1918私有地址范围的网络,在一个给定的时间绝大部分 IP地址都是不使用的。 当Nmap试图发送一个原始IP报文如ICMP回声请求时,
操作系统必须确定对应于目标IP的硬件 地址(ARP),这样它才能把以太帧送往正确的地址。
这一般比较慢而且会有些问题,因为操作系统设计者认为一般不会在短时间内 对没有运行的机器作几百万次的ARP请求。

当进行ARP扫描时,Nmap用它优化的算法管理ARP请求。 当它收到响应时,
Nmap甚至不需要担心基于IP的ping报文,既然它已经知道该主机正在运行了。 这使得ARP扫描比基于IP的扫描更快更可靠。
所以默认情况下,如果Nmap发现目标主机就在它所在的局域网上,它会进行ARP扫描。 即使指定了不同的ping类型(如 -PI或者 -PS)
,Nmap也会对任何相同局域网上的目标机使用ARP。 如果您真的不想要ARP扫描,指定 --send-ip。

-n (不用域名解析) 告诉Nmap 永不对它发现的活动IP地址进行反向域名解析。 既然DNS一般比较慢,这可以让事情更快些。

-R (为所有目标解析域名) 告诉Nmap 永远 对目标IP地址作反向域名解析。 一般只有当发现机器正在运行时才进行这项操作。

--system-dns (使用系统域名解析器) 默认情况下,Nmap通过直接发送查询到您的主机上配置的域名服务器 来解析域名。为了提高性能,许多请求 (一般几十个 ) 并发执行。如果您希望使用系统自带的解析器,就指定该选项
(通过getnameinfo()调用一次解析一个IP)。除非Nmap的DNS代码有bug--如果是这样,请联系我们。
一般不使用该选项,因为它慢多了。系统解析器总是用于IPv6扫描。

Referenced from:https://nmap.org/man/zh/man-host-discovery.html

ARP Scan

-PR option is used for arp inspection so it just sends arp request. In the second block, we see the target host network dump. The -sn option disable port scan.

$ nmap -PR -sn u1

List Scan
The list scan is a passive scan so we do not send packets to the network we just listen. As you can see output there is one host which is up but the scan shows no one is up.

$ nmap -sL 192.168.122.0/24

No Ping Scan
No ping scan disables ping stage of the scan. Normally a scan starts with ping to find live hosts and then start heavy port scan to the live hosts. But if you set these options it starts with heavy port scan for all specified hosts.

$ nmap -Pn 192.168.122.0/24

Sync Scan
TCP Sync ping is another method for reliable scanning. To the given ports sync are send and got a response if there is a host like RST or ACK. Here we can for TCP 22.

$ nmap -sn -PS22 192.168.122.0/24

Ack Scan
TCP Ack ping is like sync ping but as you guess ack and sync flags are set.

$ nmap -sn -PA22 192.168.122.0/24

UDP Scan
UDP ping is like TCP ping. Here you can specify data-length for the packet which is randomly chosen payload.

$ nmap -sn --data-length 500 -PU514 192.168.122.0/24

ICMP Echo Scan
ICMP ping types are used for ping ICMP types. The most used and helpful is the echo . This type of scan pings all of the hosts

$ nmap -sn -PE 192.168.122.0/24

Protocol list is used to specify ip protocol numbers. As you know ICMP, TCP , UDP and similar protocol numbers specified in the IP packet header. Here we can set these numbers. For example, UDP is 17. This type of scan is not reliable so I skip it.

Do Not Resolve DNS
Resolving DNS can slow down scan or it may be unnecessary. So we can stop DNS resolving with -n option or force it with -R option. If we want to use the system specified DNS use --system-dns or want to specify manual DNS servers use --dns-servers 8.8.8.8

$ nmap -sn -n 192.168.122.0/24

广州二手房贷可绕过指导价?部分成交价开始低于指导价了|广州市_新浪财经_新浪网
update:2021-11-22
11月21日,网上突然传出“广州二手房贷可以不用参考指导价”的消息,并指出上述消息已获几家大银行确认。

  就此,第一财经向多家银行求证,某国有大行否认了该消息。

  不过,广州某银行业内人士对第一财经表示,该种情况确实存在,“绕过指导价的背后,是部分小区房产的成交价已经低于指导价了,指导价自然也就无效了。”该业内人士说。

  房价连续两月“双降”

  今年8月31日晚,广州市住房和城乡建设局发布《关于建立二手住房交易参考价格发布机制的通知》。通知指出,成交活跃的热点区域,以住宅小区为单位,由广州市住房政策研究中心在过去一年二手住房网签成交价格基础上,综合考虑评估价格、周边一手楼盘成交价格等因素,形成二手住房交易参考价格。

  同日,广州市住房政策研究中心发布《首批热点区域住宅小区二手住房交易参考价格表》,共涉及96个小区,覆盖天河、越秀、海珠、番禺、黄埔五个区域。

  由此,指导价开始深入影响了广州新房以及二手房成交市场。此前就有市场人士表示,后续广州二手房量价都有下跌的可能。

  9月15日,国家统计局公布8月份70个大中城市商品住宅销售价格变动情况。其中,北京、上海和深圳新房价格环比分别上涨0.2%、0.4%和1.0%,而广州则由7月的环比上涨0.2%转为环比下降0.1%,是四个一线城市中唯一下跌的城市,也是今年以来首次出现房价下跌。

  同时,广州二手房房价环比上涨0.5%,相比7月份的环比上涨0.6%,其上涨幅度开始下降。

  进入9月,无论是新房抑或二手房价格,均呈现环比下跌,其中,新房环比下跌0.1%,二手房环比下跌0.4%,这是广州新房和二手房价格首度双降;10月份,跌幅逐渐加大,新房环比下跌0.3%,二手房环比下跌0.6%,是北上广深四个一线城市中跌幅最大的。

  连跌之下,部分涉及指导价的小区,成交价已经低于指导价。

  比如,有中介群发消息,广州万博板块内的广地花园某套业主急售盘已经低于政府指导价。

  不仅仅是广州,在深圳,华润城也出现了一套跌破二手房指导价的笋盘。

  按揭申请不用排队了

  在市场上,人们对于房价走势的预期也在悄然发生变化,最直接的体现就是银行住房按揭业务量的下滑。

  去年的这个时候,各家银行的房贷额度基本已经用尽,不过,今年出现了例外。

  “我们听说,中行的额度基本用完,农行次之,其他银行还是够用的。”一位地产中介对第一财经透露。

  上述广州某银行业内人士也透露,由于市场成交太少,部分银行的住房按揭贷款申请都已经不用排队了。

  “之前积压的贷款申请基本已经消化完毕,尤其是股份行,现如今,各家银行都开始在市场上抢‘生意’了,甚至是恶意抢单,如果贷款额度消化不完,对于银行来说,将会影响明年的开门红。”该银行人士说。

  房贷利率也是银行贷款额度松紧的市场化体现。尽管5年期以上LPR稳如泰山,但自9月份开始,广州的房贷利率已经悄然下滑,其中,四大行首套利率普遍降到5.85%(LPR+120BP),二套则是6.05%(LPR+140BP)。而8月,四大行还普遍执行首套5.95%(LPR+130BP)、二套6.15%(LPR+150BP)的房贷利率。

  最新的全国热点城市房贷利率亦显示,广州目前中行、农行、广州银行、招行、光大银行等房贷利率已经较过往有所下降,其中,农行首套房贷款利率从6%降至5.4%,二套房也下调至5.6%。

  11月19日,银保监会新闻发言人在答记者问时表示,房地产合理贷款需求得到满足。10月末,银行业金融机构房地产贷款同比增长8.2%,整体保持稳定。个人住房贷款中90%以上用于支持首套房,投向住房租赁市场的贷款同比增长61.5%。
Referenced from:https://finance.sina.com.cn/china/2021-11-21/doc-iktzqtyu8689403.shtml

A股头条:央行货币报告表述明显变化!广州二手房贷松动?新华社发文解码元宇宙,A股上演独董“辞职潮”_股票频道_证券之星
update:2021-11-22
1、央行货币政策执行报告删除“管好货币总闸门”表述

央行发布2021年第三季度中国货币政策执行报告,报告删除了“坚决不搞‘大水漫灌’”、“管好货币总闸门”相关表述。国君固收研究点评称,前者指向货币,后者偏向信用。二者同时删除,无疑预留了后续相机抉择的空间。据华创宏观统计,2018年以来,通常在央行不提及“货币总闸门”和“不搞大水漫灌”之后的下一季度,社融的同比增速有明显的抬升。

2、广州二手房贷松动?中介:只要评估价能过审 可不按指导价

周日网上突然传出“广州二手房贷可以不用参考指导价”的消息,并指出上述消息已获几家大银行确认。证券时报向中介机构求证,得到的消息是:广州二手房贷,只要评估价能够通过银行评审,就可以按评估价贷款,而不是按指导价。该消息尚未获得官方证实。
Referenced from:http://stock.stockstar.com/IG2021112200000020.shtml

spdlog 下载编译

wget https://github.com/gabime/spdlog/archive/refs/tags/v1.9.2.tar.gz
tar xvf v1.9.2.tar.gz
cd spdlog-1.9.2/
cmake -DCMAKE_INSTALL_PREFIX=$(pwd)/../../install .

spdlog 简单使用

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"

int main() 
{
    spdlog::info("Welcome to spdlog!");
    spdlog::error("Some error message with arg: {}", 1);
    
    spdlog::warn("Easy padding in numbers like {:08d}", 12);
    spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
    spdlog::info("Support for floats {:03.2f}", 1.23456);
    spdlog::info("Positional args are {1} {0}..", "too", "supported");
    spdlog::info("{:<30}", "left aligned");
    
    spdlog::set_level(spdlog::level::debug); // Set global log level to debug
    spdlog::debug("This message should be displayed..");    
    
    // change log pattern
    spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
    
    // Compile time log levels
    // define SPDLOG_ACTIVE_LEVEL to desired level
    SPDLOG_TRACE("Some trace message with param {}", 42);
    SPDLOG_DEBUG("Some debug message");
    
    // Set the default logger to file logger
    auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
    spdlog::set_default_logger(file_logger);            
}

spdlog 常见问题

//打印行号
// 先设置日志输出格式
// %s:文件名,my_file.cpp
// %#:行号,123
// %!:函数名,my_func
spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");

// 使用宏才会有行号
SPDLOG_DEBUG("Some debug message");
spdlog::info("Welcome to spdlog!");

志输出格式具体见:https://github.com/gabime/spdlog/wiki/3.-Custom-formatting

spdlog 推荐用法

#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)

DEBUG("debug");
LOG("info");
WARN("warn");
ERROR("error");

spdlog 控制台输出

// 设置默认logger为控制台即可
// 设置默认logger,这里是控制台,所以spdlog::info的内容会输出到控制台
auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);

spdlog 控制台输出官方代码

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
    // create color multi threaded logger
    auto console = spdlog::stdout_color_mt("console");    
    //auto err_logger = spdlog::stderr_color_mt("stderr");    
    spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}

spdlog 同时输出控制台和写日志文件

// spdlog 同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);
// 每天2:30 am 新建一个日志文件
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// 遇到warn flush日志,防止丢失
logger->flush_on(spdlog::level::warn);

spdlog 文件按天分割

#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
    // Create a daily logger - a new file is created every day on 2:30am
    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}

spdlog 定时flush到文件
spdlog为了提高性能,降低对磁盘的写操作,通过flush机制来一次性把日志写入到文件里面持久化。所以如果没有恰当的配置,停止调试或者进程崩溃的时候会有日志丢失的问题。

//每三秒刷新一次
spdlog::flush_every(std::chrono::seconds(3));

遇到error级别,立即flush到文件:

enum level_enum
{
    trace = SPDLOG_LEVEL_TRACE, // 最低
    debug = SPDLOG_LEVEL_DEBUG,
    info = SPDLOG_LEVEL_INFO,
    warn = SPDLOG_LEVEL_WARN,
    err = SPDLOG_LEVEL_ERROR,
    critical = SPDLOG_LEVEL_CRITICAL, // 最高
    off = SPDLOG_LEVEL_OFF,
    n_levels
};

auto logger = spdlog::daily_logger_mt("daily_logger", "log/daily.txt", 2, 30);
// 遇到warn或者更高级别,比如err,critical 立即flush日志,防止丢失
logger->flush_on(spdlog::level::warn);

spdlog 使用完整代码

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream>
#include <memory>

// spd 带行号的打印,同时输出console和文件
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

int main(int argc, char *argv[]) {
    // 按文件大小
    //auto file_logger = spdlog::rotating_logger_mt("file_log", "log/log.log", 1024 * 1024 * 100, 3);
    // 每天2:30 am 新建一个日志文件
    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
    // 遇到warn flush日志,防止丢失
    logger->flush_on(spdlog::level::warn);
    //每三秒刷新一次
    spdlog::flush_every(std::chrono::seconds(3));
    
    // Set the default logger to file logger
    auto console = spdlog::stdout_color_mt("console");
    spdlog::set_default_logger(console);
    spdlog::set_level(spdlog::level::debug); // Set global log level to debug

    // change log pattern
    // %s:文件名
    // %#:行号
    // %!:函数名
    spdlog::set_pattern("%Y-%m-%d %H:%M:%S [%l] [%t] - <%s>|<%#>|<%!>,%v");

    LOG("test info");
    ERROR("test error");
    
    // Release and close all loggers
    spdlog::drop_all();
}

spdlog 线程安全
对于sinks,以 _mt 后缀结尾的是线程安全的,比如:daily_file_sink_mt
以_st 后缀结尾的是非线程安全的,比如:daily_file_sink_st

spdlog 接口
spdlog 总体而言提供了日志接口

spdlog::debug(), 默认的日志对象,使用默认的日志信息格式,输出至 stdout。
logger->debug(), 指定日志对象进行日志记录,输出至该日志对象对应的文件中。
SPDLOG_LOGGER_DEBUG(logger), SPDLOG_DEBUG(), 使用宏对以上两种接口进行包装,产生的日志格式包含 文件、函数、行。

spdlog set_level 设置日志级别

低于设置级别的日志将不会被输出。各level排序,数值越大级别越高:

// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");

第一行日志debug级别低于设定的级别info,在level为info时不会被输出。
第二行日志debug级别与设定的级别相同,所以可以显示出来。

typedef enum
{
    trace = 0,
    debug = 1,
    info = 2,
    warn = 3,
    err = 4,
    critical = 5,
    off = 6
} level_enum;

spdlog 同步和异步设置
Asynchronous logging

官方说明:https://github.com/gabime/spdlog/wiki/6.-Asynchronous-logging

默认情况下是不开启异步模式的,开启异步模式方式如下:

size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);

队列大小:

队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。由此可根据系统的log输出总量来确定队列大小。

spdlog drop -- 释放logger

在程序结束时,应该调用drop_all() 释放所有logger。

There is a bug in VS runtime that cause the application dead lock when it exits. If you use async logging, please make sure to call spdlog::drop_all() before main() exit. If some loggers are not in the registry, those should be released manually as well. stackoverflow: std::thread join hangs if called after main exits when using vs2012 rc

// Release and close all loggers
spdlog::drop_all();

或者单独drop某个logger

spd::drop("console");
spd::drop("basic_logger");

spdlog dump_backtrace

// 日志的所有信息能够被储存在一个环形缓冲里(包括 调试/追踪信息),之后可以根据需要将之打印出来。
// 需要时,调用dump_backtrace()

spdlog::enable_backtrace(32); // 保存最后的32条信息,早些时候的信息会被丢掉。
spdlog::dump_backtrace(); // 这时会打印出来,最后的32条信息。

spdlog::logger::dump_backtrace() will not work inside signal handlers.

spdlog_level environment variable

./example SPDLOG_LEVEL=info,mylogger=trace 

也可以使用

export SPDLOG_LEVEL=info
./example

#include "spdlog/cfg/env.h"
void load_levels_example()
{
    // Set the log level to "info" and mylogger to to "trace":
    // SPDLOG_LEVEL=info,mylogger=trace && ./example
    spdlog::cfg::load_env_levels();
    // or from command line:
    // ./example SPDLOG_LEVEL=info,mylogger=trace
    // #include "spdlog/cfg/argv.h" // for loading levels from argv
    // spdlog::cfg::load_argv_levels(args, argv);
}

websocket 维基百科介绍
WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。WebSocket协议在2011年由IETF标准化为RFC 6455「https://tools.ietf.org/html/rfc6455」,后由RFC 7936「https://tools.ietf.org/html/rfc7936」补充规范。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

WebSocket是一种与HTTP不同的协议。两者都位于OSI模型的应用层,并且都依赖于传输层的TCP协议。 虽然它们不同,但是RFC 6455中规定:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries(WebSocket通过HTTP端口80和443进行工作,并支持HTTP代理和中介),从而使其与HTTP协议兼容。 为了实现兼容性,WebSocket握手使用HTTP Upgrade头[1]从HTTP协议更改为WebSocket协议。

WebSocket协议支持Web浏览器(或其他客户端应用程序)与Web服务器之间的交互,具有较低的开销,便于实现客户端与服务器的实时数据传输。 服务器可以通过标准化的方式来实现,而无需客户端首先请求内容,并允许消息在保持连接打开的同时来回传递。通过这种方式,可以在客户端和服务器之间进行双向持续对话。 通信通过TCP端口80或443完成,这在防火墙阻止非Web网络连接的环境下是有益的。另外,Comet之类的技术以非标准化的方式实现了类似的双向通信。

大多数浏览器都支持该协议,包括Google Chrome、Firefox、Safari、Microsoft Edge、Internet Explorer和Opera。

与HTTP不同,WebSocket提供全双工通信。此外,WebSocket还可以在TCP之上实现消息流。TCP单独处理字节流,没有固有的消息概念。 在WebSocket之前,使用Comet可以实现全双工通信。但是Comet存在TCP握手和HTTP头的开销,因此对于小消息来说效率很低。WebSocket协议旨在解决这些问题。

WebSocket协议规范将ws(WebSocket)和wss(WebSocket Secure)定义为两个新的统一资源标识符(URI)方案,分别对应明文和加密连接。除了方案名称和片段ID(不支持#)之外,其余的URI组件都被定义为此URI的通用语法。

websocket 背景
早期,很多网站为了实现推送技术,所用的技术都是轮询。轮询是指由浏览器每隔一段时间(如每秒)向服务器发出HTTP请求,然后服务器返回最新的数据给客户端。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求与回复可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。

比较新的轮询技术是Comet。这种技术虽然可以实现双向通信,但仍然需要反复发出请求。而且在Comet中普遍采用的HTTP长连接也会消耗服务器资源。

在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

Websocket使用ws或wss的统一资源标志符(URI)。其中wss表示使用了TLS的Websocket。如:

ws://example.com/wsapi
wss://secure.example.com/wsapi
Websocket与HTTP和HTTPS使用相同的TCP端口,可以绕过大多数防火墙的限制。默认情况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。

websocket 优点

  • 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
  • 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  • 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
  • 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

websocket 请求应答示例
一个典型的Websocket握手请求如下:

客户端请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服务器回应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

websocket 字段说明

  • Connection必须设置Upgrade,表示客户端希望连接升级。
  • Upgrade字段必须设置Websocket,表示希望升级到Websocket协议。
  • Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行Base64编码,将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。如此操作,可以尽量避免普通HTTP请求被误认为Websocket协议。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均应当弃用。
  • Origin字段是必须的。如果缺少origin字段,WebSocket服务器需要回复HTTP 403 状态码(禁止访问)。
  • 其他一些定义在HTTP协议中的字段,如Cookie等,也可以在Websocket中使用。

websocket 数据协议分析
协议官方文档地址:https://datatracker.ietf.org/doc/html/rfc6455#section-5.2

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

FIN 位,也是整个片段的第一个字节的最高位,他只能是 0 或者是 1,这个位的作用只有一个,如果它为 1,表示这个片段是整个消息的最后一个片段,如果是 0,表示这个片段之后,还有其它的片段。RSV1,RSV2,RSV3
这三位是保留给扩展使用的,基本不会用到,反正我没用到,所以我们可以把它们当做空气就行,永远设置为 0,就是这么果断。

websocket opcode
opcode 顾名思义就是操作码,占用第一个字节的低四位,所以 opcode 可以代表 16 种不同的值。你是不是想问,opcode 是用来干嘛的?
opcode 是用 来解析当前片段的载荷(携带的数据)的,具体的后面会再次说明。

0x00,表示当前片段是连续片段,这是啥意思呢?还记得上面讨论 FIN 的时候,一条消息被分割成多条片段?如果当前片段不是第一个,那么 opcode 必须设置为 0。
0x01,表示当前片段所携带的数据是文本数据(记得最开始说的文本数据和二进制数据的区别??),如果有多个片段的话,只需要在第一个片段设置该值,属于同一条消息中后面的片段,只需要设置为 0 即可。
0x02,表示当前片段所携带的数据是二进制数据,如果有多个片段的话,只需要在第一个片段设置该值,属于同一条消息中后面的片段,只需要设置为 0 即可。
0x03-0x07,保留给将来使用,也就是说暂时还没用到。
0x08,表示关闭 websocket 连接,这个后面我会再一次讲到,先放着
0x09,发送 Ping 片段,说白了,它主要是用来检测远程端点是否还存活,我想检查我的对象是不是已经死了,但是这个片段可以携带数据,如果端点的一方发送了 Ping,那么接受方,必须返回 Pong 片段,用中国人的话来说,就是礼尚往来嘛。
0xA,发送 Pong,用以回复 Ping,是不是很简单?
0xB-F,保留给将来使用,也就是说暂时还没用到。
websocket MASK
表示当前片段所携带的数据是否经过加密,位置为第二个字节的最高位,总共 1 位,它的值不是你想设置就设置的啊,RFC6455 明确规定,所有从客户端发送给服务器的数据必须加密,所以 mask 的值必须是 1。还有,所有从服务器发往客户端的数据,一定不能加密,所以呢,mask 必须为 0,就是这么简单粗暴。

websocket Payload Length
这部分是用来定义负载数据的长度的,总共 7 位,所以最大值为 127,就这么简单?哼哼,不会的。
websocket 大于125字节 0x7e
payload_length<=125,此时数据的长度就是 payload_length 的大小。
payload_length=126,那么紧接着 payload_length 的 2 个字节,就用来表示数据的大小,所以当数据大小大于 125,小于 65535 的时候,payload_length 设置为 126,后面分析代码的时候,我会再次讲到。
payload_length=127,也就是 payload_length 取最大值,那么紧接着 payload_length 的 8 个字节,就用来表示数据的大小,此可以表示的数据可就相当大了,后面分析代码的时候,我会再次讲到。
websocket Mask key
它的位置紧接着数据长度的后面,大小为 0 或者是 4 个字节。前面分析了 mask 的作用,如果 mask 为 1 的话,数据需要加密,此时 mask key 占用 4 个字节,否则长度为 0,至于 mask key 如何用来解密数据的,后面会再次讲到。

websocket payload data
这里就是我们从客户端接收到的数据,不过它是经过加密的,“我是奥巴马”,之前 payload_length 的长度,就是经过加密之后的数据的长度,而不是原始数据的长度。
websocket 解析数据包
读取第二个字节的低 7 位,也就是之前讨论的 payload_length,0x7f 转换为二进制就是 01111111,当 payload_length 的长度小于 125 的话,数据长度就等于片段长度。当 payload_length 的长度等于 126 的时候,就有些麻烦了,此时第 3 和第 4 个字节组合为一个无符号 16 位整数,还记得我们之前说的,网络字节序吗?高位字节在前,低位字节在后面,所以当我们读的时候,第 3 个字节就是高 8 位,第 4 个字节就是低 8 位,所以我们首先将高 8 位左移 8 位再和低 8 位做或运算。当 payload_length 的长度等于 127 的时候,此时的第 3 到第 10 位组合为一个无符号 64 位整数,所以最高的 8 位需要左移 56 位,后面的依次类推,低 8 位保持不动。

websocket 解析 mask key
要找到 maskey,首先必须找到它在当前片段的偏移,如果 payload_length<=125,那么偏移就是 2,如果 payload_length==126,那么偏移就是 (2+2)=4,如果 payload_length>126,那么偏移就是(2+8)=10,同时 mask key 的大小为 4 个字节,所以找到了偏移和长度,mask key 就可以获取到了。

websocket 解密数据
解密数据的第一步就是要找到加密数据在当前片段中的偏移,很简单,这个值等于 maskkey 的偏移(上面已经求过了)+maskkey 本身的长度 4,那么怎么来解密数据呢?看上面的代码,就可以看出来,解密的过程其实就是遍历加密数据的每一个字符的 ASCII 值和数据(当前遍历的位置对 4 取模,得出的数据必定是 0,1,2,3,将得出的数据找到 maskkey 对应位置的 ASCII 值)进行异或运算求得,这个算法是 RFC6455 规定的,全世界都是这样。

websocket 掩码算法
掩码键(Masking-key)是由客户端挑选出来的32位的随机数。掩码操作不会影响数据载荷的长度。掩码、反掩码操作都采用如下算法:

首先,假设:

original-octet-i:为原始数据的第i字节。
transformed-octet-i:为转换后的数据的第i字节。
j:为i mod 4的结果。
masking-key-octet-j:为mask key第j字节。
算法描述为: original-octet-i 与 masking-key-octet-j 异或后,得到 transformed-octet-i。

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j

websocket Sec-WebSocket-Accept的计算

Sec-WebSocket-Accept根据客户端请求首部的Sec-WebSocket-Key计算出来。

计算公式为:

将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
通过SHA1计算出摘要,并转成base64字符串。
伪代码如下:

>toBase64( sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 )  )
验证下前面的返回结果:

const crypto = require('crypto');
const magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
const secWebSocketKey = 'w4v7O6xFTi36lq3RNcgctw==';

let secWebSocketAccept = crypto.createHash('sha1')
    .update(secWebSocketKey + magic)
    .digest('base64');

console.log(secWebSocketAccept);
// Oy4NRAQ13jhfONC7bP8dTKb4PTU=

websocket 接保持+心跳
WebSocket为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。

但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。

发送方->接收方:ping
接收方->发送方:pong
ping、pong的操作,对应的是WebSocket的两个控制帧,opcode分别是0x9、0xA。

举例,WebSocket服务端向客户端发送ping,只需要如下代码(采用ws模块)

ws.ping('', false, true);

websocket 数据掩码的作用
WebSocket协议中,数据掩码的作用是增强协议的安全性。但数据掩码并不是为了保护数据本身,因为算法本身是公开的,运算也不复杂。除了加密通道本身,似乎没有太多有效的保护通信安全的办法。

那么为什么还要引入掩码计算呢,除了增加计算机器的运算量外似乎并没有太多的收益(这也是不少同学疑惑的点)。

答案还是两个字:安全。但并不是为了防止数据泄密,而是为了防止早期版本的协议中存在的代理缓存污染攻击(proxy cache poisoning attacks)等问题。

from:https://www.cnblogs.com/chyingp/p/websocket-deep-in.html

websocket test websocket server

ws://echo.websocket.org/

js 使用websocket

在支持WebSocket的浏览器中,在创建socket之后。可以通过onopen,onmessage,onclose即onerror四个事件实现对socket进行响应
一个简单是示例:

var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()
{
  console.log(“open”);
  ws.send(“hello”);
};
ws.onmessage = function(evt)  {  console.log(evt.data); };
ws.onclose   = function(evt)  {  console.log(“WebSocketClosed!”); };
ws.onerror   = function(evt)  {  console.log(“WebSocketError!”); };

首先申请一个WebSocket对象,参数是需要连接的服务器端的地址,同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。

websocket 0x81 0x7e 0x7f

注意,从客户端发送到服务端的数据都被 异或加密(用一个32位的key)格式化。详情请参见规范的第5节。掩码明确告知我们消息是否经过格式化。从客户端来的消息必须经过格式化,所以你的服务器必须要求这个掩码是1(事实上,规范5.1节规定了如果客户端发送了没有格式化的消息,你的服务器应该断开连接)

当向客户端发送帧时,不要对其进行掩码,也不要设置掩码位。稍后我们将解释屏蔽。注意:即使使用安全套接字,也必须屏蔽消息。RSV1-3可以忽略,它们是用于扩展的。

操作码字段定义了如何解释有效负载数据:0x0表示延续,0x1表示文本(总是用UTF-8编码),0x2表示二进制,以及其他所谓的“控制代码”,稍后将对此进行讨论。在这个版本的WebSockets中,0x3到0x7和0xB到0xF没有任何意义。

FIN位告诉我们这是不是系列的最后一条消息。如果是0,那么服务器将继续侦听消息的更多部分;否则,服务器应该考虑传递的消息。不仅仅是这样。

解码有效载荷长度
要读取有效负载数据,您必须知道何时停止读取。这就是为什么有效载荷长度很重要。不幸的是,这有点复杂。要阅读它,请遵循以下步骤:

读取9-15(包括)位并将其解析为无符号整型。如果长度小于等于125,那么就是长度;你就完成了。如果是126,到第二步。如果是127,到步骤3。
读取下面的16位,并将其解释为无符号整型。你就完成了。
读取接下来的64位,并将其解释为无符号整型(最重要的位必须为0)。
from: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSockets_API/Writing_WebSocket_servers

websocket java代码实现

public static final String RESPONSE_HEADERS = "HTTP/1.1 101 Switching Protocols\r\n" +
            "Upgrade: websocket\r\n" +
            "Connection: Upgrade\r\n" +
            "WebSocket-Location: ws://127.0.0.1:9527\r\n";

 ServerSocket serverSocket = new ServerSocket(7000);

        while (true) {
            Socket socket = serverSocket.accept();
            // 开启一个新线程
            Thread thread = new Thread(() -> {
                // 响应握手信息
                try {
                    // 读取请求头
                    byte[] bytes = new byte[10000000];
                    socket.getInputStream().read(bytes);
                    String requestHeaders = new String(bytes, StandardCharsets.UTF_8);

                    // 获取请求头中的
                    String webSocketKey = "";
                    for (String header : requestHeaders.split("\r\n")) {
                        if (header.startsWith("Sec-WebSocket-Key")) {
                            webSocketKey = header.split(":")[1].trim();
                        }
                    }

                    // 将webSocketKey 与 magicKey 拼接用sha1加密之后在进行base64编码
                    String value = webSocketKey + magicKey;
                    String webSocketAccept = new String(Base64.encodeBase64(DigestUtils.sha1(value.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);

                    // 写入返回头 握手结束 成功建立连接
                    String responseHeaders = RESPONSE_HEADERS + "Sec-WebSocket-Accept: " + webSocketAccept + "\r\n\r\n";
                    socket.getOutputStream().write(responseHeaders.getBytes(StandardCharsets.UTF_8));
                    System.out.println("握手成功,成功建立连接");
                }
            }
        }

from: https://segmentfault.com/a/1190000039890327

websocket debugging-websockets-with-curl

$ curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: echo.websocket.org" -H "Origin:http://www.websocket.org" http://echo.websocket.org

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://www.websocket.org
WebSocket-Location: ws://echo.websocket.org/
Server: Kaazing Gateway
Date: Mon, 11 Jun 2012 16:34:46 GMT
Access-Control-Allow-Origin: http://www.websocket.org
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Headers: x-websocket-extensions
Access-Control-Allow-Headers: x-websocket-version
Access-Control-Allow-Headers: x-websocket-protocol

Referenced from:https://www.thenerdary.net/post/24889968081/debugging-websockets-with-curl

websocket Base Framing Protocol
https://datatracker.ietf.org/doc/html/rfc6455#section-5.2

websocket implementing-web-sockets-with-libcurl

#define concat(a,b) a b
  handle = curl_easy_init();
  // Add headers
  header_list_ptr = curl_slist_append(NULL , "HTTP/1.1 101 WebSocket Protocol Handshake");
  header_list_ptr = curl_slist_append(header_list_ptr , "Upgrade: WebSocket");
  header_list_ptr = curl_slist_append(header_list_ptr , "Connection: Upgrade");
  header_list_ptr = curl_slist_append(header_list_ptr , "Sec-WebSocket-Version: 13");
  header_list_ptr = curl_slist_append(header_list_ptr , "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==");
  curl_easy_setopt(handle, CURLOPT_URL, concat("http","://echo.websocket.org"));
  curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list_ptr);
  curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, my_opensocketfunc);
  curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, my_func);
  curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_writefunc);
  curl_easy_perform(handle);

Referenced from:https://phpandmore.net/2015/02/17/implementing-web-sockets-with-curl/

websocket 使用curl测试websocket服务

curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: example.com:80" \
     --header "Origin: http://example.com:80" \
     --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
     --header "Sec-WebSocket-Version: 13" \
     http://example.com:80/

Referenced from:https://blog.csdn.net/sd2131512/article/details/74996577

Test a WebSocket using curl.

curl \
    --include \
    --no-buffer \
    --header "Connection: Upgrade" \
    --header "Upgrade: websocket" \
    --header "Host: example.com:80" \
    --header "Origin: http://example.com:80" \
    --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
    --header "Sec-WebSocket-Version: 13" \
    http://example.com:80/

Referenced from:https://gist.github.com/htp/fbce19069187ec1cc486b594104f01d0