记一次网络io学习流水账
一次网络io学习流水账
buffer & NIO & BIO
最近在学习redis线程io模型时,遇到内核缓冲区的概念,然后我由这个名称联想到学习计网时也出现过的读写Buffer,资料一查,这两个就是同一个东西。
这个buffer是处于网络的应用层和运输层之间,当应用程序调用操作系统的 write() 函数时,数据就会先被存放在这个Send buffer中,接下来TCP就会不时从发送缓存里取出一块数据,并将数据传递到网络层。同理,当应用程序调用操作系统的 read() 函数时,就是从缓存里读数据。
我们知道,redis服务器是非阻塞读,也就是如果receive buffer中,如果没有数据,会直接返回,而不会被阻塞。非阻塞模式还是阻塞模式是由套接字(Socket)的属性决定的,而这个属性是由应用程序在创建或配置套接字时主动设置的。
然后又让我联系到NIO和BIO。NIO就是非阻塞读,BIO就是阻塞读。在我的知识库里还浅显的知道netty是Java NIO框架。
-
一个网络连接在程序中体现为一个 Socket 对象。
-
在BIO模式下:一个线程在执行这些IO操作时,如果条件不满足(如无数据可读),线程会被挂起,这个线程也就被这个Socket独占,无法去处理其他Socket。因此是 “一个线程一个连接”。
-
在NIO模式下:通过Selector(选择器),一个线程可以轮询无数个Socket,只去处理那些已经“就绪”(有数据可读、可以写入数据等)的Socket上的IO操作。因为操作不会阻塞,线程可以快速地在所有就绪的连接之间切换。因此是 “一个线程多个连接”。
接着,我又提出疑惑,用户在浏览器发送HTTP请求到服务端与TCP的关系?
答案是:HTTP协议是建立在TCP协议之上的。浏览器发送HTTP请求之前,必须先在网络上建立一条可靠的TCP连接作为“通道”。
当TCP三次握手成功建立好连接后,浏览器通过调用操作系统的 write() 或 send() 函数,将这个HTTP报文写入到本地的TCP Send Buffer(发送缓冲区) 中。操作系统的TCP/IP协议栈会负责将Send Buffer里的数据(即你的HTTP请求)分割成多个TCP段(Segment),通过之前建立的TCP连接这个“通道”, 可靠地发送给服务器。服务器的Web服务器程序(如Nginx、Tomcat)调用 read() 从Receive Buffer中读取到的就是一整个完整的HTTP请求报文。
当Receive Buffer中有数据时,我们继续分析Java服务器(如Tomcat的NIO连接器、Netty)是怎么NIO的:
HTTP/1.0 (短连接模式) vs HTTP/1.1 Keep-Alive (持久连接) vs WebSocket
- 没有Keep-Alive时: 一个TCP连接 只用于传输 一对 HTTP请求/响应。
- 有Keep-Alive时: 一个TCP连接 可以用于传输 多对 HTTP请求/响应。只有当这条连接空闲时间超过超时时间,才会被关闭。
HTTP/1.1 Keep-Alive (持久连接) vs WebSocket
HTTP/1.1 Keep-Alive 和 WebSocket 都复用了底层的TCP连接,但是二者区别如下:
特性 | HTTP/1.1 Keep-Alive | WebSocket |
---|---|---|
通信模式 | 半双工(Half-Duplex):请求-响应循环。必须客户端先发起,服务器才能响应。严格的一问一答。 | 全双工(Full-Duplex):客户端和服务器完全平等,任何一方都可以在任何时候主动向对方发送数据。 |
服务器主动性 | 完全被动:服务器永远不能主动推送消息给客户端。客户端必须不断“轮询”询问。 | 完全主动:服务器可以随时主动向客户端推送消息。 |
协议开销 | 大:每个HTTP请求和响应都必须携带完整的、冗长的头部(如cookie、user-agent等),即使大部分是重复的。 | 极小:只在初始握手时有HTTP头。之后的数据传输使用轻量级的数据帧,头部长仅2-10字节。 |
设计目的 | 优化传统Web浏览的体验:减少重复建立TCP连接的开销,让一个页面中的多个资源(图片、CSS、JS)能更快加载。 | 实现实时双向Web应用:为在线聊天、实时游戏、股票行情、协同编辑等场景提供底层支持。 |
一个网页聊天室的实现可以很好的看出这两者的区别:
- 使用 HTTP/1.1 Keep-Alive:
- 你的浏览器(客户端)必须每隔一秒就向服务器发送一个请求:“有新消息吗?”(这叫轮询 Polling)。
- 服务器回答:“没有。” 或 “有,这里是消息。”
- 这个过程会产生大量无用的请求(问“有新消息吗?”),并且消息到达有延迟(最多要等1秒)。效率极低。
- 使用 WebSocket:
- 建立WebSocket连接。
- 然后,双方就保持沉默,直到有人说话。
- 你发送一条消息,服务器瞬间收到。
cket连接。 - 然后,双方就保持沉默,直到有人说话。
- 你发送一条消息,服务器瞬间收到。
- 另一个用户发送了一条消息,服务器立刻、主动地将这条消息推送到你的浏览器上,完全不需要你询问。