Wireshark分析艺术【读书总结】

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Wireshark分析艺术【读书总结】相关的知识,希望对你有一定的参考价值。

参考技术A

[TOC]

性能分析三板斧之一:

【统计->捕获文件属性】
Statistics -> Summary,查看文件属性信息,如平均速度、包大小、包数等等

判断流量高低峰、是否过载

[图片上传失败...(image-7200fa-1541746330755)]

性能分析三板斧之二:

【分析->专家信息】
Wireshark ->Analyze -> Expert Infos -> Notes,查看抓包的统计信息

查看是否有Notes、Warnings、errors之类的信息,看看是否有相关警告和错误,判断网络质量、重传乱序等

[图片上传失败...(image-1e2d8b-1541746330755)]

性能分析三板斧之三:

【统计->服务响应时间】
statistics -> Service Response Time -> xxxxx(如:ONC-RPC -> Program:NFS)

查看各项操作的服务响应时间,判断是否过载

Edit->Preferences->Protocols->TCP,勾选 Relative Sequence Numbers

启用之前就是相对值了。

Statistics -> TCP StreamGraph -> TCP Sequence Graph(Stevens)

查看数据传输情况,如传输的是否均匀、是否有TCP Zero Windows之类的

字段含义就是wireshark的一些提示信息,也就是wireshark抓包的一些info信息,这些提示信息都是Info这一栏中体现。

如果某个包被标记提示 [Packer size limited during caputre] ,说明这个包没有抓全,可以进一步查看下面的frame信息。一般这个情况是抓包的姿势不对。某些操作系统中,tcpdump默认只抓取每个帧的前96个字节,因此tcpdump抓包的时候,可以通过 -s参数指定要抓取的字节数

如果wireshark发现被Ack的那个包没有抓到,就会提示 [TCP ACKed unseen segment] ,不过这个提示大部分情况都可以忽略。因为大都情况下,刚开始抓包的时候,都是只抓到了后面的Ack而没有抓到前面的ACK

TCP数据传输中,除了三次握手和四次握手之外,同一台机器发出的数据段应该是连续的,即后一个包的Seq等于前一个包的Seq+Len,正确情况都应该是这样;如果发现后一个包的Seq大于前一个包的Seq+Len,那么就说明中间丢了一段数据,如果丢失的数据在整个网络包中都找不到,wireshark就会提示 [TCP Previous segment not captured] ,

出现这种情况的两个可能性:

TCP数据传输中,除了三次握手和四次握手之外,同一台机器发出的数据段应该是连续的,即后一个包的Seq等于前一个包的Seq+Len,正确情况都应该是这样;或者说后一个包的Seq应该会大于等于前一个包的Seq+Len,如果wireshark发现后一个包的Seq小于前一个包的Seq+Len,那么就认为是乱序了,就会提示 [TCP Out-of-Order] 。

一般而言,小跨度的乱序影响不大,如果是大跨度的乱序则会导致快速重传。举例如下,如果一个包的顺序是1、2、3、4、5被打乱成2、1、3、4、5则属于小跨度乱序,影响不大;如果被打乱成2、3、4、5、1,则会触发足够多的Dup ACK,从而导致1号包的重传。

当乱序或者丢包发生时,接收方就会收到一些Seq号比期望值大的包,TCP协议每收到一个这种包就会ACK一次期望的Seq值,通过这个方式告知发送方,因此就产生了一些重复的Ack。Wireshark抓到这些重复的Ack就会提示 [TCP Dup ACK] .

当发送方连续收到3个或者以上 [TCP Dup ACK] 时,就意识到之前发的包可能丢了,于是根据RFC的规定就会开始快速重传。 [TCP Dup ACK] 是接收方回应给发送方的,因此发送方就能够感知到并当连续收到3个以上的时候就开启快速重传。

快重传算法规定,发送方只要一连收到3个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计数器时间到期。

如果一个包真的丢了,又没有后续包可以在接收方触发[Dup Ack],那么就不会开启快速重传,这种情况发送方只能等到超时后再发送重传,超时重传的包就会被wireshark标记并提示 [TCP Retransmission]

TCP 超时与重传应该是 TCP 最复杂的部分之一,超时重传是 TCP 保证可靠传输的基础。当 TCP 在发送数据时,数据和 ack 都有可能会丢失,因此,TCP 通过在发送时设置一个定时器来解决这种问题。如果定时器溢出还没有收到确认,它就重传数据。关键之处就在于超时和重传的策略,需要考虑两方面:

在 Linux 较高的内核版本中,比如 3.15 中,已经有了至少 9 个定时器:超时重传定时器,持续定时器,ER延迟定时器,PTO定时器,ACK延迟定时器,SYNACK定时器,保活定时器,FIN_WAIT2定时器,TIME_WAIT定时器。

TCP包中“win=xxx”代表接收窗口的大小,表示这个包的发送方当前还有多少缓冲区可以接受数据。当wireshark发行一个包中的“win=0”时,就会标记提示 [TCP zerowindow] ,表示缓冲区已经满了,无法再接收数据了。

一般的,在缓冲区满之前,窗口大小应该是逐渐减小的过程。

如果一个包的发送方已经把对方所声明的接收窗口大小耗尽了,就会被wireshark标记为 [TCP window Full] 。比如某一端在握手时声明自己的接收窗口只有65535,也就意味着对端最多只能给他发送65535字节的数据而无需确认,即“在途字节数”最多只能是65535,当wireshark计算出对端已经有65535字节未被确认时,就会发生这个提示。

[TCP window Full]和上面的[TCP zerowindow]比较容易混淆,前者表示这个包的发送方暂时没有办法再发送数据了;后者表示这个包的发送方没有办法再接收数据了;两者都会意味着要暂停数据传输

只有在Edit->Preferences->Protocols->TCP菜单里启用了 Allow sub dissector to reassemble TCP streams 后,才有可能收到这个提示。这个表示可以把属于同一个应用层PDU的TCP包虚拟的集中起来

只有在Edit->Preferences->Protocols->TCP菜单里关闭了 Allow sub dissector to reassemble TCP streams 后,才有可能收到这个提示。

(Fragment reasembly time execeeded)表示这个包的发送方之前收到了一些分片,但是由于某些原因导致迟迟无法组装起来。

比如传输过程中有一些分片被丢包了,那么接收方就无法组装起来,然后就通过这个ICMP的方式告知发送方

ICMP是(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。

在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.

抓包显示的控制字段形态如下:

[SYN] : 建立连接、发起包
[FIN] : 关闭连接、结束包
[PSH] : DATA数据传输
[ACK] : ACK回应
[RST] : RESET、连接重置

另外两个常用字段:

[Len] :数据包长度
[Seq] :数据包序列号

ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,如果只是单个的一个SYN,它表 示的只是建立连接

当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接
当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接

对应http而言,一般就是request->reponse,一问一答。但对应TCP而言,并不一定每个包都会ACK。TCP的ACK是一种累积的ACK,也就是表示在我这个ACK之前的所有其他ACK都已经确认收到了。

比如,97号包的ACK=65701,96号包的Seq+Len=64273+1428=65701,那么就是表示97号的ACK是对96号的回应,也就是96号之前的其他没有被显示ACK的包,其实都已经通过97号包ACK了,这样发送方也就知道了在96号之前发出去的所有包对方都已经收到并ACK了。

Protocol = ARP
Source 和 Destination 都是MAC地址格式如 00:60:48:ff:12:31

抓包分析中,如果网络不通,发出去收不到ACK等等之类的,要再进一步看看每个包的MAC地址是否正确,反之有多个MAC地址导致的一些问题

一些实战经验告诉我们, Wireshark ->Analyze -> Expert Info -> Notes 统计中的重传率如果超过了0.1%,就需要采取一些措施了。但是现实网络环境下,要低于0.01%的重传是基本不可能的。

如果tcp对每个数据包都发送一个ack确认,那么只是一个单独的数据包为了发送一个ack代价比较高,所以tcp会延迟一段时间,如果这段时间内有数据发送到对端,则捎带发送ack,如果在延迟ack定时器触发时候,发现ack尚未发送,则立即单独发送;

延迟ACK好处:

(1) 避免糊涂窗口综合症;
(2) 发送数据的时候将ack捎带发送,不必单独发送ack;
(3) 如果延迟时间内有多个数据段到达,那么允许协议栈发送一个ack确认多个报文段;

试想如下典型操作,写-写-读,即通过多个写小片数据向对端发送单个逻辑的操作,两次写数据长度小于MSS,当第一次写数据到达对端后,对端延迟ack,不发送ack,而本端因为要发送的数据长度小于MSS,所以nagle算法起作用,数据并不会立即发送,而是等待对端发送的第一次数据确认ack;这样的情况下,需要等待对端超时发送ack,然后本段才能发送第二次写的数据,从而造成延迟;

使用TCP套接字选项TCP_NODELAY可以关闭套接字选项;

如下场景考虑关闭Nagle算法:

(1) 对端不向本端发送数据,并且对延时比较敏感的操作;这种操作没法捎带ack;
(2) 如上写-写-读操作;对于此种情况,优先使用其他方式,而不是关闭Nagle算法:

TCP和UDP的区别如TCP是可靠的、UDP是不可靠的,但是实际中的表现是何为可靠?何为不可靠?具体协议的ACK有何区别?

不管对于TCP还是UDP,都可能会被分片,这是由于以太网的MSS决定的;不同在于分片传输的处理:

语音通话的场景在于不能接受延迟,但是可以接受音质稍差。这样的话,UDP传输的时候,如果有些包丢失,应用层可以选择忽略并继续传输其他包,丢到一些包只会影响到音质,但是保证了流畅性。TCP而言,会重传每个包,只要丢包就重传,这样就会导致有一定的延迟,在语音中如果有延迟则并不可取。

因此,TCP和UDP,各自有各自的适合场景。 语音、视频中,UDP更合适,像声网、linphone等都是UDP去处理音视频。 基础、核心协议交互中必须采用TCP。

TCP在传输过程都需要往返时间来确认也就是ACK,而UDP则无需确认,那么UDP的效率一定比TCP高吗?这个是不一定的,虽然UDP可以一直往外不停的发包,不用等待ACK;但是TCP有发送窗口的存在,如果发送窗口小,并没有占满带宽,那么肯定受到往返时间的约束使得效率稍低,但是如果只要窗口足够大并且合适,跑满带宽,那么TCP也是可以不受往返时间的约束而源源不断的传输数据。

举例:马路上只有一辆车来回跑去拉货,回程过程相当于空跑(回程相当于TCP的ACK),这样TCP的效率当然低。但是如果在不拥塞的情况下,尽量提高车辆数量,是的马路上的车被刚好充满,这样总体的传输效率提高了,并且回程的ACK也不受影响。

分组交换,把大的数据分割成小包,这样可以实现链路共享,而不至于因为某一方阻塞所有。既然要分割成小包,那么必然要确定一个最大的包大小,这个就是MTU(Maximum Transmission Unit)最大传输单位,值为1500字节。如果除去20个字节的包头结构,那么一个IP包最大的包大小为1500-20=1480字节。如果要传输的数据块超过1480字节,那么网络层就会将其分片处理,封装为多个网络包传输。对于TCP而言,TCP协议层会主动把数据分成小段后再交给网络层,TCP的最大分段大小称之为MSS(Maximum Segment Size),这个MSS被设置为MTU减去IP头和TCP头之后的大小,这样刚好可以满足一个MTU。因为UDP没有MSS的概念,因此就只能交给网络层去处理分片了。

但是需要注意的是,目前有些网络是Jumbo Frame(巨帧)或者PPPOE这样的设备,那么他们的MTU则不是1500字节。目前发送方并没有一个好的机制来确定最佳分片大小,应该尽量使得网络中的设备的MTU保持一致。如果网络中的设备的MTU不一致,那么TCP协议如何适配MTU呢?我们知道TCP建连的时候必须要先进行三次握手,TCP在前两个握手包中会相互声明自己的MSS。如果client端声明自己的MSS=8960(巨帧),server端申明自己的MSS=1460,那么client在三次握手后就知道了server端的MSS,因此当client想要发送大于server端MSS的包的时候就会主动将自己的MSS降低为server端的MSS大小,从而适配接收方的MTU,可见,TCP协议层做了非常多的优化和处理

既然有分片,那么接收方必然要进行分片重组,通过抓包工具可以得知,分片的每个包都包含了 off=xxx,ID=xxx 这样的信息,接收方会把ID相同的分片按照off偏移量进行重组。那么接收方又如何得知那个包才是最后的分片呢?然后什么时候开始重组呢?这里就在于最后一个分片包有一个特殊的Flag,叫 More fragment = 0 ,抓包中的表现形式是 ..0. ... = More fragment: Not set ,这个就表示它是最后一个分片,然后可以开始重组包了。如果是其他分片包,形如 ..1. ... = More fragment: set 则表示接收方需要缓存,等待其他分片传输完成

如果client端的MTU=9000,server端的MTU=1500,那么当client请求server端的时候,client的包经过路由器时候,要么就被丢包,要么就被分片。如果这个巨帧包在网络层携带了DF(Don\'t fragment)标志则被丢弃(设置则表示不允许分片),如果没有设置则进行分片传输。需要注意的是,这种情况下如果丢包了重传还是会被丢弃,就成了黑洞了。

测试中,可以通过ping命令模拟这样的情况:

-f 参数表示设置DF标志
-l 参数表示请求字节

当请求字节设置为1472的时候,因为ICMP头部为8字节、IP头为20字节,因此1472+8+20=1500,刚好是一个MTU,因此可以ping成功。但是第二个1473+8+20=1501字节超过MTU了,又因为设置了DF标志表示不允许分片,因此传输失败,一般这样的情况下,路由器会回复 Packet needs to be fragmented but DF set 。

抓包的时候,如果发现一直重传,某个某些相对较大的包(查看Len值)才重传,那么可以通过 ping xxx.xxx.xxx.xxx -l [Len] -f 值进行测试验证,通过这个ping指定的[Len]的大小变化来寻得规律,可能就会发现网络上某个设备的MTU并非1500,这样导致了超过这个就重传的现象。

client 和 server端直接一定有交换机、路由器等设备,如果server端是万兆网卡,client端是千兆网卡,就有可能使得server端发送过快导致数据堵在交换机上,从而交换机在堵满之后发生丢包。 但是一般而言,server端的带宽本应该要大于client端才算合理,为了避免拥塞,因此需要有一种流控机制,允许交换机在过载时告知server端放慢速度或者暂停传输。

有一种“暂停帧”,就能够满足这样的需求:当交换机的缓冲区即将被填满时发送给server端一个暂停帧,当server端等待一会儿再发,这样就可以避免溢出丢包,也避免丢包后的重传。server端等待多久则由暂停帧中的pause_time指定,这样server端在等待pause_time后才会开始继续发送。当然交换机还可以给server端发送一个pause_time=0的暂停帧,告知server端,我已经消化完了,可以立即发送了。

注意,这的流控和TCP的流控是不一样的

《JavaScript DOM编程艺术(第二版)》读书总结

这本书是一本很基础的书,但对于刚入前端不久的我来说是一本不错的书,收获还是很大的,对一些基础的东西理解得更加透彻了。

1.DOM即document object model的缩写,文档对象模型,JavaScript做的就是对DOM的操作,或者说对节点(node)的操作。

2.js中DOM、this这是都是对象,有属性有方法。

3.document对象有body属性,所以可以直接写成document.body,无需再获取body元素(document.getElementsByTagName("body")[0]),createElement创建出来的节点也是一个对象。

4.取得当前页面的URL,window.location.href。

5.循环、判断不会形成作用域,只有函数才会形成,比如说下面这个代码:

    function fn(){
        if(1==1){
            alert(1)
            return false
            alert(2)
        }
        alert(3)
    }
    fn()

以前认为1和3都会弹出,其实不是,这里只会弹出1,虽然if语句中有大括号{ },但它不会有形成作用域的作用,只有函数才会形成,所以在函数fn里return false后面的代码都不会被执行。

6.书中四个比较有用的原生js函数。

①我们都知道如果在body元素前加入script标签引入js,那么就必须写上:

window.onload= function (){

}

意思是等文档结构加载完毕再加载这个函数里面的代码,不然就无法获取DOM元素。书中给了另外一种不写这个函数的方法,将要执行的函数当做addLoadEvent函数的参数即可 :

//onload事件共享函数
 function addLoadEvent(func){

     var oldonload=window.onload;
     if(typeof window.onload !="function"){
         window.onload=func;
     }else{
         window.onload =  function (){
             oldonload();
             func();
          }
     }
  }

②原生js没有提供insertAfter方法,只有insertBefore方法,封装insertAfter函数,参数是DOM对象:

 function insertAfter(newElement,targetElement){
     var parent = targetElement.parentNode;
     if(parent.lastChild == targetElement){
         parent.appendChild(newElement);
     }else{
         parent.insertBefore(newElement,targetElement.nextSibling);
     }
  }

③动画函数

//动画函数
 function moveElement(elementID,final_x,final_y,interval){
     if(!document.getElementById) return false;
     if(!document.getElementById(elementID)) return false;
     var elem=document.getElementById(elementID);
     if(elem.movement){
         clearTimeout(elem.movement);
     }
     if(!elem.style.top){
         elem.style.top = "0px"
     }
     if(!elem.style.left){
         elem.style.left = "0px"
     }
     var xpos = parseInt(elem.style.left);
     var ypos = parseInt(elem.style.top);
     var dist = 0;
     if(xpos == final_x && ypos == final_y){
         return true;
     }
     if(xpos < final_x){
         dist = Math.ceil((final_x - xpos)/10);
         xpos = xpos + dist;
     }
     if(xpos > final_x){
         dist = Math.ceil((final_x - xpos)/10);
         xpos = xpos - dist;
     }
     if(ypos < final_y){
         dist = Math.ceil((final_y - ypos)/10);
         ypos = ypos + dist;
     }
     if(ypos > final_y){
         dist = Math.ceil((final_y - ypos)/10);
         ypos = ypos - dist;
     }
     elem.style.left = xpos + "px";
     elem.style.top =  ypos + "px";
     var repeat = "moveElement(‘"+elementID+"‘,"+final_x+","+final_y+","+interval+")";
     elem.movement = setTimeout(repeat,interval);
 }

用法如: moveElement("div",300,300,20),第一个参数元素id,第二个第三个参数是元素最终的位置,第四个参数是时间间距。

④给一个元素添加类名的函数

//添加类名addClass函数
 function addClass(element,value){
     if(!element.className){
         element.className = value;
     }else{
         newClassName = element.className;
         newClassName+= " ";
         newClassName+= value;
         element.className = newClassName;
     }
  }

一同事看我读这本书,总是对我讲“看书没有用,你还是会忘记的”,我其实听他说这话好几次挺烦的,我没有反驳他,我觉得这种话题真的不值得我去反驳。

以上是关于Wireshark分析艺术【读书总结】的主要内容,如果未能解决你的问题,请参考以下文章

《艺术探索》读书笔记(更新中)

JavaScript DOM编程艺术(第二版)读书笔记 ——

读书笔记和sprint总结

《修改代码的艺术》读书笔记二

读书笔记关于写读书笔记的阶段性总结

Sprint总结和第八九十的读书笔记