零碎的知识点-3

零碎的知识点-3


TCP拥塞控制-慢启动、拥塞避免、快重传、快启动

一般原理:发生拥塞控制的原因:资源(带宽、交换节点的缓存、处理机)的需求>可用资源。

作用:拥塞控制就是为了防止过多的数据注入到网络中,这样可以使网络中的路由器或者链路不至于过载。拥塞控制要做的都有一个前提:就是网络能够承受现有的网络负荷。

对比流量控制:拥塞控制是一个全局的过程,涉及到所有的主机、路由器、以及降低网络相关的所有因素。流量控制往往指点对点通信量的控制。是端对端的问题。

拥塞窗口:发送方为一个动态变化的窗口叫做拥塞窗口,拥塞窗口的大小取决于网络的拥塞程度。发送方让自己的发送窗口等于拥塞窗口,但是发送窗口不是一直等于拥塞窗口的,在网络情况好的时候,拥塞窗口不断的增加,发送方的窗口自然也随着增加,但是接受方的接受能力有限,在发送方的窗口达到某个大小时就不在发生变化了。

发送方如果知道网络拥塞了呢?发送方发送一些报文段时,如果发送方没有在时间间隔内收到接收方的确认报文段,则就可以人为网络出现了拥塞。

慢启动算法的思路:主机开发发送数据报时,如果立即将大量的数据注入到网络中,可能会出现网络的拥塞。慢启动算法就是在主机刚开始发送数据报的时候先探测一下网络的状况,如果网络状况良好,发送方每发送一次文段都能正确的接受确认报文段。那么就从小到大的增加拥塞窗口的大小,即增加发送窗口的大小。

慢开始和拥塞避免算法的实现举例

拥塞避免:为了防止cwnd增加过快而导致网络拥塞,所以需要设置一个**慢开始门限**ssthresh状态变量(拥塞控制的标识),它的用法:

               1. 当cwnd < ssthresh,使用慢启动算法,

               2. 当cwnd > ssthresh,使用拥塞控制算法,停用慢启动算法。

               3. 当cwnd = ssthresh,这两个算法都可以。

拥塞避免的思路:是让cwnd缓慢的增加而不是加倍的增长,每经历过一次往返时间就使cwnd增加1,而不是加倍,这样使cwnd缓慢的增长,比慢启动要慢的多。

无论是慢启动算法还是拥塞避免算法,只要判断网络出现拥塞,就要把慢启动开始门限(ssthresh)设置为设置为发送窗口的一半(>=2),cwnd(拥塞窗口)设置为1,然后在使用慢启动算法,这样做的目的能迅速的减少主机向网络中传输数据,使发生拥塞的路由器能够把队列中堆积的分组处理完毕。拥塞窗口是按照线性的规律增长,比慢启动算法拥塞窗口增长块的多。

快重传:快重传算法要求首先接收方收到一个失序的报文段后就立刻发出重复确认,而不要等待自己发送数据时才进行捎带确认。接收方成功的接受了发送方发送来的M1、M2并且分别给发送了ACK,现在接收方没有收到M3,而接收到了M4,显然接收方不能确认M4,因为M4是失序的报文段。如果根据可靠性传输原理接收方什么都不做,但是按照快速重传算法,在收到M4、M5等报文段的时候,不断重复的向发送方发送M2的ACK,如果接收方一连收到三个重复的ACK,那么发送方不必等待重传计时器到期,由于发送方尽早重传未被确认的报文段。

快重传示意图

快恢复

  1. 当发送发连续接收到三个确认时,就执行乘法减小算法,把慢启动开始门限(ssthresh)减半,但是接下来并不执行慢开始算法。
  2. 此时不执行慢启动算法,而是把cwnd设置为ssthresh的一半, 然后执行拥塞避免算法,使拥塞窗口缓慢增大。

快恢复


TCP报头

TCP/IP协议栈主要分为四层:应用层、传输层、网络层、数据链路层,每层都有相应的协议,如下图

TCP/IP协议体系

TCP数据报头格式:
TCP数据报头格式

  • 端口号:32位,16位源端口+16位目的端口, 最大端口为65536
  • 位序号:32位,也称顺序号,简写位SEQ。
  • 确认序号:32位,简写为ACK,在握手阶段,确认序号将发送方的序号加1作为回答。
  • 数据偏移:规定了整个报头的长度。
  • 6位标志字段:
    • ACK 置1时表示确认号(为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。
    • RST 置1时重建连接。如果接收到RST位时候,通常发生了某些错误。
    • SYN 置1时用来发起一个连接。
    • FIN 置1时表示发端完成发送任务。用来释放连接,表明发送方已经没有数据发送了。
    • URG 紧急指针,告诉接收TCP模块紧要指针域指着紧要数据。注:一般不使用。
    • PSH 置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送。注:一般不使用。
  • 检验和:16位,检验和覆盖了整个的TCP报文段: TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。
  • 紧急指针:16位,一般不用。
  • 可选与变长选项:通常为空,可根据首部长度推算。用于发送方与接收方协商最大报文段长度(MSS,1460是以太网默认的大小),或在高速网络环境下作窗口调节因子时使用。首部字段还定义了一个时间戳选项。

UDP报头

UDP协议也是传输层协议,它是无连接,不保证可靠的传输层协议。
UDP报头

  • 源端口:16位,源端口是一个大于1023的16位数字,由基于UDP应用程序的用户进程随机选择。
  • 目的端口:16位,
  • 数据包长度:16位,指明了包括首部在内的UDP报文段长度。UDP长字段的值是UDP报文头的长度(8字节)与UDP所携带数据长度的总和。
  • 校验和:16位,是指整个UDP报文头和UDP所带的数据的校验和(也包括伪报文头)。伪报文头不包括在真正的UDP报文头中,但是它可以保证UDP数据被正确的主机收到了。因在校验和中加入了伪头标,故ICMP除能防止单纯数据差错之外,对IP分组也具有保护作用。

DNS解析过程

DNS(Domain Name System,域名系统),DNS协议运行在UDP协议之上,使用端口号53。

主机解析域名的顺序

  1. 浏览器缓存:浏览器会按照一定的频率缓存DNS记录。
  2. 操作系统缓存:如果浏览器缓存中找不到需要的DNS记录,那就去操作系统中找(如/etc/hosts文件)。
  3. 路由缓存:路由器也有DNS缓存。
  4. ISP的DNS服务器:ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS查询请求。
  5. 根服务器:ISP的DNS服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS服务器先问根域名服务器.com域名服务器的IP地址,然后再问.com域名服务器,依次类推)。(root -> com -> baidu.com)

get和post请求的区别

GET的语义是请求获取指定的资源。GET方法是安全、幂等、可缓存的(除非有 Cache-ControlHeader的约束),GET方法的报文主体没有任何语义。

POST的语义是根据请求负荷(报文主体)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST不安全,不幂等,(大部分实现)不可缓存。

  • GET后退按钮/刷新无害,POST数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
  • GET书签可收藏,POST为书签不可收藏。
  • GET能被缓存,POST不能缓存 。
  • GET编码类型application/x-www-form-url,POST编码类型encodedapplication/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
  • GET历史参数保留在浏览器历史中。POST参数不会保存在浏览器历史中。
  • GET对数据长度有限制,当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。POST无限制。
  • GET只允许 ASCII 字符。POST没有限制。也允许二进制数据。与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET !

http和https区别,https在请求时额外的过程,https是如何保证数据安全的

http是应用层协议,它会将要传输的数据以明文的方式给传输层,这样显然不安全。https则是在应用层与传输层之间又加了一层,该层遵守SSL/TLS协议。

加密的方式有两种,
* 对称加密:对称加密速度快,但是加密和解密的钥匙是相同的。
* 非对称加密,算法更加复杂,速度慢,加密和解密钥匙不相同。

在https中,加密过程大致如下:
首先服务器将公钥给浏览器,浏览器拿到公钥之后,生成一个“会话密钥”,这个会话密钥属于对称加密,然后用公钥加密这个“会话密钥”发送给服务器,最后,在数据传输的过程中,就用这个会话密钥来加密数据。打个比方:我有二把钥匙,我把其中一把钥匙放在信封里,然后把信封交给你。但是这个信封只有你能打开。

上述的过程是在3次握手中完成,采用明文发送,握手完成以后,客户端和服务端就约定好了“会话密钥”,以后的数据传输,就采用这个会话密钥加密。

在上述的过程中要注意
公钥怎么给客户端?
如果公钥A在发送给客户端的过程中,被截取,被黑客替换成了公钥B,以后的事情可想而知,你就会用公钥B加密“会话密钥”,然后。。。你的隐私就被窃取喽。对于这个问题,要引入证书和数据签名的概念。


Java正则表达式

java.util.regex 包主要包括以下三个类:

  • Pattern 类:
    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

  • Matcher 类:
    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

捕获组

捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。

      // 按指定模式在字符串查找
      String line = "This order was placed for QT3000! OK?";
      String pattern = "(\\D*)(\\d+)(.*)";

      // 创建 Pattern 对象
      Pattern r = Pattern.compile(pattern);

      // 现在创建 matcher 对象
      Matcher m = r.matcher(line);
      if (m.find( )) {
         System.out.println("Found value: " + m.group(0) );
         System.out.println("Found value: " + m.group(1) );
         System.out.println("Found value: " + m.group(2) );
         System.out.println("Found value: " + m.group(3) ); 
      } else {
         System.out.println("NO MATCH");
      }

切记,要先调用find( )方法才能调用group()方法


初步理解AQS

AQS全称AbstractQueuedSynchronizer,它是concurrent包中最重要的基础设施类之一,负责作为模板类向业务层提供对临界区的管理。AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch/FutureTask。

它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。这里volatile是核心关键词,具体volatile的语义,

AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

类名 互斥锁 资源 如何访问 备注
Mutex 互斥锁 1 仅排他 出现在AQS类的注释中
Semaphore 信号量 可指定(构造参数permits) 仅共享
ReentrantReadWriteLock 读写(可重入)锁 无上限(实际为16位unsigned short) 读写共享访问,写锁排他访问 可重入

复用AQS

Step1/3. 定义state。
state是一个int,它没有具体语义,但作为AQS的核心成员变量,可根据业务类的需要赋予state具体的语义。

| 方法 | 简介 |
| getState() | 读状态 | setState (int newState) | 写状态 |
| compareAndSetState (int expect, int update) | CAS写状态 |

Step2/3. 新建内部类Sync,继承自AQS并按需实现钩子方法

钩子方法 简介
tryAcquire (int arg) 排他获取(资源数)
tryRelease(int arg) 排他释放(资源数)
tryAcquireShared(int arg) 共享获取(资源数)
tryReleaseShared(int arg) 共享释放(资源数)
isHeldExclusively() 是否排他状态

Step3/3. 业务类把业务方法的实现委托给Sync。Sync可以直接使用继承自AQS的模板方法。

AQS内部细节

概述

AQS实现了一个FIFO队列,用于管理等候在临界区外的线程。队列的每个节点保留了线程信息,因此线程可以安然休眠,待临界区的资源可用时再次唤醒。注意,队列的存在并不保证公平,因为AQS并不确保被唤醒的队首线程能进入临界区,唤醒过程中可能会被其它尚未入队的竞争线程抢占先机。

线程控制

线程的休眠和唤醒是通过LockSupport.park()LockSupport.unpark()实现的。LockSupport是整个concurrent包最底层的API之一。在AQS的注释中写到,如果你对其排队机制不满,可以重新定义队列的数据结构并通过LockSupport调度。

队列

数据结构:双向链表构成的FIFO队列(链表是其物理结构,队列是其逻辑用途。注释中称其为CLH Lock队列的变种,而CLH Lock常用于实现自旋锁,其确保无饥饿性和公平性)

CLH Lock 队列


LockSupport的park和unpark的基本使用,以及对线程中断的响应性

LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语。java锁和同步器框架的核心AQS:AbstractQueuedSynchronizer,就是通过调用LockSupport.park()和LockSupport.unpark()实现线程的阻塞和唤醒的。LockSupport很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继续执行;如果许可已经被占用,当前线程阻塞,等待获取许可。

许可默认是被占用的。

LockSupport是不可重入。

线程如果因为调用park而阻塞的话,能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException。

猜你喜欢

转载自blog.csdn.net/weixin_35040169/article/details/81569210