比如我们使用浏览器访问淘宝时,浏览器就会发送一个遵循 HTTP 协议的请求报文到淘宝服务器,告诉淘宝服务器自己想要获取淘宝首页信息。淘宝服务器收到此报文后,则会发送一个同样遵循 HTTP 协议的响应报文到浏览器,此响应报文中包含淘宝首页的内容。浏览器收到响应报文后解析其内容并展示在界面上。

面试中常见的一个问题: GET 和 POST 的区别是什么?,在这里做一下解答。
| 请求头 | 含义 |
| Host | 接收请求的域名 |
| User-Agent | 客户端软件的名称和版本号等相关信息 |
| Connection | 设置发送响应之后 TCP 连接是否继续保持的通信选项 |
| Cache-Control | 控制缓存的相关信息 |
| Referer | 记录请求的来源(当通过点击超级链接进入下一个页面时,会记录上一个页面的 URI) |
| Accept | 客户端可支持的数据类型, 以 MIME 类型来表示 |
| Accept-Encoding | 客户端可支持的编码格式 |
| Accept-Language | 客户端可支持的语言 |
| If-Modified-Since | 用于判断资源的缓存是否有效(客户端通知服务器,本地缓存的最后变更时间) |
| If-None-Match | 用于判断资源的缓存是否有效 |
| Range | 用于断点续传,指定第一个字节的位置和最后一个字节的位置。 |
| Cookie | 表示请求者的身份,用于保存状态信息 |
请求空行用于表明请求头已经结束。
请求体用于传送客户端要发给服务器的数据,比如请求参数,通常出现在 POST 请求方法中,而 GET 方法无请求体,它的请求参数直接会显示在网址上面。请求行和请求头的数据都是文本形式且格式化的,而请求体不同,其可以包含任意的二进制数据,比如文本、图片、视频等等。

响应行用于说明对请求的处理情况,包含三部分内容,中间用空格分割。
面试中常见的一个问题: HTTP 有哪些常见状态码?,在这里做一下解答。
响应头用于向客户端传递一些额外的重要信息,比如响应内容的长度等。响应头由字段名和字段值构成,二者之间用冒号进行分隔。常见的一些响应头有:
| 响应头 | 含义 |
| Date | 日期时间信息,表示服务器产生并发送响应报文的日期和时间。 |
| Server | 表示HTTP服务器应用程序的信息,类似于请求报文中的 User-Agent |
| Location | 此字段会配合重定向使用,用于提供重定向后新的 URI。 |
| Connection | 设置发送响应之后 TCP 连接是否继续保持的通信选项 |
| Cache-Control | 控制缓存的相关信息 |
| Content-Type | 服务器返回的响应类型 |
| Content-length | 服务器返回的响应长度 |
| Content-Encoding | 服务器返回的响应编码 |
| Content-Language | 服务器返回的响应语言 |
| Last-Modified | 指定响应内容最后的修改时间 |
| Expires | 表示资源失效的时间,浏览器会在指定过期时间内使用本地缓存 |
| Etag | 用于协商缓存,返回一个摘要值 |
| Accept-Ranges | 用于断点续传,指定服务器所支持的内容范围 |
| Set-Cookie | 设置状态信息 |
响应空行用于表明响应头已经结束。
响应体用于传送服务器要发给浏览器的正文。同请求报文的请求体一样,响应体可包含任意的二进制数据。浏览器收到响应报文后,则会将正文加载到内存,然后解析渲染,最后显示页面内容。
非持续连接的缺点:
又比如在后端开发中,由于数据库一般存储在硬盘上,读取速度较慢,于是可能会采用 Redis 等内存数据库作为缓存,先去 Redis 中取数据,如果取不到,再去数据库中取。再比如在操作系统中,由于页表进行地址转换的速度较慢,于是有了 TLB 快表,当需要进行逻辑地址到物理地址的转换时,先去查询速度更快的 TLB 快表,如果查不到,再去查询页表,此时 TLB 快表就是一种缓存。缓存的主要目的在于提升查询速度,一般逻辑如图所示。
同样,在 HTTP 设计中也有缓存的概念,主要是为了加快响应速度,HTTP 缓存的实现依赖于请求报文和响应报文中的一些字段,分为强缓存和协商缓存。
由于服务器端时间和客户端时间可能不同步,存在偏差,这也就是导致了使用 Expires 可能会存在时间误差,因此一般更推荐使用 Cache-Control 来实现强缓存。以 Cache-Control 为例,强缓存的具体的实现流程如下:
协商缓存可以基于以下两种方式来实现:第一种(HTTP/1.0 规范):请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段:

Cookie 中可以包含任意信息,最常见的是包含一个服务器为了进行跟踪而产生的独特的识别码。举个栗子:张三在发出第一次请求后,服务器将其状态信息记录下来,比如他的名字、年龄、地址、购物历史等,并通过响应头 Set-Cookie字段,给予其一个 id=12345 的独特识别码作为 Cookie,那么其再次向服务器发出请求时,浏览器会自动在请求报文中的 Cookie 字段中带上 id=12345,服务器就可以通过这个查询到张三的具体信息,从而实现了保持状态的功能。
Cookie 属性:
Web 服务软件一般都会提供 keepalive_timeout 参数,用来指定 HTTP 持久连接的超时时间。比如设置了 HTTP 持久连接的超时时间是 60 秒,Web 服务软件就会启动一个定时器,如果完成某个 HTTP 请求后,在 60 秒内都没有再发起新的请求,就会触发回调函数来释放该连接。② 管道机制持久连接虽然可以多个请求复用同一个连接,但是每次都需要等到上一个请求响应完成后,才能发送下一个请求。管道机制中,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,即相当于同时发出多个请求,因而可以减少整体的响应时间。
虽然客户端可以同时发出多个 HTTP 请求,不用⼀个个等待响应,但是服务器必须按照接收请求的顺序依次发送对这些管道化请求的响应,以保证客户端能够区分出每次请求的响应内容。这存在下面问题:如果服务端在处理一个请求时耗时比较长,那么后续请求的处理都会被阻塞住,会导致客户端迟迟收不到数据,这称为「队头堵塞」。实际上,虽然管道机制的想法很好,但实现却非常困难,因而很多浏览器根本不支持它。一般为了提升性能,采用并行多个 TCP 连接的形式来实现请求的同时发送。
③ 缓存控制前文已经提到过,HTTP/1.1 在 HTTP/1.0 基础之上,增加了一些请求响应头,以更好的实现对缓存的控制。比如
② 二进制帧HTTP/1.1 的报文为纯文本格式,而 HTTP/2 的报文全面采用二进制格式,并将原始的报文拆分为头信息帧(Headers Frame)和数据帧(Data Frame)。采用二进制格式有利于提升数据传输效率。③ 多路复用在 HTTP/2 中定义了流(Stream)的概念,它是二进制帧的双向传输序列,一个数据流对应着一个完整的请求-响应过程,在同一个请求响应过程中,往返的帧会分配一个唯一的流编号(Stream ID)。在流的支持下,HTTP/2 可以在一个 TCP 连接中传输多个请求或响应,而不用按照顺序一一对应(即实现多路复用),因为它们属于不同的流,所发送的帧头部都会携带 Stream ID,可以通过此 Stream ID 有效区分不同的请求-响应。
因而 HTTP/2 解决了 HTTP/1.1 的『队头阻塞』问题,多个请求 - 响应之间没有了顺序关系,不需要排队等待,降低了延迟,大幅度提高了连接的利用率。举个栗子:在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分,接着回应 B 请求,完成后,再发送 A 请求剩下的部分。④ 服务端推送在 HTTP/1.1 中,只能客户端发起请求,服务器对请求进行响应。而在 HTTP/2 中,服务端可以主动给客户端推送必要的资源,以减少请求延迟时间。比如当客户端向服务器请求一个 HTML 文件后,服务器除了将此 HTML 文件响应给客户端外,还可以提前主动将此 HTML 中所依赖的 JS 和 CSS 文件推送给客户端,这样客户端在解析 HTML 时,无需耗费额外的请求去得到相应的 JS 和 CSS 文件。
② 优化重传机制TCP 采用序号+确认号+超时重传机制来保证消息的可靠性,即如果某条消息超过一定时间还没有得到确认,则重新发送此消息。由于网络拥堵情况不断变化,因而消息的超时时间并不是固定的,而是通过不断采样消息的往返时间不断调整的,但 TCP 超时采样存在不准确的问题。举个栗子:客户端发送一个序号为 N 的包,然后超时了(可能丢了,也可能网络堵塞了),于是重新发送一个序号为 N 的包,之后服务器收到后返回一个确认号 ACK 为 N+1 的包。但此时客户端并无法判断这个确定包是对原始报文的确认还是重传报文的确认,那么此时往返时间应该如何计算呢?
QUIC 是如何解决此问题呢?其定义了一个递增的序列号(不再叫 Seq,而是 Packet Number),每个序列号的包只发送一次,即使重传相同的包,其序列号也不一样。举个栗子:客户端发送一个序号为 N 的包,然后超时了,于是重新发送一个相同的包,但序号不再是 N,而是 N+1;那么如果返回的确认包 ACK 为 N+1,就是对原始报文的响应,如果 ACK 为 N+2,就是对重传报文的响应,因而采样时间计算相对更加准确!
那此时怎么知道包 N 和包 N+1 是同一个包呢?QUIC 定义了一个 Offset 概念。发送的数据有个偏移量 Offset,可以通过 Offset 知道数据目前发送到了哪里,因而如果某个 Offset 的包没有收到确认,就重发。③ 连接迁移众所周知,一条 TCP 连接是由四元组标识的,分别是源 IP、源端口、目的 IP、目的端口。一旦其中一个元素发生了变化,就需要断开重连。当手机信号不稳定或者在 WIFI 与移动网络切换时,都将会导致重连,而重连就意味着需要重新进行三次握手,将产生一定的时延,用户感到卡顿,体验不友好。而 QUIC 不采用四元组的方式标识连接,而是以一个 64 位的随机数作为 ID 来标识,通过此连接 ID 标记通信的两端,之后即使网络发生变化,IP 或端口变了,但只要 ID 不变,则无需重连,只需要复用原先连接即可,时延低,减少了用户的卡顿感,实现连接迁移。
本文转载自微信公众号「 一枫说码」,作者「一枫说码」,可以通过以下二维码关注。
转载本文请联系「 一枫说码」公众号。 是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba
我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara
我试图在我的网站上实现使用Facebook登录功能,但在尝试从Facebook取回访问token时遇到障碍。这是我的代码:ifparams[:error_reason]=="user_denied"thenflash[:error]="TologinwithFacebook,youmustclick'Allow'toletthesiteaccessyourinformation"redirect_to:loginelsifparams[:code]thentoken_uri=URI.parse("https://graph.facebook.com/oauth/access_token
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
我正在尝试解析网页,但有时会收到404错误。这是我用来获取网页的代码:result=Net::HTTP::getURI.parse(URI.escape(url))如何测试result是否为404错误代码? 最佳答案 像这样重写你的代码:uri=URI.parse(url)result=Net::HTTP.start(uri.host,uri.port){|http|http.get(uri.path)}putsresult.codeputsresult.body这将打印状态码和正文。
我正在安装gitlabhq,并且在Gemfile中有对某些资源的“git://...”的引用。但是,我在公司防火墙后面,所以我必须使用http://。我可以手动编辑Gemfile,但我想知道是否有另一种方法告诉bundler使用http://作为git存储库? 最佳答案 您可以通过运行gitconfig--globalurl."https://".insteadOfgit://或通过将以下内容添加到~/.gitconfig:[url"https://"]insteadOf=git://