高并发系统设计之限流
本文已收录至Github,推荐阅读 👉 Java随想录
文章目录
- 限流算法
- 计数器算法
- 滑动窗口
- 漏桶算法
- 令牌桶算法
- 限流算法实现
- Guava RateLimiter实现限流
- 令牌预分配
- 预热限流
- Nginx 限流
- limit_conn
- limit_req
- 黑白名单限流
这篇文章来讲讲限流,在高并发系统中限流是必不可少的,限流可以保证一部分的请求得到正常的响应,是一种自我保护的措施。限流可以保证使用有限的资源提供最大化的服务能力,按照预期流量提供服务,超过的部分将会拒绝服务、排队或等待、降级等处理。
首先,先来了解下几种限流算法。
限流算法
计数器算法
计数器算法是限流算法里最简单也是最容易实现的一种算法。
举个例子:我们规定接口A在1分钟内访问次数不能超过1000个。我们可以设置一个计数器,对固定时间窗口1分钟进行计数,每有一个请求,计数器就+1,如果请求数超过了阈值,则舍弃该请求,当时间窗口结束时,重置计数器为0。
计数器算法虽然简单,但是有一个十分致命的问题,那就是临界问题。
假设有一个用户,他在0:59时,瞬间发送了1000个请求,并且1:01又发送了1000个请求,那么其实用户在 2秒里面,瞬间发送了2000个请求。用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能利用这个漏洞卡Bug,瞬间压垮我们的应用。
缺点:没有办法防止时间范围临界点突发大流量,很可能在时间范围交界处被大量请求直接打到降级,影响后续服务。
滑动窗口
滑动窗口算法解决了上诉计数器算法的缺点。计数器的时间窗口是固定的,而滑动窗口的时间窗口是动态的。
整个红色的矩形框表示一个时间窗口,在我们的例子中,一个时间窗口就是一分钟。然后我们将时间窗口进行划分,比如图中,我们就将滑动窗口划成了6格,所以每格代表的是10秒钟。每过10秒钟,我们的时间窗口就会往右滑动一格。每一个格子都有自己独立的计数器,比如当一个请求在0:35秒的时候到达,那么0:30~0:39对应的计数器就会加1。
那么滑动窗口怎么解决刚才的临界问题的呢?我们可以看上图,0:59到达的1000个请求会落在灰色的格子中,而1:01到达的请求会落在橘黄色的格子中。当时间到达1:00时,我们的窗口会往右移动一格,那么此时时间窗口内的总请求数量一共是2000个,超过了限定的1000个,所以此时能够检测出来触发限流。
当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。
缺点:滑动窗口无法平滑控制请求流量,仅能控制时间段内请求总量,宏观来看,时间轴上的请求数量波形可能出现较大的波动。
漏桶算法
说到漏桶算法的时候,我们脑中先构思出一幅图:一个水桶,桶底下有一个小孔,水以固定的频率流出,水龙头以任意速率流入水,当水超过桶则”溢出“。
漏桶算法的话保证了固定的流出速率,这是漏桶算法的优点,也可以说是缺点。始终恒定的处理速率有时候并不一定是好事情,对于突发的请求洪峰,在保证服务安全的前提下,应该尽最大努力去响应,这个时候漏桶算法显得有些呆滞,最终可能导致水位”溢出“,请求被丢弃。
缺点:无法应对突发流量,由于处理速度恒定,当大量请求到来时,用户等待时间长,用户体验差。
令牌桶算法
对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。
令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。
缺点:令牌桶的数量,生成的速度需要根据以往的系统性能以及用户习惯等经验的累积来判断,实际限流数难以预知。
限流算法实现
Guava RateLimiter实现限流
引入依赖
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>
下面是一个使用的简单例子:
import com.google.common.util.concurrent.RateLimiter;public class RateLimiterTest {public static void main(String[] args) {RateLimiter rateLimiter = RateLimiter.create(1); //创建一个每秒产生一个令牌的令牌桶for (int i = 1; i <= 5; i++) {double waitTime = rateLimiter.acquire(i); //一次获取i个令牌System.out.println("acquire:" + i + " waitTime:" + waitTime);}}
}结果:acquire:1 waitTime:0.0
acquire:2 waitTime:0.995081
acquire:3 waitTime:1.998054
acquire:4 waitTime:2.999351
acquire:5 waitTime:3.999224
可以发现等待时间差不多间隔都是1秒。
RateLimiter是个抽象类,子类SmoothRateLimiter又做了层抽象,SmoothRateLimiter有两个子类SmoothBursty和SmoothWarmingUp。
- SmoothBursty: 令牌的生成速度恒定。使用 RateLimiter.create(double permitsPerSecond) 创建的是 SmoothBursty 实例。
- SmoothWarmingUp:令牌的生成速度持续提升,直到达到一个稳定的值。WarmingUp,顾名思义就是有一个热身的过程。使用 RateLimiter.create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) 时创建就是 SmoothWarmingUp 实例,其中 warmupPeriod 就是热身达到稳定速度的时间。
SmoothWarmingUp可以理解为是进阶版的SmoothBursty。
令牌预分配
RateLimiter 使用令牌桶算法,会进行令牌的累积,令牌会被预先分配。
public class RateLimiterTest {public static void main(String[] args) {RateLimiter r = RateLimiter.create(5);while (true) {System.out.println("get 5 tokens: " + r.acquire(5) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("end");/*** output:* get 5 tokens: 0.0s* get 1 tokens: 0.996766s 滞后效应,需要替前一个请求进行等待* get 1 tokens: 0.194007s* get 1 tokens: 0.196267s* end* get 5 tokens: 0.195756s* get 1 tokens: 0.995625s 滞后效应,需要替前一个请求进行等待* get 1 tokens: 0.194603s* get 1 tokens: 0.196866s*/}}
}
RateLimiter 由于会累积令牌,所以可以应对突发流量。有一个请求会直接请求5个令牌,但是由于此时令牌桶中有累积的令牌,足以快速响应。 RateLimiter 在没有足够令牌发放时,采用滞后处理的方式,也就是前一个请求获取令牌所需等待的时间由下一次请求来承受,也就是代替前一个请求进行等待。
预热限流
RateLimiter 的 SmoothWarmingUp 是带有预热期的平滑限流,它启动后会有一段预热期,逐步将分发频率提升到配置的速率。
public class RateLimiterTest {public static void main(String[] args) {RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS);while (true) {System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("get 1 tokens: " + r.acquire(1) + "s");System.out.println("end");/*** output:* get 1 tokens: 0.0s* get 1 tokens: 1.329289s* get 1 tokens: 0.994375s* get 1 tokens: 0.662888s 上边三次获取的时间相加正好为3秒* end* get 1 tokens: 0.49764s 正常速率0.5秒一个令牌* get 1 tokens: 0.497828s* get 1 tokens: 0.49449s* get 1 tokens: 0.497522s*/}}
}
创建一个平均分发令牌速率为2,预热期为3秒。令牌桶一开始并不会0.5秒发一个令牌,而是频率越来越高,在3秒钟之内达到原本设置的频率,以后就以固定的频率输出。
介绍几个重要的参数
abstract class SmoothRateLimiter extends RateLimiter {//当前存储令牌数double storedPermits;//最大存储令牌数double maxPermits;//添加令牌时间间隔double stableIntervalMicros;private long nextFreeTicketMicros;
}
通过Debug我们可以看到,SmoothBursty方法的最大令牌数被设置成了,maxBurstSeconds * permitsPerSecond,而maxBurstSeconds默认是1。
而 SmoothWarmingUp最大令牌数的计算方法要复杂的多。
Nginx 限流
对于Nginx接入层限流可以使用 Nginx自带的两个模块:连接数限流模块ngx_http _limit_conn_module和漏桶算法实现的请求限流模块ngx_http_limit_req_module。
limit_conn 用来对某个key对应的总的网络连接数进行限流,可以按照如IP、域名维度进行限流。limit_req用来对某个key对应的请求的平均速率进行限流,有两种用法:平滑模式(delay)和允许突发模式(nodelay)。
limit_conn
limit_conn是对某个key对应的总的网络连接数进行限流。可以按照IP来限制IP维度的总连接数,或者按照服务域名来限制某个域名的总连接数。但是,记住不是每个请求连接都会被计数器统计,只有那些被Nginx处理的且已经读取了整个请求头的请求连接才会被计数器统计。
http {limit_conn_zone $binary_remote_addr zone=addr:10m;limit_conn_log_level error;limit_conn_status 503 ;server {location /limit {limit_conn addr l;}}
}
- limit_conn:要配置存放key和计数器的共享内存区域和指定key的最大连接数。此处指定的最大连接数是1,表示Nginx最多同时并发处理1个连接,addr就是限流key,对应上文 zone=addr。
- limit_conn_zone:用来配置限流key及存放key对应信息的共享内存区域大小。此处的key是**binaryremoteaddr∗∗,表示IP地址,也可以使用∗∗binary_remote_addr**,表示IP地址,也可以使用**binaryremoteaddr∗∗,表示IP地址,也可以使用∗∗server_name**作为key来限制域名级别的最大连接数。
- limit_conn_status:配置被限流后返回的状态码,默认返回503。
- limit_conn_log_level:配置记录被限流后的日志级别,默认error级别。
limit_req
limit_req 是漏桶算法实现,用于对指定key 对应的请求进行限流,比如,按照 IP维度限制请求速率。配置示例如下:
http {limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;limit_conn_log_level error;limit_conn_status 503;server {location /limit {limit_req zone=one burst=20 nodelay;}}
}
limit_req 和 limit_conn 的配置类似。
- limit_req:配置限流区域,上面的参数会让nginx 每个IP一秒钟只处理一个请求。
- burst: burst 参数定义了超出 zone 指定速率的情况下,客户端还能发起多少请求,超出速率的请求将会被放入队列,我们将队列大小设置为20。这意味着,如果从一个给定 IP 地址发送 21 个请求,Nginx 会立即将第一个请求发送到上游服务器群,然后将余下 20 个请求放在队列中。然后每1秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过 20 时,Nginx 才会向客户端返回 503。
- nodelay:配置 burst 参数将会使通讯更流畅,但是可能会不太实用,因为该配置会使站点看起来很慢。在上面的示例中,队列中的第 20 个包需要等待 20 秒才能被转发,此时返回给客户端的响应可能不再有用。要解决这个情况,可以在 burst 参数后添加 nodelay 参数。使用 nodelay 参数,当一个请求到达“太早”时,只要在队列中能分配位置,Nginx 将立即转发这个请求。将队列中的该位置标为”taken”(占据),并且不会被释放以供另一个请求使用,直到一段时间后才会被释放。假设如前所述,队列中有 20 个空位,从给定的 IP 地址发出的 21 个请求同时到达。Ngin x会立即转发这个 21 个请求,并且标记队列中占据的 20 个位置,然后每 1秒释放一个位置。如果是25个请求同时到达,Nginx 将会立即转发其中的 21 个请求,标记队列中占据的 20 个位置,并且返回 503 状态码来拒绝剩下的 4 个请求。如果希望不限制两个请求间允许间隔的情况下实施“流量限制”,nodelay 参数是很实用的。
- limit_req_zone:配置限流key、存放key对应信息的共享内存区域大小、固定请求速率。此处指定的key是“$binary_remote_addr”,表示IP地址。10m表示共享内存的大小,16000 个 IP 地址的状态信息,大约需要 1MB,所以示例中区域可以存储 160000 个 IP 地址。
- limit_conn_status:配置被限流后返回的状态码,默认返回503。
- limit_conn_log_level:配置记录被限流后的日志级别,默认级别为error。
黑白名单限流
geo $limit {default 1;10.0.0.0/8 0;192.168.0.0/64 0;
}
map $limit $limit_key {0 "";1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
server {location / {limit_req zone=req_zone burst=10 nodelay;}
}
geo 指令将给在白名单中的 IP 地址对应的 $limit 变量分配一个值 0,给其它不在白名单中的分配一个值 1。然后我们使用一个映射将这些值转为 key。
白名单内 IP 地址的**$limit_key变量被赋值为空字符串,不在白名单内的被赋值为客户端的 IP 地址。当limit_req_zone**后的第一个参数是空字符串时,不会应用“流量限制”,所以白名单内的 IP 地址不会被限制。其它所有 IP 地址都会被限制到每秒 5 个请求。
而要做出网站黑名单,就有可能要屏蔽一堆ip,但是如果将其放在nginx.conf文件夹下,既不美观,也不利于管理,因此需要单独写出一个conf文件,然后在nginx.conf中使用 include标签引用它。
如果我们不是要限流,而是要直接实现黑名单禁止访问网站的话。可以使用allow和deny标签。
server{listen: 80;server_name www.baidu.com;allow all; #允许访问所有的ipdeny 172.0.0.1; #禁止 172.0.0.1 访问
}
可以配合shell脚本,然后把脚本加入crontab定时任务就可以实现动态添加黑名单。
#!/bin/bash
#取最近5w条数据
tail -n50000 /usr/local/nginx/logs/access.log \
#过滤需要的信息行ip等
|awk '{print $1,$12}' \
#过滤爬虫
|grep -i -v -E "google|yahoo|baidu|msnbot|FeedSky|sogou|360|bing|soso|403|admin" \
#统计
|awk '{print $1}'|sort|uniq -c|sort -rn \
#超过1000加入黑名单
|awk '{if($1>1000)print "deny "$2";"}' >> /usr/local/nginx/conf/blockip.conf
#重启nginx生效
/usr/local/nginx/sbin/nginx -s reload
本篇文章就到这里,感谢阅读,如果本篇博客有任何错误和建议,欢迎给我留言指正。
相关文章:

高并发系统设计之限流
本文已收录至Github,推荐阅读 👉 Java随想录 文章目录限流算法计数器算法滑动窗口漏桶算法令牌桶算法限流算法实现Guava RateLimiter实现限流令牌预分配预热限流Nginx 限流limit_connlimit_req黑白名单限流这篇文章来讲讲限流,在高并发系统中…...

ZCMU--5286: Rose的字符串(C语言)
Description 一天Rose同学想得到一个仅由01组成的字符串S,Jack同学为了让Rose同学开心,于是打算去商店购买另一个也仅由01组成的字符串T。而商店的字符串价格由它的长度决定,比如字符串011售价3元,001011售价6元,商店…...

MAC下搭建hadoop
一:简介 Hadoop是一个用Java开发的开源框架,它允许使用简单的编程模型在跨计算机集群的分布式环境中存储和处理大数据。它的设计是从单个服务器扩展到数千个机器,每个都提供本地计算和存储。特别适合写一次,读多次的场景。 Hado…...

Python如何实现自动登录和下单的脚本,请看selenium的表演
前言 学python对selenium应该不陌生吧 Selenium 是最广泛使用的开源 Web UI(用户界面)自动化测试套件之一。Selenium 支持的语言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驱动…...

华为OD机试真题Python实现【关联子串】真题+解题思路+代码(20222023)
关联子串 题目 给定两个字符串str1和str2 如果字符串str1中的字符,经过排列组合后的字符串中 只要有一个是str2的子串 则认为str1是str2的关联子串 若不是关联子串则返回-1 示例一: 输入: str1="abc",str2="efghicaibii" 输出: -1 预制条件: 输入的…...

Flutter+【三棵树】
定义 在Flutter中和Widgets一起协同工作的还有另外两个伙伴:Elements和RenderObjects;由于它们都是有着树形结构,所以经常会称它们为三棵树。 这三棵树分别是:Widget、Element、RenderObject Widget树:寄存烘托内容…...

若依系统【SpringBoot】如何集成qq邮件发送【超详细,建议收藏】
若依系统的部署博主就不在这儿阐述了,默认大家的电脑已经部署好了若依系统,这里直接开始集成邮件系统,首先我们得需要对qq邮箱进行配置;一套学不会你来打我😀; 一、开启我们的qq邮箱发送邮件的配置 1、先进…...

kettle使用--1.mysql多表关联导入mongoDB
文章目录1. 初步体验:csv 转为excelKettle概念配置mysql链接mysql 一对多关联查询结果保存到mongodb中1. 初步体验:csv 转为excel Windows环境下安装pdi-ce-8.0.0.0-28.zip ,解压后执行lib下的Spoon.bat 将csv输入拖入 双击拖进去的csv&…...

2023年CDGA考试-第10章-参考数据和主数据(含答案)
2023年CDGA考试-第10章-参考数据和主数据(含答案) 单选题 1.实现主数据中心环境的三种基本方法中不包括哪种? A.参考目录 B.注册表 C.交易中心 D.混合模式 答案 A 2.参考数据还具有很多区别于其他主数据 (例如,企业结构数据和交易结构数据)的特征。以下哪项目描述错误的…...

2023年,什么行业更有发展前景?
关于有前景有发展的行业推荐,小课今天还是推荐咱们IT互联网行业。 很多人会说现在懂电脑的那么多,这个行业都饱和了,很多学电脑的找不到工作都改行了。但事实是现在每个行各业都需要互联网,需要懂电脑的技术人才,尤其是在云计算、大数据到来…...

致盛咨询携手亚马逊云科技进一步开拓中国市场
作为医疗保健领域的咨询公司,ZS需要保证服务可靠性、敏捷性和安全性的同时,获得经济效益。亚马逊云科技丰富的云服务产品简化了ZS基础架构的搭建,为ZS节省了大量的人力与资金成本。同时,缩短了ZS扩展基础设施的周转时间࿰…...

ts之 命名空间 namespace、三斜线指令、声明文件(declare 声明ts的变量函数第三方模块等 )
目录ts之 命名空间 namespacets之 命名空间 namespacets之 三斜线指令 ( 引入其他.ts文件 )app.tsindex.tsts之 声明文件 d.ts - declare01:declare声明express第三方模块typings 为代码或者第三方模块 编写声明文件index.ts02:de…...

Day898.Join语句执行流程 -MySQL实战
Join语句执行流程 Hi,我是阿昌,今天学习记录的是关于Join语句执行流程的内容。 在实际生产中,关于 join 语句使用的问题,一般会集中在以下两类: 不让使用 join,使用 join 有什么问题呢?如果有…...

ChatGPT商业前景如何?人工智能未来会如何发展?
ChatGPT不仅在互联网和多个行业引发人们的关注,在投资界还掀起了机构对人工智能领域的投资热潮。人工智能聊天程序ChatGPT在去年11月亮相之后,在推出仅两个月后,今年1月份的月活用户已达到了1亿,成为史上增长最快的消费者应用程序…...

代码随想录第十六天(347、194、195、94)
347. 前 K 个高频元素 答案 思路: 1、首先,用到了每个值对应的出现次数,想到要用哈希map存放 2、还需要将出现频率从大到小进行排序,找出前k个元素 3、时间复杂度应该比O(nlogn)小 如果想用快速排序&…...

< elementUI组件样式及功能补全: 实现点击steps组件跳转对应步骤 >
文章目录👉 前言👉 一、效果演示👉 二、点击steps跳转效果实现👉 三、实现案例往期内容 💨👉 前言 在 Vue elementUi 开发中,elementUI中steps步骤条组件只提供了change方法,并未提…...

【学习笔记】互联网金融:芝麻信用分的建模过程
学习资料: 数据分析学习随记 | 互联网金融行业2C授信模型(芝麻信用) 1. 背景 互联网金融的本质是风控。 1.1 数据分析师的角色 数据分析师在金融行业基本上有两种角色: 1.1.1 数据建模师 偏算法,但要很懂业务。要求对算法的理解较深&am…...

Linux C/C++或者嵌入式开发到底有没有35岁危机?
一个读者问了一个问题: 我现在25岁,双非一本本科。在深圳上班,做嵌入式开发,打算走Linux C/C开发,工资目前一般。读了前辈写的很多博客之后,觉得很棒。我现在有一些疑问。 1.最近互联网裁员很厉害嘛&#x…...

国内领先的十大API接口排行
应用程序编程接口API即(Application Programming Interface),现在众多企业的应用系统中常用的开放接口,对接相应的系统、软件功能,简化专业化的程序开发。 一、百度API 百度API超市开通1136个数据服务接口。 网址&a…...

【Linux】Kickstart 配置U盘自动化安装Linux系统
文章目录前言一、刻录USB二、配置以BIOS方式启动引导2.1 引导文件配置2.2 KS文件配置三、以EFI方式启动引导3.1 引导文件3.2 KS文件四、 总结前言 之前安装系统,例如在VMware虚拟机中或物理服务器中,都是根据图形界面上的指示进行下一步这类的操作。 现…...

【Spring MVC】这一篇,带你从入门到进阶
目录 1、什么是MVC? 2、什么是 Spring MVC 3、如何学好 Spring MVC? 3.1、如何创建 Spring MVC 项目 3.1.1、使用Spring Initializr创建(推荐) 3.2、将 Spring 程序与用户(浏览器)联通 3.3、基础注解…...

InstallAware Multi-Platform updated
InstallAware Multi-Platform updated 原生ARM:为您的内置设置、IDE和整个工具链添加了Apple macOS和Linux ARM构建。 本地化:引擎内多语言感知,可再分发工具,具有资产隔离功能,使您的IP保持安全。 模板:将…...

Spring Batch 高级篇-多线程步骤
目录 引言 概念 案例 转视频版 引言 接着上篇:Spring Batch ItemWriter组件,了解Spring Batch ItemWriter处理组件后,接下来一起学习一下Spring Batch 高级功能-多线程步骤 概念 默认的情况下,步骤基本上在单线程中执行&…...

关于iframe一些通讯的记录(可适用工作流审批)
一.知识点(1).我们可以通过postMessage(发送方)和onmessage(接收方)这两个HTML5的方法, 来解决跨页面通信问题,或者通过iframe嵌套的不同页面之间的通信a.父页面代码如下<div v-if"src" class"iframe"><iframeref"iframe"id…...

JavaWeb
1、静态Web html、css 2、动态Web 提供给所有人看的数据始终会发生变化。技术栈:Servlet/JSP,ASP,PHP。 Web应用程序:可以提供浏览器访问的程序。 1、这个统一的web资源会被放在同一个文件夹下,web应用程序-->Tom…...

ip段192.168.1.0/24和192.168.0.0/16
192.168.1.0/24192.168.1.1 ~ 192.168.1.254前24位为网络前缀,后8位代表主机号。如下1100 0000,1010 1000,0000 0001,0000 0000192.168.0.0/16192.168.0.1 ~ 192.168.255.254前16位为网络前缀,后16位代表主机号。如下1…...

《爆肝整理》保姆级系列教程python接口自动化(二十二)--unittest执行顺序隐藏的坑(详解)
简介 大多数的初学者在使用 unittest 框架时候,不清楚用例的执行顺序到底是怎样的。对测试类里面的类和方法分不清楚,不知道什么时候执行,什么时候不执行。虽然或许通过代码实现了,也是稀里糊涂的一知半解,这样还好&am…...

【第二章 IOC操作bean管理(XML注入其他类型属性(字面量,外部bean,内部bean,级联赋值)、XML注入集合属性)】
第二章 IOC操作bean管理(XML注入其他类型属性(字面量,外部bean,内部bean,级联赋值)、XML注入集合属性) 1.IOC操作bean管理(XML注入其他类型属性) (1…...

Kotlin-枚举和印章
kotlin-枚举 枚举也是一个对象,枚举对象的定义需要使用enum关键字 枚举对象可以定义函数 假设定义一个星期枚举对象。就是一下写法 enum class Week {星期一,星期二,星期三,星期四,星期五,星期六,星期日;//打印星期几fun printWeek(){println("这是星期枚举对…...

_linux (TCP协议通讯流程)
文章目录TCP协议通讯流程TCP 和 UDP 对比TCP协议通讯流程 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器初始化: 调用socket, 创建文件描述符;调用bind, 将当前的文件描述符和ip/port绑定在一起;如果这个端口已经被其他进程占用了, 就会bind失 败;调用listen, 声…...