赞
踩
【跟小嘉学 Rust 编程】一、Rust 编程基础
【跟小嘉学 Rust 编程】二、Rust 包管理工具使用
【跟小嘉学 Rust 编程】三、Rust 的基本程序概念
【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念
【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据
【跟小嘉学 Rust 编程】六、枚举和模式匹配
【跟小嘉学 Rust 编程】七、使用包(Packages)、单元包(Crates)和模块(Module)来管理项目
【跟小嘉学 Rust 编程】八、常见的集合
【跟小嘉学 Rust 编程】九、错误处理(Error Handling)
【跟小嘉学 Rust 编程】十、泛型(Generic Type)、特征(Trait)和生命周期(Lifetimes)
【跟小嘉学 Rust 编程】十一、编写自动化测试
【跟小嘉学 Rust 编程】十二、构建一个命令行程序
【跟小嘉学 Rust 编程】十三、函数式语言特性:迭代器和闭包
【跟小嘉学 Rust 编程】十四、关于 Cargo 和 Crates.io
【跟小嘉学 Rust 编程】十五、智能指针(Smart Point)
【跟小嘉学 Rust 编程】十六、无畏并发(Fearless Concurrency)
【跟小嘉学 Rust 编程】十七、面向对象语言特性
【跟小嘉学 Rust 编程】十八、模式匹配(Patterns and Matching)
【跟小嘉学 Rust 编程】十九、高级特性
【跟小嘉学 Rust 编程】二十、进阶扩展
【跟小嘉学 Rust 编程】二十一、网络编程
【跟小嘉学 Rust 编程】二十二、常用 API
【跟小嘉学 Rust 编程】二十三、Cargo 使用指南
【跟小嘉学 Rust 编程】二十四、内联汇编(inline assembly)
【跟小嘉学 Rust 编程】二十五、Rust命令行参数解析库(clap)
【跟小嘉学 Rust 编程】二十六、Rust的序列化解决方案(Serde)
【跟小嘉学 Rust 编程】二十七、Rust 异步编程(Asynchronous Programming)
【跟小嘉学 Rust 编程】二十八、Rust中的日期与时间
【跟小嘉学 Rust 编程】二十九、Rust 中的零拷贝序列化解决方案(rkyv)
【跟小嘉学 Rust 编程】三十、Rust 使用 Slint UI
【跟小嘉学 Rust 编程】三十一、Rust的日志与追踪
【跟小嘉学 Rust 编程】三十二、Rust的设计模式(Design Patterns)
【跟小嘉学 Rust 编程】三十三、Rust的Web开发框架之一: Actix-Web的基础
【跟小嘉学 Rust 编程】三十四、Rust的Web开发框架之一: Actix-Web的进阶
本章节讲解 Rust 标准库(std::net 模块)操作 TCP 和 UDP 编程
主要教材参考 《The Rust Programming Language》
主要教材参考 《Rust For Rustaceans》
主要教材参考 《The Rustonomicon》
主要教材参考 《Rust 高级编程》
TCP 服务端使用 std::net::TcpListener::bind 方法来监听IP地址和端口。该方法定义如下
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener>
我们可以看到泛型参数 A 为 ToSocketAddrs 其实是一个 SocketAddr 的数组,我们可以使用 字符串方式来调用该方法 也可以使用数组的方式来使用。
use std::net::{SocketAddr, TcpListener};
fn main(){
let listener = TcpListener::bind("127.0.0.1:3000").unwrap();
let addrs = [
SocketAddr::from(([127, 0, 0, 1], 80)),
SocketAddr::from(([127, 0, 0, 1], 443)),
];
let listener = TcpListener::bind(&addrs[..]).unwrap();
}
两种方式调用都可以,第二种就是绑定 80端口失败,绑定端口443;
为 socket 创建一个新的句柄 ,两个句柄都可以用于接受传入的链接。
pub fn accept(&self) -> Result<(TcpStream, SocketAddr)>
从 监听器上接收一个新的连接,该方法返回 Result 。
use std::net::TcpListener;
#![allow(unused)]
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
match listener.accept() {
Ok((_socket, addr)) => println!("new client: {addr:?}"),
Err(e) => eprintln!("couldn't get client: {e:?}"),
}
}
返回监听器上正在接收连接的迭代器,迭代器永远不会返回None,也不会产生一个 SokcetAddr 结构,相当于循环调用 accept 。
use std::net::{TcpListener, TcpStream};
fn handle_connection(stream: TcpStream) {
//...
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:80")?;
for stream in listener.incoming() {
match stream {
Ok(stream) => {
handle_connection(stream);
}
Err(e) => { /* connection failed */ }
}
}
Ok(())
}
这是一个 仅限 nightly 使用的实验性 API,参考 https://github.com/rust-lang/rust/pull/88339
#![feature(tcplistener_into_incoming)]
use std::net::{TcpListener, TcpStream};
fn listen_on(port: u16) -> impl Iterator<Item = TcpStream> {
let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();
listener.into_incoming()
.filter_map(Result::ok) /* Ignore failed connections */
}
fn main() -> std::io::Result<()> {
for stream in listen_on(80) {
/* handle the connection here */
}
Ok(())
}
pub fn set_ttl(&self, ttl: u32) -> Result<()>
pub fn ttl(&self) -> Result<u32>
socket 发送 internet 协议数据包的生存时间值。
将 TCP 流设置成阻塞或非阻塞方式。
use std::io;
use std::net::TcpListener;
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
listener.set_nonblocking(true).expect("Cannot set non-blocking");
for stream in listener.incoming() {
match stream {
Ok(s) => {
// do something with the TcpStream
handle_connection(s);
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
// wait until network socket is ready, typically implemented
// via platform-specific APIs such as epoll or IOCP
wait_for_fd();
continue;
}
Err(e) => panic!("encountered IO error: {e}"),
}
}
IpAddr 枚举
pub enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
使用方式
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#![allow(unused)]
fn main() {
let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!("127.0.0.1".parse(), Ok(localhost_v4));
assert_eq!("::1".parse(), Ok(localhost_v6));
assert_eq!(localhost_v4.is_ipv6(), false);
assert_eq!(localhost_v4.is_ipv4(), true);
}
同样 SocketAddr 是一个枚举
pub enum SocketAddr {
V4(SocketAddrV4),
V6(SocketAddrV6),
}
使用用例:
use std::net::{Ipv4Addr, SocketAddrV4};
use std::net::{Ipv6Addr, SocketAddrV6};
fn main(){
let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080);
assert_eq!("127.0.0.1:8080".parse(), Ok(socket));
assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(socket.port(), 8080);
let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);
assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket));
assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
assert_eq!(socket.port(), 8080);
}
使用 std::net::TCPStream 可以连接服务器、进行数据读取、写入数据;
打开一个 Tcp 连接到远程主机。
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpStream>
使用
use std::net::TcpStream;
if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") {
println!("Connected to the server!");
} else {
println!("Couldn't connect to server...");
}
pub fn connect_timeout(
addr: &SocketAddr,
timeout: Duration
) -> Result<TcpStream>
在超时时间内打开一个远程主机的TCP连接。
pub fn shutdown(&self, how: Shutdown) -> Result<()>
关闭连接读、写或都关闭。该函数将导致指定部分上的所有挂起和未来的I/O 立即返回一个适当的值。
特定平台的行为
pub fn try_clone(&self) -> Result<TcpStream>
pub fn set_read_timeout(&self, dur: Option<Duration>) -> Result<()>
pub fn set_write_timeout(&self, dur: Option<Duration>) -> Result<()>
如果指定为 None 读取调用将无限期阻塞,如果传入 0 Duration 。则返回Err。
特定平台行为:每当设置此项导致读取超时,平台可能返回不同的错误代码
从连接上读取数据,不从队列中删除数据,如果成功返回已peek的字节数,连续调用返回相同的数据。
这是通过将MSG_PEEK作为一个标志传递给底层的recv系统调用来实现的。
pub fn peek(&self, buf: &mut [u8]) -> Result<usize>
这是一个 nightly 版本的 实验性 API。
pub fn set_linger(&self, linger: Option<Duration>) -> Result<()>
如果设置了 SO_LINGER,系统尝试发送挂起数据时 ,sokect 将在指定时间内保持打开状态,否则系统会立即关闭套接字 或等待默认超时。
#![allow(unused)]
#![feature(tcp_linger)]
fn main() {
use std::net::TcpStream;
use std::time::Duration;
let stream = TcpStream::connect("127.0.0.1:8080")
.expect("Couldn't connect to the server...");
stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");
}
Rust 标准库中的 TCP 流默认启用了 Nagle 的算法,它会避免高频发送小量的数据包,所以数据不会总是立即发送,这会对一些场合的应用产生影响(Rust TCP/Websocket 连接延迟波动)。
设置为 true 之后,会禁用 Nagle 算法。
pub fn set_nodelay(&self, nodelay: bool) -> Result<()>
示例
use std::net::TcpStream;
#![allow(unused)]
fn main() {
let stream = TcpStream::connect("127.0.0.1:8080")
.expect("Couldn't connect to the server...");
stream.set_nodelay(true).expect("set_nodelay call failed");
}
可以用于 UDP 协议通信,通常用于低延迟比保证传输更重要的场景,例如:音频/视频流、网络发现等。
pub fn bind<A: ToSocketAddrs>(addr: A) -> Result<UdpSocket>
从给定地址创建一个 UdpSocket 对象。使用方法和 TCPListener 差不多
pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)>
接受单个数据包信息,如果成功,返回读取的字节数和原始字节。函数必须使用有效字节数组调用,但必须有足够大小来保存消息字节,如果消息太长而无法转入所提供的缓存区则会丢弃多余的字节。
use std::net::UdpSocket;
#![allow(unused)]
fn main() {
let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
let mut buf = [0; 10];
let (number_of_bytes, src_addr) = socket.recv_from(&mut buf)
.expect("Didn't receive data");
let filled_buf = &mut buf[..number_of_bytes];
}
发送数据到服务端
pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> Result<usize>
返回成功写入的字节数。
use std::net::UdpSocket;
#![allow(unused)]
fn main() {
let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data");
}
广播是一种一对多的通信,即目的是将数据报发送到网络中的所有节点。对于点对点通信的情况不同,我们不必知道目标主机的IP地址,而是使用广播地址。
广播地址是一个逻辑地址,连接到网络的设备可以在该地址上接收数据包。
pub fn set_broadcast(&self, broadcast: bool) -> Result<()>
pub fn broadcast(&self) -> Result<bool>
默认值为false,设置为true之后,会开启广播
广播效率低下,因为数据包被发现倒网络中的所有节点,而不管他们是否有兴趣接收通信,这可能是一种资源浪费。多播解决了这个问题,并且只向感兴趣的消费者发送数据包
其中多播地址代表每个组。在IPv4 中,224.0.0.0 到 239.255.255.255 之间的任何地址都可以用作多播地址。只有订阅组的那些节点才能接收传送到该组的数据包。
获取此套接字的 IP_MULTICAST_LOOP 选项的值。
设置组播 ttl 超时时间
接收端离开组播
接收端加入组播地址才能接收数据。
设置超时时间
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()>
以上就是今天要讲的内容
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。