三万长文50+趣图带你领悟web编程的内功心法:一文带你深入解读HTTP的发展史
看到题目,大家是不是认为根据上一篇(两万字长文50+张趣图带你领悟网络编程的内功心法)一样,其实不然,我们上一边介绍的是网络编程的基本功,有了这些基本功之后,我们就可以在此之上构建更加接近实际应用的web程序了。为了快速展示他们的层次关系,我用几本书叠了起来进行说明,顺便给大家推荐这几本基本算是这些领域比较权威的书籍。如下图,由下往上看:
- TCP/IP构成了网络编程的基础设施;
- Socket套接字编程为应用层提供了访问TCP/IP协议栈的接口;
- 在应用层上面,指定了面向Web编程的HTTP协议;
- Tomcat是实现HTTP协议的一个应用服务器。
在 两万字长文50+张趣图带你领悟网络编程的内功心法 一文中,我们详细介绍了TCP/IP协议,从物理层一直讲到了应用层。在应用层可以利用TCP/IP底层的能力,实现丰富的功能,而本文,我们就重点讲解构建在应用层上的协议:HTTP协议。
首先,我们来思考一个问题,HTTP是怎么来的,为什么要创造HTTP,HTTP接下来会怎么发展呢?这就得聊聊HTTP的发展演变史了。
阅读本文的同时,我们可以看到整个HTTP的发展演变史,可以发现,假设规范是一个小伙子,一般来说,一个优秀的小伙子从来不是出生之后就是优秀的,而是随着业务场景,技术挑战的产生,不断的被拉去劳改,学习,在忧患中蜕变进化。所以现在所谓的标准、成熟的技术,未必符合所有的场景,是技术的挑战与创新促成了新事务的发展。
(前方高能预警:此处有篇高考高分作文...)如果Google只满足于HTTP/1.1,就不会推出SPDY促进HTTP/2的诞生了;如果Google只满足于SPDY,就不会推出QUIC促进HTTP/3的诞生了;如果快手只满足于HTTP/1.1,就不会自己实现一套kQUIC了;如果我只满足于通过HttpClient发起HTTP调用,就不会写这篇文章了。
还记得那个学术风浓厚的OSI网络模型吗,最终是被TCP/IP给盖过了风头。深入应用场景,深入业务,挖掘痛点,探索折腾起来吧。哦对了,最重要的一点:计算机基础知识得打牢固。
1、HTTP发展演变史
话不多少,我直接画了一个图,总结一下HTTP的演变史,一个从诞生之日开始就不断被劳改的小伙子,一路被互联网巨头和互联网标准化组织IETF逐渐带上正轨的心酸历史,它还有很长一段路需要走:
1.1、时代背景
在HTTP协议诞生以前,都有哪些事情的发生,为其做好了铺垫呢?下面来看看。
接下来,我会请出我们的机器人为我们总结每个小节的技术,格式如下:
每个版本的协议都会有很多特性,这个章节会把相关特性或者技术点描述出来,但是在发展演变史这章节不会细讲,我们后面会有专门章节为你揭秘HTTP技术点的详细实现原理。
所以,本章节主要是为您梳理HTTP的发展演变史,知道技术的来龙去脉,以及接下来的发展趋势。
1.1.1、ARPANET
ARPANET(Advanced Research Projects Agency Network)是第一个具有分布式控制的广域分组交换网络,该网络由美国国防部高级研究计划局建立。
为了实现对远程计算机的访问,美国互联网先驱 Bob Taylor 在1966年启动了ARPANET项目,1969年连接了第一台计算机,在1970年实现了网络控制程序。
关于为何要搞这样的技术,据ARPA的总监Charles Herzfeld说:因为当时美国直邮数量有限的大型的研究计算机,而许多应该使用这些计算机的研究人员由于地理空间问题导致不能很好使用起来,这使他们感到很挫败,所以就提出了这个研究计划。当然也有传言是处于军事目的,实现对核力量的控制,改善军事战术和管理决策。但不管怎样,网络世界从此往前迈进了一大步。
1.1.2、TCP/IP
在70年代的时候,基于ARPA网络的发展,研究人员指定了传输控制方案,最终演变成了一个协议,通过该协议可以将多个单独的网络合成一个网络,这个协议也就是TCP/IP。
在1983年UNIX操作系统BSD诞生了,其内核就包含了网络编程套接字的设计和实现,TCP/IP就这么被BSD带起来了,最终逐渐成为了事实的标准。
可以说UNIX套接字联网API就是网络编程的一个源头了,所以大学老师教网络编程的时候大概率会推荐这本书:《UNIX网络编程 卷1:套接字联网API》,当时我们老师也推荐了这本书,虽然当时大概率不会细看,但是迟早会用到的。
1.1.3、OSI参考模型
两万字长文50+张趣图带你领悟网络编程的内功心法 一文我们也提到了,为了制定一个统一的计算机网络体系,国际标准化组织ISO提出了一个试图使各种计算机可以在世界范围内互联成网的标准框架:OSI/RM(Open System Interconnection Reference Model 开放系统互连基本参考模型)。
不过嘛,这个标准是1984年发布的,此时TCP/IP已占据大半江山,逐渐成为了事实的标准,而且OSI更加学术,上一篇文章我们也提到了OSI的一些缺点,导致其不能取代TCP/IP,TCP/IP则是在实践中得到了验证。
1.1.4、World Wide Web
好了,万事具备,我们离HTTP的诞生越来越接近了。
在1989年,英国工程师兼计算机科学家 Timothy Berners-Lee 在1989年发明了World Wide Web万维网,万维网是信息时代发展的核心。他开发了三种基础技术:
- URI:统一资源标识符;
- HTML:超文本标记语言;
- HTTP:超文本传输协议;
是什么促成了 Timothy Berners-Lee 发明万维网呢?在CERN工作的时候,他对查找存储在不同计算机上的信息所带来的低效率和困难感到沮丧。于是,他想CERN管理层提交了一份备忘录:《信息管理:一项提案》。逐渐促成了可以在文档中通过单击鼠标跳转到其他引用的文档的系统的实现,这种呈现形式的文档被称为超文本。
HTTP诞生了。
1.2、HTTP/0.9
1991年,是一个单纯的年代,网上只有文字,看不了图,看不了片。在如此单纯的环境下,Timothy Berners-Lee设计了HTTP协议的0.9版本,为啥不给多个0.1凑个整呢,因为跟我们现在用的HTTP1.1+协议比起来实在是简单多了:
- 基于客户端服务端的请求响应协议;
- 没有首部;
- 没有状态码;
- 只支持纯文本,其设计目标是获取HTML;
- 只支持GET方法;
- 每次请求都建立新的TCP连接;
但是这也是满足那个纯真年代的需求了。保持简单,避免过度设计也好,为后续扩展留了个口子。
1.2.1、报文示例
HTTP/0.9协议下通过浏览器访问:https://www.itzhai.com/hello-world.html
在建立了TCP连接之后,最终浏览器会发送报文如下:
GET /hello-world.html
GET后面为什么是资源路径,而不是URL呢?
因为浏览器首先是拿到了URL对应的IP地址,然后建立TCP连接,一旦连接到服务器,就不需要协议,服务器,和端口号了。第二节我们会抓包演示。
响应也非常简单,仅仅由HTML文件本身组成:
<html>
<body>
welcome to itzhai (www.itzhai.com), wechat account: itread.
</body>
</html>
可以发现:
- 没有HTTP首部,也就意味着只能传输HTML文件;
- 没有状态码:出现问题的时候,只能发送一个特定的HTML文件,其中包含问题的描述,以供人们解读错误信息。
1.3、HTTP/1.0
人们的欲望是那么的没有止境呀,后来又想在网上看小图片,又想听音乐,不单单只是看文字了,还想自己写段子到网上。这么多需求来了,只能升级HTTP协议了。在1996年5月,HTTP工作组发布了RFC 1945[1],在该文档中记录了许多HTTP/1.0实现的通用方法,于是HTTP/1.0就诞生了。
HTTP/1.0跟我现在用的HTTP/1.1比接近了,加了如下概念:
增加了首部:
- Allow
- Authorization
- Content-Encoding
- Content-Length
- Content-Type
- Date
- Expires
- From
增加了16个响应状态码;
引入了重定向;
内容编码(压缩):
content-coding = "x-gzip" | "x-compress" | token
更多的请求方法:GET,HEAD,POST;
传输数据不限于文本;
更详细的内容,参考RFC 1945[1:1]。
但是,HTTP/1.0并不是一个正是规范或Interger标准,只是一个已有实践的参考文档,没有约束力,对当时的互联网来说没有太大的推进作用。
另外,HTTP/1.0也是有很多瑕疵的,比如不能让多个请求共用一个TCP连接,缺少强制的Host首部,并且缓存比较控制比较粗糙。
如何复用TCP连接?
有些浏览器在请求的时候,会使用一个非标准的Connection字段:
Connection: keep-alive
这个表示客户端端要求服务器不要关闭TCP连接,以便其他请求可以复用,服务器同样的回应这个字段。
1.3.1、报文示例
HTTP/1.0协议下通过浏览器访问:https://www.itzhai.com/hello-world2.html
请求报文如下所示:
GET /hello-world2.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
Accept: */*
响应报文如下所示:
200 OK
Date: Tue, 15 Nov 1996 08:00:00 GMT
Server: Apache 0.84
Content-Type: text/html
<html>
<body>
welcome to itzhai (www.itzhai.com), wechat account: itread.<br/>
<img src="https://www.itzhai.com/resources/images/itzhai_qrcode.jpeg" width="100px">
</body>
</html>
由于浏览器拿到响应的HTML之后,解析到里面还有一个img图片请求,于是又发起了第二个连接获取图片:
GET /resources/images/itzhai_qrcode.jpeg HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
响应报文如下所示:
200 OK
Date: Tue, 15 Nov 1996 08:00:01 GMT
Server: Apache 0.84
Content-Type: image/jpeg
(image content)
1.4、HTTP/1.1
将HTTP转变为正式的IETF Internet标准的工作与围绕HTTP/1.0文档编制工作并行进行。
有道是,有竞争才会有进步,微软在1995年推出的Windows系统中发布了IE浏览器,与当时的浏览器霸主Netscape浏览器展开对抗,IE逐步占有了更多的市场,直到1998年Netscape被AOL收购后,IE的市场还在不断攀升,在两大浏览器互相厮杀期间,HTTP/1.1诞生了。
在1997年1月,发布了HTTP/1.1的第一个正式标准 RFC2068[2]。然后,在两年半之后的1999年6月,许多改进和更新被纳入该标准,并以 RFC 2616[3]的形式发布。
HTTP/1.1有如下特性:
新增了POTIONS、PUT、DELETE、TRACE、CONNECT等新方法;
强化了缓存管理和控制;
支持维持持久连接,支持通知服务器弃用连接;也就是说TCP连接默认不关闭,可以被多个请求复用,不用声明
Connection: keep-alive
;请求HTML文件的时候要求携带编码、字符集、cookie元数据等信息;
支持原始HTML请求的分块响应,利于传输大文件;
就这样,一个最稳定版本的HTTP协议诞生了,直到现在,仍然有很多网站在使用HTTP/1.1协议。
在HTTP/1.1期间,涌现了很多互联网企业,如,Google,腾讯,百度,阿里,淘宝,美团头条等。
1.4.1、报文示例
请求报文:
GET /hello-world2.html HTTP/1.1
Host: www.itzhai.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
If-None-Match: "9a3bee90-q1"
If-Modified-Since: Tue, 18 Aug 2020 15:06:33 GMT
响应报文:
HTTP/1.1 200 OK
Date: Tue, 18 Aug 2020 15:30:57 GMT
Last-Modified: Tue, 18 Aug 2020 15:06:33 GMT
Content-Type: text/html
Content-Length: 192
ETag: "5f3bee79-c0"
Accept-Ranges: bytes
<html>
<body>
welcome to itzhai (www.itzhai.com), wechat account: itread.<br/>
<img src="https://www.itzhai.com/resources/images/itzhai_qrcode.jpeg" width="200px">
</body>
</html>
HTTP/1.1具体特性说明我们第二节会讲到。
本文首次发表于: HTTP发表演变史 以及公众号 Java架构杂谈,未经许可,不得转载。
1.5、HTTP/2
当然,HTTP/1.1的问题也是很多的,主要是连接缓慢,服务器只能按顺序响应,如果某个请求花了很长时间,就会出现请求队头阻塞,从而影响其他请求。
这个时期出现了很多各式的前端优化小技巧,当年搞过一段时间前端,也对这些技术略知一二,如:
- 为了增加并发请求,做域名拆分;
- CSS、JS等资源内联到HTML中,或者进行资源合并;
- 生成精灵图,一次性传输所有小图标;
- 资源预取...
最终,为了推进从协议上进行优化,Google跳出来了,推出了SPDY协议。
1.5.1、SPDY
为啥Google敢推出这样的协议呢,主要还是因为在2008年诞生的Chrome浏览器迅速占据了市场,拥有了大部分用户,挟天子以令诸侯,尝试推广新技术是水到渠成的事情。
SPDY是Google开发,用于传输Web内容的协议,SPDY协议减少了网页加载延迟,并且提高了We标的安全性。
SPDY主要通过帧和首部压缩、多路复用和优先级属性降低等待时间。
SPDY诞生之后,很快被整合进Chrome和Firefox,最终被所有主流浏览器所采用,另外服务器和网络代理也对SPDY提供了必要的支持。
SPDY的核心人员后来都参与到了HTTP/2的开发,在2015年2月,Google宣布最终批准HTTP/2标准之后,也就不再继续支持SPDY协议了,并且最终在Google Chrome 51中删除了SPDY的支持。
1.5.2、HTTP/2
HTTP/2又解决了HTTP/1.1面临的大部分问题,主要有如下功能:
- 使用虚拟的流传输消息,解决了HTTP一个连接中应用层的队头阻塞的问题;
- 使用了二进制协议,不再是纯文本,避免文本歧义,缩小了请求体积;
- 实现了多路复用,提高了连接的利用率,在拥塞控制方面有了更好的能力提升;
- 使用HPACK首部压缩方案压缩头部信息,大大节约了带宽;
- 增强了安全性,使用HTTP/2,要求必须至少用TLS1.2;
- 允许服务器主动向客户端推送数据;
1.6、HTTP/3
HTTP/2还在草案的时候,Google又发现新的问题了,那就是由于HTTP/2依赖于TCP,TCP有什么问题,那么HTTP/2就会存在什么问题。最主要的问题还是队头阻塞问题:队头阻塞问题在应用层解决了,但是在TCP协议层并没有解决:
- 上一篇文章我们提到过TCP在丢包的时候会进行重传,前面有一个包没有接收到,就只能把后面的包先放到缓冲区里面,应用层实际上是无法取数据的。也就是说HTTP / 2的多路复用的并行性对于TCP的丢失恢复机制不管用,因此丢失或者重新排序的数据报都会导致所有活动事务陷入停顿。
为了解决以上问题,于是Google发明了gQUIC(Quick UDP Internet Connection)协议。
1.6.1、QUIC
QUIC是最初由Google开发的一种传输层网络协议,在QUIC协议中,传输层用UDP替换掉了TCP,并在用户空间实现了一套拥塞控制算法,从而避免了TCP的队头阻塞问题。
在UDP之上,QUIC实现了连接管理、拥塞窗口、流量控制等。
后来IETF HTTP和QUIC工作组主席Mark Nottingham提出了正式请求,将HTTP-over-QUIC重命名为HTTP/3。
1.6.2、HTTP/3
HTTP / 3使用与HTTP/1.1和HTTP/2相同的语义(相同的操作,例如GET和POST)和相同的响应代码(例如200或404),但是使用QUIC传输协议协议,以及采用类似于HTTP/2的内部成帧层提供HTTP语义的传输。
HTTP/3进展如何?
截止到2020年8月,HTTP/3协议已经成为Internet草案,并且具有多种实现方案,前1000万个网站中有6.7%支持HTTP / 3。在浏览器方面,Firefox和Chrome稳定版本都支持HTTP/3,但是默认情况下是禁用的,Safari 14将默认启用HTTP/3。
想进一步了解,可以阅读最新的发布于2020年8月14日的HTTP/3草案:Hypertext Transfer Protocol Version 3 (HTTP/3)[4]
目前我们使用最广发的还是HTTP/1.1,接下来我就基于HTTP/1.1来介绍下HTTP协议。
接下来,由于篇幅所限,为了给大家呈现更好的阅读体验,我把后续的内容分为以下章节,深入细节更精彩,欢迎大家继续阅读:
这篇文章的内容就介绍到这里,能够阅读到这里的朋友真的是很有耐心,为你点个赞。
本文为arthinking基于相关技术资料和官方文档撰写而成,确保内容的准确性,如果你发现了有何错漏之处,烦请高抬贵手帮忙指正,万分感激。
如果您觉得读完本文有所收获的话,可以关注我的账号,或者点赞吧,码字不易,您的支持就是我写作的最大动力,再次感谢!
为了把相关系列文章收集起来,方便后续查阅,这里我创建了一个Github仓库,把发布的文章按照分类收集起来了,感兴趣的朋友可以Star跟进:
关注我的博客IT宅(itzhai.com)
或者公众号Java架构杂谈
,及时获取最新的文章。我将持续更新后端相关技术,涉及JVM、Java基础、架构设计、网络编程、数据结构、数据库、算法、并发编程、分布式系统等相关内容。
References
- 谢希仁. 计算机网络(第6版). 电子工业出版社.
- TCP/IP详解 卷1:协议(原书第2版). 机械工业出版社.
- UNIX网络编程 卷1:套接字联网API. 人民邮电出版社
- HTTP权威指南. 人民邮电出版社
- HTTP/2基础教程. 人民邮电出版社
- 刘超. 趣谈网络协议. 极客时间
- 罗剑锋. 透视HTTP协议. 即可时间
本文同步发表于我的博客IT宅(itzhai.com)和公众号(Java架构杂谈)
作者:arthinking | 公众号:Java架构杂谈
博客链接:https://www.itzhai.com/articles/comprehend-the-underlying-principles-of-web-programming.html
版权声明: 版权归作者所有,未经许可不得转载,侵权必究!联系作者请加公众号。
Hypertext Transfer Protocol -- HTTP/1.0 RFC 1945. Retrieved from https://datatracker.ietf.org/doc/rfc1945/ ︎ ︎
HypertextTransferProtocol--HTTP/1.1 2068. Retrieved from https://tools.ietf.org/html/rfc2068 ︎
Hypertext Transfer Protocol -- HTTP/1.1 2616. Retrieved from https://tools.ietf.org/html/rfc2616 ︎
Hypertext Transfer Protocol Version 3 (HTTP/3). Retrieved from https://quicwg.org/base-drafts/draft-ietf-quic-http.html ︎
三万长文50+趣图带你领悟web编程的内功心法:一文带你深入解读HTTP的发展史的更多相关文章
- 年近30的Java程序员为了达到月入三万的目标,都做了哪些准备?
1.我觉得像我这般年纪的(29岁),有相对扎实技术功底的(就不自谦了),对赚钱有着强烈欲望的程序员,应该定一个切实的小目标——五年内月入三万! 之所以要定这个目标,最主要的原因是老婆的批评刺痛了我—— ...
- Tomcat架构解析(三)-----Engine、host、context解析以及web应用加载
上一篇博文介绍了Server的创建,在Server创建完之后,就进入到Engine的创建过程,如下: 一.Engine的创建 1.创建Engine实例 当前次栈顶元素为Service对象,通过Se ...
- Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程
Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程 缓冲区(Buffer) 用于存储数据 通道(Channel) 用于传输数据 多路复用器(Selector) 用于轮询 Channel 状 ...
- Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用
前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...
- Java程序员月薪三万,需要技术达到什么水平?
最近跟朋友在一起聚会的时候,提了一个问题,说 Java 程序员如何能月薪达到二万,技术水平需要达到什么程度?人回答说这只能是大企业或者互联网企业工程师才能拿到.也许是的,小公司或者非互联网企业拿二万的 ...
- 猿灯塔:Java程序员月薪三万,需要技术达到什么水平?
最近跟朋友在一起聚会的时候,提了一个问题,说Java程序员如何能月薪达到二万,技术水平需要达到什么程度?人回答说这只能是大企业或者互联网企业工程师才能拿到.也许是的,小公司或者非互联网企业拿二万的不太 ...
- HTML5 学习笔记(三)——本地存储(LocalStorage、SessionStorage、Web SQL Database)
一.HTML4客户端存储 B/S架构的应用大量的信息存储在服务器端,客户端通过请求响应的方式从服务器获得数据,这样集中存储也会给服务器带来相应的压力,有些数据可以直接存储在客户端,传统的Web技术中会 ...
- 趣谈编程史第3期-大器晚成的新晋流量Python发展史
写在前面 这篇博文主要介绍javaScript的发展史,根据作者在B站发布的同名视频的文案整理修改而成,对视频感兴趣的博友可访问https://www.bilibili.com/video/av860 ...
- 成本节省 50%,10 人团队使用函数计算开发 wolai 在线文档应用
作者: 马锐拉 我们的日常工作场景几乎离不开"云文档".目前,人们对于文档的需求再不仅仅是简单的记录,而扩展到办公协同.信息组织.知识分享等.在国内众多在线文档中,wolai 因为 ...
随机推荐
- Docker安装Mycat并实现mysql读写分离,分库分表
Docker安装Mycat并实现mysql读写分离,分库分表 一.拉取mycat镜像 二.准备挂载的配置文件 2.1 创建文件夹并添加配置文件 2.1.1 server.xml 2.1.2 serve ...
- K8s (常用命令)
查看集群信息: [root@kubernetes-master pods]# kubectl cluster-infoKubernetes master is running at http://lo ...
- spark SQL (五)数据源 Data Source----json hive jdbc等数据的的读取与加载
1,JSON数据集 Spark SQL可以自动推断JSON数据集的模式,并将其作为一个Dataset[Row].这个转换可以SparkSession.read.json()在一个Dataset[Str ...
- PHP-表单提交(form)
PHP-表单提交 一 form表单 GET 将表单内容附加到URL地址后面,提交的信息长度有限制,不可以超过8192个字节,同时不具有保密性,而且只能传送ASCII字符(一般传送的不保密性数据 ...
- C#委托的进一步学习
一.委托的说明 namespace LearningCsharp { class Program { //定义一个委托,使用delegate加上方法签名 //将委托理解为存储方法的"数组&q ...
- Pytest(17)运行未提交的git(pytest-picked)
前言 我们每天写完自动化用例后都会提交到 git 仓库,随着用例的增多,为了保证仓库代码的干净,当有用例新增的时候,我们希望只运行新增的未提交 git 仓库的用例.pytest-picked 插件可以 ...
- springboot中扩展ModelAndView实现net mvc的ActionResult效果
最近在写spring boot项目,写起来感觉有点繁琐,为了简化spring boot中的Controller开发,对ModelAndView进行简单的扩展,实现net mvc中ActionResul ...
- CF-1354 E. Graph Coloring(二分图,背包,背包方案输出)
E. Graph Coloring 链接 n个点m条边的无向图,不保证联通,给每个点标号1,2,3.1号点个数n1,2号点个数n2,3号点个数n3.且每条边的两点,标号之差绝对值为1.如果有合法方案, ...
- CCF计算机软件能力认证试题练习:201912-5 魔数
CCF计算机软件能力认证试题练习:201912-5 魔数 前置知识:BFS,线段树等 \(f(x) = (x\%A)\%B\) 这个函数值的和直接用线段树维护是不太行的(也可能是我不知道),后来想了很 ...
- BZOJ2882 工艺【SAM】 最小循环串
BZOJ2882 工艺 给出一个串,要求其循环同构串中字典序最小的那个 串翻倍建\(SAM\)然后从起点开始贪心的跑\(n\)次即可 当然也能用最小表示法来做 #include<bits/std ...