Nginx 与后端服务器socket 连接层 理解 HTTP 和 WebSocket 协议的异同

补充:

  1. Nginx 所谓的反向代理,实际上只是与后端服务器建立了 Socket  B 连接,生成了一个 Socket 对象,Socket 就是一个对象,这个对象可以与操作系统进行通信,操作系统负责发送数据;
  2. 针对 HTTP 协议的请求,Nginx 也会与浏览器建立一个 Socket A 连接,建立链接之后,接收浏览器发送过来的请求,然后将浏览器的发过来的消息,通过 Scoket B 发送给后端服务器,这个过程有且只有一次,发完后,就只在 Socket B 的 onMessage 函数中等待后端传过来的数据了,一旦后端服务器将结束符\r\n\r\n发过来,则 Nginx 就把这些请求 Close 掉。所以 Http 协议不能支持双向通信。
  3. 针对 Websocket 协议,Socket A 和 B 都会长时间保持住,同时 onMessage 在没收到 close 报文时都不会主动发起关闭,这样就能让 Websocket协议下进行全双工通信了。
  4. 至于异步框架是否和 Websocket 有必然关系?可以推测应该不是必然关系,假如 Nginx 和 Django,Django 是同步框架,只要 Django 能按照 Websocket 协议进行发送返回报文,都可以支持双向交互

Http/Websocket 都称为一种协议,能用现实中的例子来解释协议吗?

AI 举例:

您(客户端): 您坐在餐厅桌子上,想点一份菜单。

服务员(服务器): 服务员站在您旁边,准备接受您的点餐请求。

点餐(HTTP 请求): 您向服务员递交一个菜单,上面列出了您想点的菜品。(菜单和你的点单就是协议的内容)

等待(等待响应): 服务员拿着菜单去厨房,等待厨师准备好食物。

上菜(HTTP 响应): 厨师准备好了您点的菜,服务员将食物端到您的桌子上。

上面这个例子目的就是你和餐厅之间的沟通,这个沟通要在餐厅的菜单内,同理浏览器要和服务器交互,有成千上万个浏览器,不能你说要炒豆角别人就给你做炒豆角,炒出来,你说不是你要的炒豆角,这个事情怎么说的清?所以,炒豆角,炒成几成熟,带不带肉,辣不辣,麻不麻,都在菜单上进行勾选,如果对方根本就没有豆角,你就不能点炒豆角,这份菜单就是你和餐厅之间的协议。

Http 有哪些具体的协议呢?

请求(Request): 客户端向服务器发送请求,请求特定资源或执行特定操作。

  • 请求行:包括 HTTP 方法(GET、POST、等)、请求的 URL 和协议版本。
  • 请求头:包含关于请求的附加信息,如用户代理、主机名等。
  • 请求正文:在 POST 请求中,可能会包含提交的数据。

响应(Response): 服务器向客户端返回响应,包含请求的结果或错误信息。

  • 状态行:包括协议版本、状态码和状态消息。
  • 响应头:包含关于响应的附加信息,如服务器类型、内容类型等。
  • 响应正文:包含服务器返回的数据。

HTTP 方法(HTTP Methods): 定义客户端请求的操作类型,常见的有 GET、POST、PUT、DELETE 等。

状态码(Status Codes): 服务器返回的三位数字代码,表示请求的处理结果。例如,200 表示成功,404 表示资源未找到,500 表示服务器内部错误等。

请求头(Request Headers)和响应头(Response Headers): 包含各种元数据,用于提供关于请求或响应的额外信息。

URL(Uniform Resource Locator): 指定 Web 上资源的地址。URL 包括协议、域名、端口、路径等。

Cookie 和 Session: 用于在客户端和服务器之间跟踪用户会话状态。

缓存控制(Caching): 控制客户端和服务器之间的数据缓存,以提高性能。

内容协商(Content Negotiation): 客户端和服务器协商最适合的内容格式,如请求 JSON 或 XML。

安全性(Security): HTTP 可以通过 HTTPS(基于 SSL/TLS 加密)来实现安全传输。

重定向(Redirection): 服务器可能会将请求重定向到其他 URL,常见的是 301 永久重定向和 302 临时重定向。

认证(Authentication): HTTP 可以通过 Basic Auth、Bearer Token 等方式进行用户认证。

跨域资源共享(CORS): 用于在不同域之间进行数据交换,涉及安全机制。

一张 Ajax 请求的截图

我们能看到请求时可以携带很多协议key,每个协议 key 都有对应的值,这里挑 Content-Type:application/json来看,意味着浏览器传过去的是一个 json 字符串,服务端拿到后,以 json格式进行处理,同理,我还可以传递 urlencode 形式,纯 text/plain文本形式,甚至是二进制流,这里解释下什么叫流?

通常我们看到的水流,主要觉得它很柔软,无孔不入,并且是连续的,但如果你拿电子显微镜来看,它就是一粒粒的水分子组成的,我们放大一下,把它们当成沙粒,你拉远镜头,那么流沙跟水流是一样的,这就是流体,由不连续的粒子组成。

二进制流也是这意思,每次传输一个比特,但是连续很快地发出去,连起来看,就像是电流在运行,这就是二进制流。

说这么多,就是告诉大家,流就是比特,由一个发射器往外发射,因为发射的非常快,就看起来像是连续的电流。

底层所有的流都是这样处理,只是颗粒度不一样,例如字符流,就是一次处理一个字符,一个字符又有多个比特构成。

自定义一些协议

如果你理解了协议就是由两端(人或者物)约定的一些暗号,那么这些暗号,也可以给服务器端约定一下,比如,在请求中增加一个 Token 键,这个键要携带一个值,用来鉴权,只要服务端允许接收这个 Token 那么浏览器端就能将这个 Token 传递过来。

为什么说服务端允许呢?像你看的电视剧,当一个人进入城门时,都得先进行验证身份的,如果你不在名单上,例如胡人,那是不让你进的,所以必须有服务端的允许,Token 才能进去。所以浏览器如果要发一个特殊的协议头,那是要请示一下服务端的,这个请示就叫 OPTION,很多人都知道 POST/GET,但知道 OPTION并不多。讲这个点在于,我想启发大家,编程来源现实,如果经历比较多,可以靠回想现实来推测编程中的技术,现实存在的问题,编程也会遇到,所以这样你的学习就变成主动的了。

扯了那么多都是要说明 Http 是一堆协议暗号的统称,这么多的暗号(我们称为标识吧)和暗号的值聚合在一起发给服务端,服务端收到后称这次发送为一次请求,就跟后厨的师傅又收到一份点餐一样。

为何还没讲到连接?

我们需要放大这个例子,你才能理解什么叫连接,也能知道一个连接是没有什么长短之分,也没有什么链路存在的,这将打破你常规的认识,实际上这才是计算机真正的运行方式。

连接就是一次有去有回的寄送快递的过程,寄送的快递员不是同一个人。

先把三次握手抛开,直接到握手完毕,完毕之后呢?看图:

我们在浏览器端建立了一个 XMLHttpRequest 对象,在服务端也会建立一个类似XMLHttpRequest对象,这个对象里面记录了什么?

Swoole 文档  这个文档里getclientinfo 返回了一个对象,这个对象正式服务端XMLHttpRequest存储的一些信息,就是这些信息,记录着客户端的 IP 和客户端的端口; 

有点懵?为何服务端还记录着客户端的端口号?端口号不是服务端才有的?有时候程序员看不到程序的全貌,因为程序是虚拟的,除非有人告诉你,或者你自己发现,否则它们对你而言就不存在。

回到寄送快递的概念:

你在一栋楼里,你要寄东西给别人,收货员就会同时记录你的门牌号,否则服务端回传给你的信件,你就收不到了(普通我们寄快递是单向的),所以你寄给服务端的包裹里就要有你的门牌地址和你的所住的大楼,这个客户端对象有一种能力,就是可以把给到它的 data发送给它的客户端。它同时还把自己注册到操作系统上,操作系统会分配一个端口号给它,等到这个端口收到消息后(服务端发过来的),操作系统就会把这些信息,转发给它的 onsucess 函数,形成了一次消息的请求与回调。

讲到这里,你觉得连接还有长短之分吗?你觉得连接还是连续的概念吗?

客户端的XMLHttpRequest

客户端的XMLHttpRequest 同样也记录着服务端的信息,在没有出现任何服务端要关闭,或者网络中断(这玩意是服务器检测并告知给XMLHttpRequest的)XMLHttpRequest都不会消失,一直留在内存里。

这就到了你编程的环节了,你new 了一个XMLHttpRequest,当 XMLHttpRequest 三次握手之后,确定远程服务端正常存在且已经安排了人来帮你处理事情了,你就可以放开心的往服务端发消息了。

但是快递站分配给你的快递小哥只有一次,当你用XMLHttpRequest发完一个包裹之后,同时另外一个快递小哥把服务端包裹拿给你后,他就给你拜拜了,下次你再想发信息,你得再跟快递站下单,让小哥过来取包裹。

这就是远程服务端对待 Http 请求反应,只要你是 Http 请求,只要返回给你数据了,之后就跟你就拜拜了,下次有事再说,这特别像你去政务大厅办事,一次只办一次事,办完 over。

早期的 HTTP1.0

没有快递站的概念,为啥?用的人少呗,你想寄东西,就自己去建个快递站,然后再把东西交给快递小哥送过去,拿过来。

随着业务量的提高,你觉得不能忍,每次都要你去造一个快递站这种事,想想来就来气,于是你揪结了一群请求,要求建个快递站

HTTP1.1

仅仅要建立个快递站的话,是不需要更改 HTTP1.0版本的,因为 HTTP 只是一个协议,为了要把协议拉下水,你不改协议谁给你劳师动众重新造快递站?你凑合着用吧,这其实也是现实世界的一种映射,既然要整修那就得有个名头,这个名头就是 HTTP1.0 有些协议不能满足我的需求了,因为我要建快递站,所以我需要新的寄送方式。看似无关的两个东西整合在一起了,于是借着改 HTTP 协议的名头,把传输层给改了。

改成了有圆通、中通、国通各种通的快递站,每次你要发包裹,都找个排队人少的要求给你分配一个快递员来取包裹,发包裹,把服务端返回来的包裹拿给你。

这个就是 HTTP1.1中有名的 tcp/ip 复用技术,先把快递站建了,然后发包裹这件事交给比较闲的那个站,这就是连接池的概念。

但是新的问题又来了

我的请求还是太多了,我要一下子发送 100 个包裹,业务量又大了,一个快递小哥必须一次性把 100 个包裹都发给服务端,然后还得把这 100 个包裹反馈结果都按顺序带回来,一旦搞乱了,我就分不清楚哪个对哪个了。

此时,你等了好久好久,快递小哥才把 100 个包裹的返回信息给你拿来了回来,偶尔还会有丢失,你还得重发,这已经让你忍不了了,你要求服务端继续升级,不能按照这种方式来传递,要换种方式。

HTTP2.0

我想怎么发就怎么发,想什么时候发就怎么发,想让谁发就让谁发,每个包裹上快递小哥都会帮我打上标记,返回来的时候也会带着这个标记,这样一出一进,你就能对上号了。

所以你就能各种发了。当然不能单干这个事情,还得拉着协议变更一下搞事情,于是 HTTP 协议又升级了。

以上就是 HTTP 的发家史。

Websocket 协议的由来

HTTP 一直都是发一次,返回一次,下次发货还的下单,每次下单都得填单,真恶心,能不能只让我填一次,之后让我和服务端直接调派小哥来传递我们的信件呢?

当然是可以的,解决方式是服务端提出来了,你先用 Http 请求给我发一条信息,然后在里面追加一个 Upgrade 升级标签,我收到后就安排专属小哥来给你送信件,只有你就不用填单了。

你一听觉得很棒,然后就跟服务端说,我们如果不按Http 方式沟通,那怎么能告知一些消息给你呢,比如我想跟你说拜拜,或者说有些包我没收全,你还得重复,再或者我说你给我发的包太大了等等。

于是服务端说,那就再造一个协议专门解决你说的这些问题吧,我们称他为 Websocket 如何?

你高兴地说:“太棒了,于是 Websocket 协议诞生了!”

以前参考的文章

http的长连接和短连接(史上最通俗!) - 简书1.以前的误解 很久之前就听说过长连接的说法,而且还知道HTTP1.0协议不支持长连接,从HTTP1.1协议以后,连接默认都是长连接。但终究觉得对于长连接一直懵懵懂懂的,有种...https://www.jianshu.com/p/3fc3646fad80

猜你喜欢

转载自blog.csdn.net/wangsenling/article/details/132480096