精华内容
下载资源
问答
  • 关于CLOSE_WAITTIME_WAIT状态,服务器端都有可能出现,TIME_WAIT出现应该是短连接较多,需要通过修改内核参数解决,CLOSE_WAIT状态则是服务器程序可能有问题,服务器需要主动close,以及epoll多路复用模型中使用...

    关于CLOSE_WAIT和TIME_WAIT状态,服务器端都有可能出现,TIME_WAIT出现应该是短连接较多,需要通过修改内核参数解决,CLOSE_WAIT状态则是服务器程序可能有问题,服务器需要主动close,以及epoll多路复用模型中使用linger调整关闭等待时间

    分析解决这类问题,关键在于对照tcp3次握手4次挥手过程来查找,对着图看和想最易理解了

     

    http://blog.csdn.net/shootyou/article/details/6622226/

    昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下:

    http://blog.csdn.net/shootyou/article/details/6615051

    里头的分析过程有提到,通过查看服务器网络状态检测到服务器有大量的CLOSE_WAIT的状态。

     

    在服务器的日常维护过程中,会经常用到下面的命令:

    1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'    

    它会显示例如下面的信息:

    TIME_WAIT 814
    CLOSE_WAIT 1
    FIN_WAIT1 1
    ESTABLISHED 634
    SYN_RECV 2
    LAST_ACK 1

    常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

     

    具体每种状态什么意思,其实无需多说,看看下面这种图就明白了,注意这里提到的服务器应该是业务请求接受处理的一方:

     

    这么多状态不用都记住,只要了解到我上面提到的最常见的三种状态的意义就可以了。一般不到万不得已的情况也不会去查看网络状态,如果服务器出了异常,百分之八九十都是下面两种情况:

    1.服务器保持了大量TIME_WAIT状态

    2.服务器保持了大量CLOSE_WAIT状态

    因为Linux分配给一个用户的文件句柄是有限的(可以参考:http://blog.csdn.net/shootyou/article/details/6579139),而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。。。

    下面来讨论下这两种情况的处理方法,网上有很多资料把这两种情况的处理方法混为一谈,以为优化系统内核参数就可以解决问题,其实是不恰当的,优化系统内核参数解决TIME_WAIT可能很容易,但是应对CLOSE_WAIT的情况还是需要从程序本身出发。现在来分别说说这两种情况的处理方法:

     

    1.服务器保持了大量TIME_WAIT状态

    这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?

    从上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

    1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

    关于MSL引用下面一段话:

    1. MSL 為一個 TCP Segment (某一塊 TCP 網路封包) 從來源送到目的之間可續存的時間 (也就是一個網路封包在網路上傳輸時能存活的時間),由於 RFC 793 TCP 傳輸協定是在 1981 年定義的,當時的網路速度不像現在的網際網路那樣發達,你可以想像你從瀏覽器輸入網址等到第一個 byte 出現要等 4 分鐘嗎?在現在的網路環境下幾乎不可能有這種事情發生,因此我們大可將 TIME_WAIT 狀態的續存時間大幅調低,好讓 連線埠 (Ports) 能更快空出來給其他連線使用。  

    再引用网络资源的一段话:

    1. 值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可 想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。  
    2. HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个 request/response,一个主要原因就是发现了这个问题。  


    也就是说HTTP的交互跟上面画的那个图是不一样的,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。

     

    现在来说如何来解决这个问题。

     

    解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。

     

    下面来看一下我们网管对/etc/sysctl.conf文件的修改: 派生到我的代码片

    1. #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间   
    2. net.ipv4.tcp_syn_retries=2  
    3. #net.ipv4.tcp_synack_retries=2  
    4. #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒  
    5. net.ipv4.tcp_keepalive_time=1200  
    6. net.ipv4.tcp_orphan_retries=3  
    7. #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间  
    8. net.ipv4.tcp_fin_timeout=30    
    9. #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。  
    10. net.ipv4.tcp_max_syn_backlog = 4096  
    11. #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭  
    12. net.ipv4.tcp_syncookies = 1  
    13.   
    14. #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  
    15. net.ipv4.tcp_tw_reuse = 1  
    16. #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  
    17. net.ipv4.tcp_tw_recycle = 1  
    18.   
    19. ##减少超时前的探测次数   
    20. net.ipv4.tcp_keepalive_probes=5   
    21. ##优化网络设备接收队列   
    22. net.core.netdev_max_backlog=3000    

    修改完之后执行/sbin/sysctl -p让参数生效。

    这里头主要注意到的是 net.ipv4.tcp_tw_reuse

    net.ipv4.tcp_tw_recycle
    net.ipv4.tcp_fin_timeout
    net.ipv4.tcp_keepalive_*

    这几个参数。

    net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。

    net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。

    net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

    关于keepalive的用途可以参考:http://hi.baidu.com/tantea/blog/item/580b9d0218f981793812bb7b.html

     

    [2015.01.13更新]

    注意tcp_tw_recycle开启的风险:http://blog.csdn.net/wireless_tech/article/details/6405755

     

    2.服务器保持了大量CLOSE_WAIT状态

    休息一下,喘口气,一开始只是打算说说TIME_WAIT和CLOSE_WAIT的区别,没想到越挖越深,这也是写博客总结的好处,总可以有意外的收获。

     

    TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。

    但是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

     

    如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:http://blog.csdn.net/shootyou/article/details/6615051

    在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:

    服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。

     

    所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

     

    参考资料:

    1.windows下的TIME_WAIT的处理可以参加这位大侠的日志:http://blog.miniasp.com/post/2010/11/17/How-to-deal-with-TIME_WAIT-problem-under-Windows.aspx

    2.WebSphere的服务器优化有一定参考价值: http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tprf_tunelinux.html

    3.各种内核参数的含义: http://haka.sharera.com/blog/BlogTopic/32309.htm

    4. linux服务器历险之sysctl优化linux网络: http://blog.csdn.net/chinalinuxzend/article/details/1792184

    展开全文
  • 关于CLOSE_WAITTIME_WAIT状态,服务器端都有可能出现,TIME_WAIT出现应该是短连接较多,需要通过修改内核参数解决,CLOSE_WAIT状态则是服务器程序可能有问题,服务器需要主动close,以及epoll多路复用模型中使用...

    关于CLOSE_WAIT和TIME_WAIT状态,服务器端都有可能出现,TIME_WAIT出现应该是短连接较多,需要通过修改内核参数解决,CLOSE_WAIT状态则是服务器程序可能有问题,服务器需要主动close,以及epoll多路复用模型中使用linger调整关闭等待时间

    分析解决这类问题,关键在于对照tcp3次握手4次挥手过程来查找,对着图看和想最易理解了

    http://blog.csdn.net/shootyou/article/details/6622226/

    昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下:

    里头的分析过程有提到,通过查看服务器网络状态检测到服务器有大量的CLOSE_WAIT的状态。

    在服务器的日常维护过程中,会经常用到下面的命令:

    netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}‘

    它会显示例如下面的信息:

    TIME_WAIT 814

    CLOSE_WAIT 1

    FIN_WAIT1 1

    ESTABLISHED 634

    SYN_RECV 2

    LAST_ACK 1

    常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

    具体每种状态什么意思,其实无需多说,看看下面这种图就明白了,注意这里提到的服务器应该是业务请求接受处理的一方:

    0_13110767960diZ.gif

    这么多状态不用都记住,只要了解到我上面提到的最常见的三种状态的意义就可以了。一般不到万不得已的情况也不会去查看网络状态,如果服务器出了异常,百分之八九十都是下面两种情况:

    1.服务器保持了大量TIME_WAIT状态

    2.服务器保持了大量CLOSE_WAIT状态

    因为Linux分配给一个用户的文件句柄是有限的(可以参考:http://blog.csdn.net/shootyou/article/details/6579139),而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。。。

    下面来讨论下这两种情况的处理方法,网上有很多资料把这两种情况的处理方法混为一谈,以为优化系统内核参数就可以解决问题,其实是不恰当的,优化系统内核参数解决TIME_WAIT可能很容易,但是应对CLOSE_WAIT的情况还是需要从程序本身出发。现在来分别说说这两种情况的处理方法:

    1.服务器保持了大量TIME_WAIT状态

    这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?

    从上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

    1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

    2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

    关于MSL引用下面一段话:

    MSL 為一個 TCP Segment (某一塊 TCP 網路封包) 從來源送到目的之間可續存的時間 (也就是一個網路封包在網路上傳輸時能存活的時間),由於 RFC 793 TCP 傳輸協定是在 1981 年定義的,當時的網路速度不像現在的網際網路那樣發達,你可以想像你從瀏覽器輸入網址等到第一個 byte 出現要等 4 分鐘嗎?在現在的網路環境下幾乎不可能有這種事情發生,因此我們大可將 TIME_WAIT 狀態的續存時間大幅調低,好讓 連線埠 (Ports) 能更快空出來給其他連線使用。

    再引用网络资源的一段话:

    值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可 想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。

    HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个 request/response,一个主要原因就是发现了这个问题。

    也就是说HTTP的交互跟上面画的那个图是不一样的,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。

    现在来说如何来解决这个问题。

    解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。

    下面来看一下我们网管对/etc/sysctl.conf文件的修改:

    #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间

    net.ipv4.tcp_syn_retries=2

    #net.ipv4.tcp_synack_retries=2

    #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒

    net.ipv4.tcp_keepalive_time=1200

    net.ipv4.tcp_orphan_retries=3

    #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间

    net.ipv4.tcp_fin_timeout=30

    #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。

    net.ipv4.tcp_max_syn_backlog = 4096

    #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭

    net.ipv4.tcp_syncookies = 1

    #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭

    net.ipv4.tcp_tw_reuse = 1

    #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭

    net.ipv4.tcp_tw_recycle = 1

    ##减少超时前的探测次数

    net.ipv4.tcp_keepalive_probes=5

    ##优化网络设备接收队列

    net.core.netdev_max_backlog=3000

    修改完之后执行/sbin/sysctl -p让参数生效。

    这里头主要注意到的是net.ipv4.tcp_tw_reuse

    net.ipv4.tcp_tw_recycle

    net.ipv4.tcp_fin_timeout

    net.ipv4.tcp_keepalive_*

    这几个参数。

    net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。

    net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。

    net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

    [2015.01.13更新]

    注意tcp_tw_recycle开启的风险:http://blog.csdn.net/wireless_tech/article/details/6405755

    2.服务器保持了大量CLOSE_WAIT状态

    休息一下,喘口气,一开始只是打算说说TIME_WAIT和CLOSE_WAIT的区别,没想到越挖越深,这也是写博客总结的好处,总可以有意外的收获。

    TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。

    但是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

    在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:

    服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。

    所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

    参考资料:

    展开全文
  • 概述 五层模型 ...传输层:为进程提供通用...网络层:在计算机网络进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多的通信子网,网络层的任务就是选择合适的网间路由和交换节点,确保数据及时传...

    概述

    五层模型

    • 应用层:为特定的应用程序提供数据传输服务(HTTP,DNS)数据单位是报文
    • 传输层:为进程提供通用的数据传输服务,由于应用层的协议会很多,定义通用的传输层协议就支持不断增多的应用层协议
      TCP:报文段
      UDP:用户数据报
    • 网络层:在计算机网络进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多的通信子网,网络层的任务就是选择合适的网间路由和交换节点,确保数据及时传送
      网络层把传输层产生的报文段或者用户数据报封装成分组(IP数据报)进行传送
    • 数据链路层:数据链路层考虑的是两个计算机节点之间的点对点传输,会将ip数据报封装成帧。(两台主机之间的数据传输的时候,总是在一段一段的链路上进行传送的)
    • 物理层:实现相邻的计算机节点之间比特流的透明传输,尽可能屏蔽掉具体的传输介质和物理设备之间的差异。使其上层的数据链路层不必考虑网络的具体传输介质是什么。

    表示层: 数据压缩,加密以及数据描述,使应用程序不必关心在各台主机中数据内部格式不同的问题
    会话层:建立以及管理会话
    TCP/IP协议:相当于五层协议中数据链路层和网络层合并为网络接口层(应用层可能会直接使用IP层或者网络接口层)
    在向下的传播过程汇总不断添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部或者尾部

    物理层

    纯硬件设备,主要用来连接计算机等网络终端。

    数据链路层

    • 封装成帧
    • 透明传输
    • 差错校验(循环冗余CRC来检查比特差错)

    CSMA/CD协议

    CSMA/CD 表示载波监听多点接入 / 碰撞检测。
    

    多点接入 :说明这是总线型网络,许多主机以多点的方式连接到总线上。
    载波监听 :每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。
    碰撞检测 :在发送中,如果监听到信道已有其它主机正在发送数据,就表示发生了碰撞。虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。

    PPP协议

    互联网用户通常需要接到ISP之后才能接入网络,PPP协议就是用户计算机和ISP进行通信是所采用的的数据链路层协议

    MAC地址

    MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标识网络适配器(网卡)。

    一台主机拥有多少个网络适配器就有多少个 MAC 地址。例如笔记本电脑普遍存在无线网络适配器和有线网络适配器,因此就有两个 MAC 地址。

    局域网

    局域网是一种典型的广播信道,网络为一个单位所拥有,地理范围和站点数目有限
    (星型,环型,直线型)

    交换机

    • 交换机具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。
    • 目的 MAC 若不存在,交换机才广播到所有的端口,接收端口回应后交换机会“学习”新的地址,并把它添加入内部地址表中。

    网络层(IP层)

    IP地址分类

    IP地址是互联网协议地址,是IP协议提供的一种统一的地址格式,为互联网上的每一个网络和每一台主机分配一个逻辑地址,来屏蔽物理地址的差异。
    IP地址编制方案将IP地址空间划分为ABCDE五类,其中ABC是基本类,DE作为多播和保留使用,为特殊地址。
    每个IP地址包括,网络号和主机号,同一个物理网络上的所有主机都使用同一个网络号。
    在这里插入图片描述

    • A类地址:以0开头,第一个字节范围0~127
    • B类地址,以10开头,第一个字节范围128~191
    • C类地址,以110开头,第一个字节范围192~223
    • D类地址,以1110开头,第一个字节范围224~239
    • E类地址,以1111开头,保留地址

    IP地址与物理地址的区别

    • 物理地址MAC地址,是数据链路层和物理层使用的地址
    • IP地址是网络层和以上各层使用的地址,是一种逻辑地址

    有了IP地址为什么还需要mac地址?
    https://www.zhihu.com/question/21546408

    IP地址本质上是终点地址,它在跳过路由器(hop)的时候不会改变,而MAC地址则是下一跳的地址,每跳过一次路由器都会改变。

    这就是为什么还要用MAC地址的原因之一,它起到了记录下一跳的信息的作用。

    对于目的地在其他子网的数据包,路由器只需要让数据包到达那个子网即可,而剩下的工作就由子网内部解决了。
    因为如果我们只用 MAC 地址的话,我们会发现路由器需要记住每个 MAC 地址所在的子网是哪一个,因为如果我们只用 MAC 地址的话,我们会发现路由器需要记住每个 MAC 地址所在的子网是哪一个
    和MAC不同的是,IP地址是和地域相关的,对于位于同一个子网上的设备,我们给他们分配的IP的前缀是一样的,这个前缀就像邮政编码一样这样。路由器通过IP地址的前缀就能知道这个设备在那个子网上了,现在路由器只需要记住每个子网的位置即可,大大减少了路由器所需要的内存

    ARP协议(重点)

    网络层的 ARP 协议完成了 IP 地址与物理地址的映射。首先,每台主机都会在自己的 ARP 缓冲区中建立一个 ARP 列表,以表示 IP 地址和 MAC 地址的对应关系。当源主机需 要将一个数据包要发送到目的主机时,会首先检查自己 ARP 列表中是否存在该 IP 地址对 应的 MAC 地址:如果有,就直接将数据包发送到这个 MAC 地址;如果没有,就向本地 网段发起一个 ARP 请求的广播包,查询此目的主机对应的 MAC 地址。

    此 ARP 请求数据包里包括源主机的 IP 地址、硬件地址、以及目的主机的 IP 地址。 网络中所有的主机收到这个 ARP 请求后,会检查数据包中的目的 IP 是否和自己的 IP 地 址一致。如果不相同就忽略此数据包;如果相同,该主机首先将发送端的 MAC 地址和 IP 地址添加到自己的 ARP 列表中,如果 ARP 表中已经存在该 IP 的信息,则将其覆盖,然后给源主机发送一个 ARP 响应数据包,告诉对方自己是它需要查找的 MAC 地址;源主机收到这个 ARP 响应数据包后,将得到的目的主机的 IP 地址和 MAC 地址添加到自己的 ARP 列表中,并利用此信息开始数据的传输。如果源主机一直没有收到 ARP 响应数据包,表示 ARP 查询失败。

    ICMP协议(重点)

    用于在IP主机、路由器之间传递控制消息
    ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。
    ICMP 报文分为差错报告报文和询问报文。
    在这里插入图片描述

    Ping原理(重点)

    Ping是ICMP的一种重要用用,主要用来测试两台主机之间的连通性
    Ping的原理是通过向目的主机发送ICMP Echo请求报文,目的主机收到之后会发送Echo回答报文。Ping会根据时间和成功响应的次数估算出数据报往返时间和丢包率

    同一网段:

    1. 应用程序ping会判断发送的是主机名还是IP地址,调用函数gethostby name()解析主机名B,将主机名转换成一个32位的IP地址。这个过程叫做DNS
    2. ICMP协议打包这个数据包和机器B的IP地址转交给IP协议层。
    3. 由于主机A和主机B在同一个局域网内,必须把目标主机的IP地址转换为48位的MAC地址,即调用ARP协议,在局域网内发送ARP请求广播,查找主机B的硬件地址。
    4. 主机B的ARP协议层接收到主机A的ARP请求后,将本机的硬件地址填充到合适的位置后,发送ARP应答到主机A.
    5. 主机A发送ICMP数据包到主机B.
    6. 主机B接收到主机A的ICMP包,发送响应包。
    7. 主机A接收到主机B的ICMP响应包。

    ping命令详解

    Traceroute

    • Traceroute是ICMP的另一个应用,用来跟踪一个分组从源点到终点的路径
    • Traceroute发送的IP数据报封装的是无法交付的UDP用户数据报,并由目的主机发送终点不可达差错报告报文
    • ping 工具也可以进行侦测,但是,因为 IP 头的限制,ping 不能完全的记录下所经过的路由器。所以Traceroute 正好就填补了这个缺憾。

    原理:

    TTL:生存时间,它的存在视为了防止无法交付的数据报在网络中不断兜圈子,以路由器跳数为单位,当TTL为0是丢弃数据报

    当我们在使用ping命令时,返回结果里会带一个TTL值。这个东西的含义其实就是Time To Live,指的是报文在网络中能够‘存活’的限制。以前这个限制方式是设定一个时间(Time To Live中的Time就是这样来的),当报文在网络中转发时,时间超过这个限制,最后一个收到报文的‘路由点’就会把它扔掉,而不继续转发。后来把时间限制改为了跳数限制,就是当报文在网络中转发时,每经过一个‘路由点‘,就把预先设定的这个TTL数值减1,直到最后TTL=1时报文就被扔掉,不向下转发。

    • 它收到目的主机的 IP 后,首先给目的主机发送一个 TTL=1的 UDP数据包,而经过的第一个路由器收到这个数据包以后,就自动把 TTL 减1,而 TTL 变为 0 以后,路由器就把这个包给抛弃了,并同时产生一个主机不可达的 ICMP 数据报给主机。
    • 主机收到这个数据报以后再发一个 TTL=2 的 UDP 数据报给目的主机,然后刺激第二个路由器给主机发 ICMP 数据 报。如此往复直到到达目的主机。
    • traceroute 发送的是端口号 >30000的 UDP 包,所以到达目的主机的时候,目的主机只能发送一个端口不可达的 ICMP 数据报给主机。

    路由器

    路由器(Router),是一种计算机网络设备,提供了路由与转发两种重要机制,

    • 可以决定数据包从来源端到目的端所经过的路由路径(host 到 host 之间的传输路径),这个过程称为路由。
    • 将路由器输入端的数据包移送至适当的路由器输出端(在路由器内部进行),这称为转发。所以,路由器的一个作用是连通不同的网络,另一个作用是选择信息传送的线路。

    路由选择协议

    路由算法的分类

    1. 静态路由算法(非自适应路由算法)管理员手工配置路由信息 简单可靠在符合稳定 拓扑变化不大的网络中运行效果很好,广泛用于高度安全性的军事网络和较小的商业网络,路由更新慢不适用于大型网络
    2. 动态路由算法(自适应路由算法) 路由间彼此交换信息,按照路由算法优化出路由表项。路由更新快,适用于大型网络,及时响应链路费用或网络拓扑变化,算法复杂,增加网络负担

    动态路由算法又分为
    3. 全局性 链路状态路由 OSPF 所有路由器掌握完整的网络拓扑和链路费用信息
    4. 分散性 距离向量路由算法 RIP 路由器只掌握物理相连的邻居以及链路费用

    分层次的路由选择协议 (因特网规模很大,许多单位不想让外界知道自己的路由选择协议,但还想介入因特网)

    内部网关协议IGP : RIP OSPF
    外部网关协议EGP : BGP

    RIP协议

    RIP 是一种基于距离向量的路由选择协议,是因特网的洗衣标准,最大的优点是简单

    RIP 协议要求网络中的每一个路由器维护从它自己到其他每一个目的网络的唯一最佳距离记录

    距离: 通常为跳数,即从源端口到目的端口所经过的路由器个数,经过一个路由器条数+1,特别的,从1路由器到直接连接的网络的网络距离为1,RIP允许一条路有最多只能包含15个路由器,因此距离为16个表示网络不可达。

    RIP只适用于小互联网。

    信息交换 :
    ·

    1. 仅仅和相邻的路由器交换信息
    2. 路由器交换的信息是自己的路由表
    3. 每30S交换一次路由信息,然后路由器根据新信息更新路由表。若超过180S没收到邻居路由器的通告,则判定邻居没了,并更新自己的路由表

    路由器刚开始工作的时候,只知道直接连接的网络的距离(距离为1),接着每一个路由器也只和数目非常有限的相邻路由器交换并更新路由信息

    经过若干次更新之后,所有路由器最终都会知道到达本自制系统任何一个网络的最短距离和下一跳的路由器的地址,即收敛。

    距离向量算法:

    1. 修改相邻路由器发来的RIP报文中所有表项 : 对地址为X的向量路由器发来的RIP报文,修改此报文中的所有项目: 把下一跳的字段中的地址改为x,并把所有的距离字段 +1
    2. 对修改后的RIP报文中的每一个项目,进行以下步骤
      • R1路由表中没有Net3, 则把该项目填入R1路由表
      • R1路由表中NET3,则查看下一跳路由器地址:
        若下一跳是X,则用收到的项目替换源路由表中的项目
        若下一跳不是X,原来距离比从X走的距离远则更新,否则不做更新
    3. 若180S还没收到相邻路由器X的更新路由表,则把X记为不可达的路由器,即把距离设置为16

    RIP 报文 : RIP是应用层协议,使用UDP传送数据

    IP首部 + UDP首部 + RIP报文

    一个RIP报文最多可包括25个路由,如超过25个路由,

    当网络出现故障的时候,要经过比较长的时间才能将此信息传送到所有的路由器,慢收敛。

    它选择路由的度量标准(metric)是跳数,最大跳数是 15 跳。如果大于 15 跳,它就会丢弃数据包。
    在这里插入图片描述
    RIP:1.A 收到附近节点 C 的路由表信息,然后将附近节点 C。作为下一跳,那么 A 的距离就得在 C 上加 1

    OSPF协议(重点)

    开放最短路径有限OSPF协议

    开放表明OSPF不是受某一家厂商控制而是公开发表的
    最短路径有限 是因为使用dijkstra提出的最短路径算法SPF

    OSPF 最主要的特征就是使用分布式的链路状态协议

    和谁交换 ?

    使用洪泛法向自制系统内所有路由器发送信息,即路由器通过输出端口向所有相邻的路由器发送信息,而每一个相邻的路由器又再次将此信息发往其所有的相邻的路由器

    交换什么 ?
    发送的信息就是与本路由器响铃的所有路由器的链路状态,(本路由器和那些路由器响铃,以及该链路的度量、代价 --费用、距离 时延 带宽)

    多久交换 ?
    只有当链路状态发生变化的时候,路由器才向所有路由器洪范发送此信息

    最后,所有路由器都能建立一个链路状态数据库,即全网拓扑图

    链路状态路由算法 :

    1. 每个路由器发现他的邻居节点【HELLO问候分组】,并了解邻居节点的网络地址
    2. 设置到它的每个邻居的成本度量metric
    3. 构造DD数据库描述分组,向邻站给出自己的链路状态数据库中的所有链路状态项目的摘要信息
    4. 如果DD分组中的摘要自己都有,则邻站不做处理,如果有没有的或者是更新的,则发送【LSR链路状态请求分组】,请求自己没有的和比自己更新的信息
    5. 收到邻站的LSR分组之后,发送LSU链路状态更新分组进行更新
    6. 更新完毕后,邻站返回一个LSACK链路状态确认分组进行确认
    7. 使用DIJKSTRA根据自己的链路状态数据库构造到其他节点间的最短路径

    链路状态路由,底层是迪杰斯特拉算法,是链路状态路由选择协议,它选择路由的度量标准是带带宽、延迟。

    在这里插入图片描述

    传输层

    UDP首部格式

    在这里插入图片描述
    首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。

    TCP首部格式

    在这里插入图片描述

    • 序号:用于对字节流进行编号,例如序号为301,表示第一个字节编号为301,如果携带的数据长度为100字节,那么下一个报文段的序号应该就是401
    • 确认号:期望收到的下一个报文段的序号。例如B正确收到A发送来的一个报文段,序号为501,携带的数据长度为200字节,因此B期望下一个报文段的序号为701,B发送给A的确认报文段中的确认号就是701
    • 数据偏移:指的是数据部分巨鹿报文段开始位置的偏移量,实际上,指的是首部的长度
    • 确认ACK:当ACK为1的是确认号字段有效,否则无效,TCP规定,在连接建立之后所有的传送报文段都必须把ACK置位1,表明其是有效报文
    • 同步SYN:在连接建立时使用同步序号。当SYN = 1,ACK = 0时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文段中ACK = 1,SYN = 1.
    • 终止FIN: 用来释放一个连接,当FIN = 1是表示此报文段的发送方的数据已发送完毕,并要求释放连接
    • 窗口: 窗口值作为接收方设置其发送窗口的一句。之所以有这个限制,是因为接收方的数据缓层空间是有限的。

    TCP三次握手

    在这里插入图片描述

    SYN FLOOD攻击

    SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。
    配合IP欺骗,SYN攻击能达到很好的效果,通常,客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

    • 第一种是缩短SYN Timeout时间,由于SYN Flood攻击的效果取决于服务器上保持的SYN半连接数,这个值=SYN攻击的频度 xSYN Timeout,所以通过缩短从接收到SYN报文到确定这个报文无效并丢弃改连接的时间,例如设置为20秒以下(过低的SYNTimeout设置可能会影响客户的正常访问),可以成倍的降低服务器的负荷。
    • 第二种方法是设置SYNCookie,就是给每一个请求连接的IP地址分配一个Cookie,如果短时间内连续受到某个IP的重复SYN报文,就认定是受到了攻击,以后从这个IP地址来的包会被一概丢弃。

    可是上述的两种方法只能对付比较原始的SYN Flood攻击,缩短SYN Timeout时间仅在对方攻击频度不高的情况下生效,SYN Cookie更依赖于对方使用真实的IP地址,如果攻击者以数万/秒的速度发送SYN报文,同时利用SOCK_RAW随机改写IP报文中的源地址,以上的方法将毫无用武之地。

    net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 8192
    net.ipv4.tcp_synack_retries = 2
    分别为启用SYN Cookie、设置SYN最大队列长度以及设置SYN+ACK最大重试次数。
    SYN Cookie的作用是缓解服务器资源压力。启用之前,服务器在接到SYN数据包后,立即分配存储空间,并随机化一个数字作为SYN号发送SYN+ACK数据包。然后保存连接的状态信息等待客户端确认。启用SYN Cookie之后,服务器不再分配存储空间,而且通过基于时间种子的随机数算法设置一个SYN号,替代完全随机的SYN号。发送完SYN+ACK确认报文之后,清空资源不保存任何状态信息。直到服务器接到客户端的最终ACK包,通过Cookie检验算法鉴定是否与发出去的SYN+ACK报文序列号匹配,匹配则通过完成握手,失败则丢弃。
    net.ipv4.tcp_synack_retries是降低服务器SYN+ACK报文重试次数,尽快释放等待资源。
    tcp_max_syn_backlog则是使用服务器的内存资源,换取更大的等待队列长度,让攻击数据包不至于占满所有连接而导致正常用户无法完成握手。

    TCP四次挥手

    在这里插入图片描述

    大量TIME-WAIT应该怎么处理(重点)

    netstat -an |grep "TIME_WAIT" 查看处于Time_wait状态的连接详细情况
    netstat -ae|grep "TIME_WAIT" |wc -l 查看处于Time_wait状态的连接个数
    

    在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。

    正常的TCP客户端连接在关闭后,会进入一个TIME_WAIT的状态,持续的时间一般在1~4分钟,短时间内(例如1s内)进行大量的短连接,则可能出现这样一种情况:客户端所在的操作系统的socket端口和句柄被用尽,系统无法再发起新的连接!

    解决方法:

    • net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    • net.ipv4.tcp_tw_reuse = 1 表示开启重用允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    • net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    • net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间

    简单来说,就是打开系统的TIMEWAIT重用和快速回收。

    大量CLOSE-WAIT应该怎么处理(重点)

    CLOSE_WAIT状态的原因与解决方法

    • 在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。

    • 通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态。但是在一些特殊情况下,就会出现连接长时间处于CLOSE_WAIT状态的情况。

    • 出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下error,如果不是AGAIN,就断开连接。

    基本的思想就是要检测出对方已经关闭的socket,然后关闭它。

    1.代码需要判断socket,一旦read返回0,断开连接,read返回负,检查一下errno,如果不是AGAIN,也断开连接。(注:在UNP 7.5节的图7.6中,可以看到使用select能够检测出对方发送了FIN,再根据这条规则就可以处理CLOSE_WAIT的连接)
    2.给每一个socket设置一个时间戳last_update,每接收或者是发送成功数据,就用当前时间更新这个时间戳。定期检查所有的时间戳,如果时间戳与当前时间差值超过一定的阈值,就关闭这个socket。
    3.使用一个Heart-Beat线程,定期向socket发送指定格式的心跳数据包,如果接收到对方的RST报文,说明对方已经关闭了socket,那么我们也关闭这个socket。
    4.设置SO_KEEPALIVE选项,并修改内核参数。

    应用层(HTTP)

    HTTP请求格式

    在这里插入图片描述

    • 请求行 : 请求方法 + URL + HTTP版本
    • 请求头部: 紧接着请求行(第一行)之后的部分,用来说明服务器要使用的附加信息从第二行器为请求头部
      HOST 将指出请求的目的地
    • 空行: 请求头部后面的空行是必须的
    • 请求数据:也叫主题,可以添加任意的其他数据

    HTTP响应格式

    在这里插入图片描述

    • 状态行:HTTP版本号 + 状态码 + 状态消息
    • 消息报头: 用来说明客户端要使用的一些附加信息
    • 空行:消息报头部后面的空行是必须的
    • 响应正文:服务器返回给客户端的文本信息

    响应状态码

    • 1xx : 100 continue 是协议处理中的一种中间状态,实际用到的比较少
    • 2xx :服务器成功处理了客户端请求,也是我们最愿意看到的状态
      200 ok: 是最常见的成功状态码,表示一切正常,如果是非HEAD请求,服务器返回的响应头都会有body数据
      204 No Content:也是常见的成功状态码,与200OK基本相同,但是响应头部没有body
      204 Partial Content : 应用于HTTP分块下载或者断点续传,表示响应返回的body数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态
    • 3xx:3xx类状态码表示客户端请求的资源发生了变动,需要客户端用新的URL重新发送请求获取资源,也就是重定向
      301 Moved Permanently:表示永久重定向,说明请求的资源已经不存在了,需要改用心的URL再次访问
      302 Found : 表示临时重定向,说明请求的资源还在,但是暂时需要用另一个URL进行访问
      304 Not Modified :304表示上次请求结束到现在,目标网页内容未改变,客户端可以直接显示上次的内容。通过客户端和服务端之间的一个Last-Modified来判断。
      在这里插入图片描述- 4xx : 4xx类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义
      400 BadRequest表示客户端请求的报文有错误,但只是个笼统的错误
      403 Forbidden :表示服务器禁止访问资源,并不是客户端的请求出错
      404 Not Found :表示请求的资源在服务器上不存在或者未找到,所以无法提供给客户端
    • 5xx:表示客户端请求报文正确,但是服务器处理的时候发生了错误,属于服务器端的错误码
      500 Internel Server Error : 与400相似,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道
      501 Not Implemented : 表示客户端请求的功能还不支持
      502 Bad Gateway : 通常是服务器作为网关或者代理的时候范围的错误码,表示服务器自身工作正常,访问后端服务器发生了错误
      503 Service Unavailable 表示服务器很忙,暂时无法响应客户端,类似于“网络服务正忙,请稍后重试”

    HTTP请求响应字段

    Host字段

    客户端发送请求是,用力指定服务器域名
    有了Host字段,就可以将请求发往[同一台]服务器上的不同的网站。
    在这里插入图片描述

    Content-Length 字段

    服务器在返回数据的时候,会有Content-Length,表明本次回应的数据长度
    在这里插入图片描述

    Connection 字段

    connection字段最常用语客户端要求服务器使用TCP常连接,以便其他请求复用。
    在这里插入图片描述
    HTTP/1.1版本的默认连接都是持久连接,但是为了兼容老版本的HTTP,需要制定Connection首部字段的值为Keep -Alive

    Connection : keep - alive
    
    Content - Type

    Content - Type字段用于服务器回应的时候,告诉客户端,本次数据是什么格式。
    在这里插入图片描述

    Content - Encoding

    Content - Encoding 字段说明数据的编码方法,表示服务器返回的数据使用了什么压缩格式
    在这里插入图片描述

    客户端在请求时,用 Accept-Encoding 字段说明自己可以接受哪些压缩方法。

    Accept-Encoding: gzip, deflate
    

    HTTP如何实现状态化,cookie被禁用了怎么办(URL重写)

    Cookie被禁用,如何传递session id?

    1. 如果用户禁止cookie,服务器仍然会将sessionId以cookie的方式发送给浏览器,但是浏览器不再保存这个cookie了(cookie)了.
    2. 如果想继续使用session,需要采取其他方式来实现sessionId的跟踪,可以使用url重写来实现sessionId的跟踪
    3. 什么是URL重写?
      浏览器在访问服务上的某个地址的时候,不能够直接写这个组件的地址,而应该使用服务器生成的这个地址。
    <a href="some">someServlet</a> error
    
       		<a href-"<%=response.encodeURL("some")%>"></a> 
    
                encodeURL方法会在"some"后面添加sessionId。
    

    4.怎么能实习URL重写?

      //encodeURL方法用在链接地址、表单提交地址。
      response.encodeURL(String url);
                    
      //encodeRedirectURL方法用于重定向地址。
     response.encodeRedirectURL(String url);
    

    HTTP1.1

    • 长连接
    • 然后 http1.1 支持只发送 header 而不发送 body。原因是先用 header 判断能否 成功,再发数据,节约带宽,事实上,post 请求默认就是这样做的。
    • http1.1 的 host 字段。由于虚拟主机可以支持多个域名,所以一般将域名解析后 得到 host。
    • 管道网络传输
      HTTP/1.1 采用了常连接的方式,这使得管道pipeline网络传输称为了可能
      即可在同一个TCP连接里面,客户端发起多个请求,只要第一个请求发出去了,补习等待其回来,就可以发送第二个请求过去,可以减少整体的响应时间。
      但是服务器还是会按照顺讯,先回应请求A,完成后再回应请求B,要是前面的回应特别的满,后面就会有许多请求排队等待,这称为队头阻塞。

    HTTPS

    HTTP比较严重的缺点是不安全:

    • 信息采用的是明文传输,内容有可能会被窃听
    • 不验证通信放的身份,因此有可能会遭遇伪装
    • 无法证明报文的完整性,所以有可能遭到篡改

    HTTPS是如何解决上面的三个风险的?

    • 混合加密的方式实现信息传输的安全,解决了被窃听的风险
    • 将服务其的公钥放入到数字证书中,解决了冒充的风险。
    • 摘要算法的方式来实现完整性,能够为数据生成独一无二的指纹,指纹可以用于校验数据的完整性,解决了被篡改的风险。

    混合加密

    HTTPS采用的是对称加密和非对称加密结合的混合加密的方式

    • 在通信建立之前采用非对称加密的方式交换(对称加密的秘钥),后续就不再使用非对称加密
    • 在通信过程中全部使用对称加密的【会话秘钥】的方式加密明文数据。

    采用混合加密的原因

    • 对称使用相同的秘钥加密和解密,虽然运算速度快,但是秘钥的安全传输是个问题。
    • 非对称加密,使用公钥加密,私钥解密,公钥可以任意分发而私钥保密,速度较慢,但是解决了(对称加密秘钥的传输问题)

    摘要算法

    摘要算法用于实现完整性,能够为体现数据的特征,用于校验数据的完整性,防止数据被篡改。

    在这里插入图片描述
    客户端在发送明文之前会通过摘要算法算出数据的摘要信息, 发送数据的时候 会将 摘要 + 数据 一起经过对称加密 发送给服务器,服务器解密后,用相同的摘要算法对数据进行计算,然后比较两次的摘要信息是否相等,如果相等,则代表数据没有被篡改。

    数字证书

    客户端先向服务器索取公钥,然后用公钥加密信息,服务器收到密文之后,用自己的私钥解密
    这就存在问题,如果保证公钥不被篡改和可信度。
    所以这就需要借助第三方认证机构CA(数字证书认证机构),将服务器公钥放到数字证书(由数字证书认证机构颁发),只要证书是可信的,公钥就是可信的。

    HTTPS通信的详细流程

    1. 客户端发送请求
      首先,由客户端向服务器发起加密通信请求,主要是向服务器发送一下信息。
      (1)客户端支持的TLS版本
      (2)客户端生成的随机数(Client Random),后面用于生产[会话秘钥]
      (3)客户端支持的密码套件列表,用于摘要算法,如RSA加密算法。
    2. 服务端回应
      服务器收到客户端请求之后,向客户端发出响应。
      (1)确认TLS版本
      (2)服务器生成的随机数(Server Random),后面用于生产【会话私钥】
      (3)确认摘要算法 如RSA加密算法
      (4)服务器的数字证书
    3. 客户端回应
      客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的CA公钥,确认服务器的数字证书的真实性。如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用他来加密报文,向服务器发送如下信息:
      (1)一个随机数,该随机数会被服务器公钥加密
      (2) 加密通信算法改变的通知,表示随后的信息都讲用会话私钥加密通信
      (3)客户端握手结束通知,表示客户端的握手阶段已经结束。
      上面第一项的随机数是整个握手阶段的第三个随机数,这样服务器和客户端就同时有三个随机数,接着就用双方协商的加密算法,各自生成本次通信的会话秘钥。
    4. 服务端最后回应
      服务器再收到客户端的第三个随机数之后他通过协商的加密算法,计算出本次通信的会话秘钥,然后向客户端发送最后的信息。
      1. 加密算法改变通知,表示随后的信息都采用会话秘钥加密通信
      2. 服务器握手结束的通知。

    简单版 :

    1. 客户端发送连接请求,将自己支持的加密规则(摘要算法)告诉服务器

    2. 服务器从中选择出一个摘要算法以及自己的身份信息以证书的形式发送给客户端,其中包括(公钥,摘要算法,整数的办法机构)

    3. 客户端收到服务器传送回来的信息,要做以下几个事情

      1. 验证整数的合法性
      2. 生成对称加密的秘钥
      3. 用约定好的摘要算法,对对称加密的秘钥进行计算
      4. 用服务器的公钥加密(对称加密秘钥 + 摘要信息),然后发送给服务器
    4. 服务器端接受客户端传送回来的信息,要做以下几个事情

      1. 用私钥解密解析出摘要信息,再要摘要算法对对称秘钥进行加密,验证是否相同
      2. 使用秘钥加密信息。

    计算机网络书上,简要步骤:

    1. 协商加密算法
      • 浏览器A向服务器B发送浏览器的SSL版本号和一些可选的加密算法
      • B从中选择自己所支持的算法(RSA),并告知A
    2. 服务器鉴别
      • 服务器B向浏览器A发送包含其RSA公钥的数字证书
      • A使用该证书的颁发机构CA公关部的RSA公钥对该证书进行验证
    3. 会话秘钥计算 (由浏览器A随机产生一个秘密数)
      • 用服务器B的RSA公钥进行加密后发送给B
      • 双方根据协商的算法产生共享的对称对话会话秘钥
    4. 安全数据传输
      • 双方用会话秘钥加密和解密他们之间传送的数据并验证其完整性

    在这里插入图片描述

    HTTP2.0

    • 头部压缩
      HTTP2.0会压缩请求头部,如果你同时发出同个请求,但是他们的头信息是一样的,那么协议会帮你消除重复的部分。
    • 二进制格式
      HTTP2.0不再是像HTTP1.1里纯文本形式的报文,而是全面采用了二进制格式,头信息和数据i都是二进制的,并且统称为帧。头信息帧 和数据帧
      在这里插入图片描述
    • 数据流
      HTTP2.0的数据报不是按照顺序发送的,同一个连接里面连续的数据报,可能属于不同的回应。因此,必须要对数据报进行标记,指出它属于哪个回应。
      每个请求或回应的所有数据包,称之为一个数据流,每个数据流都标记着独一无二的编号,其中规定客户端发出的数据流编号为奇数,服务器发出的数据流编号为偶数
      客户端还可以指定数据流的优先级,优先级高的请求,服务器就响应该请求。
    • 多路复用
      Http2.0可以在一个连接中并发的发出多个请求或者响应,而不用按照顺序一一对应
      移除了HTTP1.1中的串行请求,不需要排队等待,也就不会出现队头阻塞的问题,大大降低了延迟,大幅度提高了连接的利用率。
    • 服务端推送
      HTTP2.0还在一定程度上改善了传统的请求应答工作模式,服务器不再是被动的响应。也可以主动的向客户端发送数据。
      举例来说,在浏览器刚请求HTML的时候,就提前把可能会用到的JS,CSS文件等静态资源主动发给客户端,减少延时的等待,也就是服务端推送。

    参考 :【面试必备】硬核!30 张图解 HTTP 常见的面试题

    展开全文
  • 下面我们用最简单的一对一的客户服务器模型来重现编程中遇到的一些问题: 初学socket的时候在编写socket程序的时候会遇到很多莫名其妙的问题,比如说bind函数返回的常见错误是EADDRINUSE 使用下面的程序重现这个...

    下面我们用最简单的一对一的客户服务器模型来重现编程中遇到的一些问题:

    初学socket的时候在编写socket程序的时候会遇到很多莫名其妙的问题,比如说bind函数返回的常见错误是EADDRINUSE


    使用下面的程序重现这个状态:

    client:

    int main(int argc, const char * argv[])
    {
    
        struct sockaddr_in serverAdd;
        
        bzero(&serverAdd, sizeof(serverAdd));
        serverAdd.sin_family = AF_INET;
        serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
        serverAdd.sin_port = htons(SERV_PORT);
        
        int connfd = socket(AF_INET, SOCK_STREAM, 0);
        
        int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
        if (connResult < 0) {
            printf("连接失败\n");
            close(connfd);
            return -1;
        }
        
        ssize_t writeLen;
        ssize_t readLen;
        char recvMsg[65535] = {0};
        char sendMsg[20] = "I am client";
        
        writeLen = write(connfd, sendMsg, sizeof(sendMsg));
        if (writeLen < 0) {
            printf("发送失败\n");
            close(connfd);
            return -1;
        }
        else
        {
            printf("发送成功\n");
        }
        
        while (1) {
            
    //        sleep(1);
            
            readLen = read(connfd, recvMsg, sizeof(recvMsg));
            if (readLen < 0) {
                printf("读取失败\n");
                close(connfd);
                return -1;
            }
            if (readLen == 0) {
                printf("服务器关闭\n");
                close(connfd);
                return -1;
            }
    
            printf("server said:%s\n",recvMsg);
            
        }
        
        close(connfd);
        return 0;
    }
    
    

    server:

    int main(int argc, const char * argv[])
    {
    
        struct sockaddr_in serverAdd;
        struct sockaddr_in clientAdd;
        
        bzero(&serverAdd, sizeof(serverAdd));
        serverAdd.sin_family = AF_INET;
        serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
        serverAdd.sin_port = htons(SERV_PORT);
        
        socklen_t clientAddrLen;
        
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        
        if (listenfd < 0) {
            printf("创建socket失败\n");
            close(listenfd);
            return -1;
        }
        
        int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
        if (bindResult < 0) {
            close(listenfd);
            printf("绑定端口失败,errno = %d\n",errno);
            return -1;
        }
        else
        {
            printf("绑定端口成功\n");
        }
        
        listen(listenfd, 20);
        
        int connfd;
        unsigned char recvMsg[65535];
        char replyMsg[20] = "I am server";
        
        clientAddrLen = sizeof(clientAdd);
        connfd = accept(listenfd,(struct sockaddr *)&clientAdd,&clientAddrLen);
        if (connfd < 0) {
            close(listenfd);
            printf("连接失败\n");
            return -1;
        }
        else
        {
            printf("连接成功\n");
        }
        
        ssize_t readLen = read(connfd, recvMsg, sizeof(recvMsg));
        printf("readLen:%ld\n",readLen);
        if (readLen < 0) {
            printf("读取失败\n");
            return -1;
        }
        else if (readLen == 0) {
            printf("读取完成\n");
            close(listenfd);
            return 0;
        }
        
        printf("client said:%s\n",recvMsg);
           
        while (1)
        {       
            write(connfd, replyMsg, sizeof(replyMsg));      
        }
        
        close(connfd);
    
        return 0;
    }
    
    

    首先运行服务器程序,再运行客户端,然后关闭服务端后立马再打开服务端,就会打印如下信息:48对应EADDRINUSE错误码

    绑定端口失败,errno 48


    这里要说明一个问题:

    当一个Unix进程无论自愿的(调用exit或者从main函数返回)还是非自愿的(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也将导致仍然打开的任何TCP连接上发出一个FIN。


    很明显服务器已经关闭了,为什么会绑定端口失败呢,下面是TCP连接终止的四个分节




    某些情况下第一个分节的FIN随数据一起发送,另外,第二个和第三个分节有可能被合并成一个分节;


    这里我们的服务器是主动关闭的一端,当主动发送FIN分节以后等待确认,状态变为FIN_WAIT_1,收到确认以后状态变为FIN_WAIT_2,收到客户端的FIN分节以后状态变为TIME_WAIT状态,这里在TIME_WAIT状态会停留2MSL后才会进入CLOSED状态;所以我们再立马启动服务器的时候,之前的连接还没有处于CLOSED状态,还存在者,所以就会绑定失败了;后面会讲到为什么会存在TIME_WAIT状态;

    这里客户端是被动关闭的一端,收到服务端的FIN之后状态进入CLOSE_WAIT,这个时候read方法会返回0,然后发送对第一个分节的确认,此时客户端调用close方法发送FIN分节给服务端进入LAST_ACK状态,等待确认到达,收到确认以后连接状态变为CLOSED


    TIME_WAIT状态:停留在该状态的持续时间是最长分节生命期(maximum segment lifetime,MSL)的两倍,有时候称为2MSL。MSL是任何IP数据报能够在因特网中存活的最长时间,最大值为255,这是一个跳数限制而不是真正的时间限制;


    TIME_WAIT状态存在理由:

    可靠地实现TCP全双工连接的终止:可能不得不重传最终那个ack,TIME_WAIT后是CLOSED,如果没有TIME_WAIT的2MSL,直接CLOSED,那么如果最后一个ACK丢失了,是不会重新再发送ACK的,那服务端收不到ACK就会重新发送最终那个FIN,这个时候客户端已经是CLOSED了,就会响应一个RST,这个RST就会被服务器解释成一个错误。

    允许来的重复分节在网络中消逝:保证每成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已经在网络中消逝了,从而不会被误解成新连接的分组。


    这里还有一种情况:打开客户端while里面的sleep,(或者屏蔽掉客户端下面的代码)然后再先运行服务器程序,再运行客户端,然后关闭服务端后立马再打开服务端,仍然会绑定失败,此时的状态和之前的有点不一样

    if (readLen == 0) {
                printf("服务器关闭\n");
                close(connfd);
                return -1;
            }

    我们从终端打印信息如下,此时服务器处于FIN_WAIT_2状态,就如上面说的因为客户端还没有关闭连接,没有发送第三个FIN分节,此时客户端因为已经收到来自服务端的FIN分节而处于CLOSE_WAIT状态;


    wanglijuntekiMac-mini:~ wanglijun$ netstat -an |grep 8000

    tcp4       0      0  192.168.1.103.8000     192.168.1.103.49632    FIN_WAIT_2 

    tcp4  290960      0  192.168.1.103.49632    192.168.1.103.8000     CLOSE_WAIT 


    解决方法:

    这里有一个SO_REUSEADDR套接字选项,打开之后就能解决如上的问题,我们在band之前添加如下设置代码:

    <span style="font-size:12px;">int yes = 1;
        setsockopt(listenfd,
                   SOL_SOCKET, SO_REUSEADDR,
                   (void *)&yes, sizeof(yes));</span>

    服务器重启监听时,试图捆绑现有连接上的端口会失败,(还有一种情况可能之前派生出来的子进程还处理着连接)如果设置了SO_REUSEADDR套接字选项,就会bind成功,所有的TCP服务器都应该指定SO_REUSEADDR套接字选项,以允许服务器在这种情形下被重新启动;SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可;


    对于TCP,我们绝不可能启动捆绑相同IP地址和相同端口号的多个服务器。


    参考:

    UNIX Network ProgrammingVolume 1, Third Edition: TheSockets Networking API


    展开全文
  • 下面我们用最简单的一对一的客户server编程模型重现遇到的一些问题: 初学者socket当写作socket名其妙的问题。比方说bind函数返回的常见错误是EADDRINUSE 使用以下的程序重现这个状态: client: int main...
  • #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #include <sys/wait.h>...time.h
  • #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #include <sys/wait.h>...time..
  • 对数据通信进行分层 1、最高层:应用层 所有的能产生网络流量的的程序 不是应用层:像电脑里的记事本、word断开网线也能运行 是应用层:QQ、微信、淘宝 2、 表示层 在传输之间是否进行加密或者压缩...TIME_WAIT就...
  • 先来看看我们的server端: 创建一个serversocket,进行监听,每来一个客户端,就启动一个新启动为其服务: ... //如果创建监听socket的时候发生异常,将会隔WAIT_TIME毫秒重试,直到成功 while (true){ try { ...
  • 文章目录TCP 11种状态连接建立TIME_WAIT 和SO_REUSEADDRSIGPIPE TCP 11种状态 详细介绍:链接 上图已有10种状态 还有一种CLOSING状态,是双方同时关闭 连接建立 TIME_WAIT 和SO_REUSEADDR SIGPIPE ...
  • poll&epll 模型

    2019-11-13 23:41:58
    POLL网络编程模型 PIPE信号的产生与处理,如何减少time_wait。 1.首先是创建socket,设置ip端口。(socket_nonbock,socket_closexec)每个socket都有自己的输入缓冲区和输出缓冲区。 2.再是设置端口复用,绑定...
  • 关于模型的一些疑惑

    2020-12-09 01:02:13
    所以想把网络改写成pytorch的。但是在研究代码和改写的过程中对多线程版本中保存的模型有一些疑惑,想向您请教一下,谢谢! 1、`if len(self.data_buffer)>self.batch_size*5: # training...
  • TCP四次挥手需要知道的细节点(CLOSE_WAITTIME_WAIT、MSL) TCP与UDP的区别与适用场景 linux常见网络模型详解(select、poll与epoll) epoll_event结构中的epoll_data_t的fd与ptr的使用场景 Windows常见的网络模型详解...
  • linux网络编程基础

    2019-10-22 15:24:46
    目录OSI七层模型TCP/IP五层(四层)模型数据包的封装和分用IP地址MAC地址HTTP...OSI(开放系统互联)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范,把网络从逻辑上分为了7层,每一层都有相关...
  • 文章目录七、计算机网络1、TCP/IP 五层模型2、浏览器输入地址后做了什么?3、三次握手与四次挥手4、TIME_WAIT 与 CLOSE_WAIT5、TCP 滑动窗口6、TCP 粘包和拆包 七、计算机网络 1、TCP/IP 五层模型 2、浏览器输入...
  • 网络模型 最简单的客户端和服务器程序实现代码 TCP和UDP的区别 二:TCP三次握手详析、telnet,wireshark示范 TCP连接的三次握手 telnet工具使用介绍 wireshark监控数据包 三:TCP状态转换,TIME_WAIT详解...
  • 文章目录5层模型TCP/UDP传输层CLOSE-WAITTIME-WAIT 的状态和意义TCP功能:全双工传输数据tcp头部控制字段tcp和udp的区别TCP优点:UDP优点:TCP为何需要握手?为何三次为什么需要握手为何三次握手?为何断开连接...
  • 2、互联网协议OSI标准模型TCP/TP协议簇IP 协议ICMP 协议ARP 协议TCP 协议UDP 协议FTP 协议DNS 协议SMTP 协议SLIP 协议PPP 协议五层协议及其作用3、TCP经典三次握手四次挥手为什么是三次握手四次挥手**TIME_WAIT状态...
  • 计算机网络

    2019-04-01 21:16:41
    什么是 HTTP 什么是 HTTPS HTTP 特点 针对 HTTP 无状态的一些解决策略 二者区别 客户端使用 HTTPS 方式...IP 地址和 MAC 地址的作用 OSI 七层模型和 TCP/IP 四层模型 ...TIME_WAIT TCP 可靠传输是如何保证的 socket AP...
  • 一、RT(Response-time、响应时间) 响应时间是用户请求发出和服务器返回之间的时间差。 这个过程包括DNS解析、网络数据传输、服务器计算、网络数据返回,如下图例子: 期中,服务器计算时间又可细分为: 1....
  • linux C网络编程基础

    2019-07-05 17:33:27
    1. 客户端和服务器的网络编程的系统调用API 2. 三次握手连接建立 ...3. read/write以及recv/send的区别 ...四次挥手time_wait, 确认最后一次数据能到达 OSI七层模型or五层模型 ...
  • Java网络编程一、OSI七层网络模型1.1 应用层1.2 表现层1.3 会话层(Session)1.4 传输层1.4.1 TCP协议1.4.2 UDP协议1.5 网路层1.6 数据链路层1.7 物理层小结二、TCP/IP四层网络模型三、TPC协议的三次握手四、TCP协议...
  • 文章目录TCP/IP和Linux客户端-服务器网络模型的基本概念套接字和地址TCP三次握手 如果我问你一些关于网络编程方面的问题,你会怎样回答呢? 大家经常说的四层、七层,分别指的是什么? TCP 三次握手是什么,TIME_...
  • 经典面试题 :网络通信题目集锦

    千次阅读 2019-09-02 20:06:11
    技术面试中常见的网络通信细节问题解答 1. TCP/IP协议栈层次结构 2. TCP三次握手需要知道的细节点 ...5. linux常见网络模型详解(select、poll与epoll) 6. epoll_event结构中的epoll_data_t的fd与ptr的使...
  • 文章目录引言OSI 七层模型传输层协议TCP 是如何做到可靠性?建立连接:三次握手断开链接:四次挥手TIME_WAIT...七层网络模型,前三层为用户相关,后四层为系统内核相关。 应用层 表示层 会话层 传输层 TC...
  • 面试官:看你简历说精通TCP和IP,那我们来讨论下网络模型和TCP、IP协议,讲下你的理解先 面试官:看你画的图,TCP有自己的首部结构,这都有哪些字段,最好说说它们的作用 面试官:那TCP和UDP有什么区别 面试官:...

空空如也

空空如也

1 2 3 4 5
收藏数 100
精华内容 40
关键字:

网络模型timewait