TCPStream 如何知道消息何时完成?
How does TCPStream know when a message is complete?
根据我的理解,TCPStream
不知道何时从客户端到达完整的消息,而是信息以字节流的形式到达。
但是,当我在 Rust 中执行标准的“Hello World”TCPStream
示例时,我正在从流中读取完整的 HTTP 消息。当我发送两条或更多条消息时,它们会相应地分开。
这怎么可能?
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("{}", String::from_utf8_lossy(&buffer[..]));
}
当我减小缓冲区大小时,HTTP 消息被截断并且新消息从头开始。我会以某种方式假设我必须自己管理结束和开始新的 HTTP 消息?
观察到效果的原因在于一般 reader 行为。
TcpStream
上的 read
方法由 Read
特性提供。引用此方法的 documentation:
Pull some bytes from this source into the specified buffer, returning how many bytes were read.
注意“一些字节”位:read
方法读取直到 或者 缓冲区被填充到最后 或者 正在读取的数据已耗尽。对于 TcpStream
,这种耗尽可能发生在两种情况下:
- 请求已发送完毕,对方不再提供任何数据;
- 或有些网络延迟,只有部分的请求已经在这里。
在您的测试中,您似乎总是遇到第一种情况:当您调用 read
时请求已经完全传输并且它适合缓冲区,因此可以读取到最后。但是,当缓冲区太小时,您将无法立即获取整个消息 - 因此您必须在同一流 上再次调用 read
,以获取其余的它。
在实际代码中,您应该在阅读请求时对其进行解析,以确定您遇到了哪种情况:请求是否已完全到达,还是必须再次阅读。
在非常低的级别上,客户端正在使用基本的 Unix write(socket, buf, nbytes)
operation to put the bytes into the socket, and the server is using nread =
read(socket, buf, maxbytes)
从套接字中提取字节。
当 read()
和 write()
与套接字一起使用时,它们不保证将 write()
拆分为多个 read()
或将多个 write()
合并为一个大 read()
。任何事情都可能发生,只要客户端写入的字节最终会按照客户端写入它们的相同顺序被服务器读取(假设网络连接由于某种原因没有中断)。
在您的特定情况下,客户端可能发出了两个 write()
调用,它们在 TCP 层中被转换为两个相应的数据包。服务器进程正在等待 read()
调用。服务器 OS 设法唤醒服务器进程,并在第二个数据包到达或以其他方式通过服务器 OS 之前将第一个数据包的内容提供给它。因此,服务器进程发现自己处于一个方便的位置,只需处理一个完整的 HTTP 请求。
根据我的理解,TCPStream
不知道何时从客户端到达完整的消息,而是信息以字节流的形式到达。
但是,当我在 Rust 中执行标准的“Hello World”TCPStream
示例时,我正在从流中读取完整的 HTTP 消息。当我发送两条或更多条消息时,它们会相应地分开。
这怎么可能?
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_connection(stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("{}", String::from_utf8_lossy(&buffer[..]));
}
当我减小缓冲区大小时,HTTP 消息被截断并且新消息从头开始。我会以某种方式假设我必须自己管理结束和开始新的 HTTP 消息?
观察到效果的原因在于一般 reader 行为。
TcpStream
上的 read
方法由 Read
特性提供。引用此方法的 documentation:
Pull some bytes from this source into the specified buffer, returning how many bytes were read.
注意“一些字节”位:read
方法读取直到 或者 缓冲区被填充到最后 或者 正在读取的数据已耗尽。对于 TcpStream
,这种耗尽可能发生在两种情况下:
- 请求已发送完毕,对方不再提供任何数据;
- 或有些网络延迟,只有部分的请求已经在这里。
在您的测试中,您似乎总是遇到第一种情况:当您调用 read
时请求已经完全传输并且它适合缓冲区,因此可以读取到最后。但是,当缓冲区太小时,您将无法立即获取整个消息 - 因此您必须在同一流 上再次调用 read
,以获取其余的它。
在实际代码中,您应该在阅读请求时对其进行解析,以确定您遇到了哪种情况:请求是否已完全到达,还是必须再次阅读。
在非常低的级别上,客户端正在使用基本的 Unix write(socket, buf, nbytes)
operation to put the bytes into the socket, and the server is using nread =
read(socket, buf, maxbytes)
从套接字中提取字节。
当 read()
和 write()
与套接字一起使用时,它们不保证将 write()
拆分为多个 read()
或将多个 write()
合并为一个大 read()
。任何事情都可能发生,只要客户端写入的字节最终会按照客户端写入它们的相同顺序被服务器读取(假设网络连接由于某种原因没有中断)。
在您的特定情况下,客户端可能发出了两个 write()
调用,它们在 TCP 层中被转换为两个相应的数据包。服务器进程正在等待 read()
调用。服务器 OS 设法唤醒服务器进程,并在第二个数据包到达或以其他方式通过服务器 OS 之前将第一个数据包的内容提供给它。因此,服务器进程发现自己处于一个方便的位置,只需处理一个完整的 HTTP 请求。