std::net
: Поддержка TCP, UDP и IP
Практические советы
Пример: Эхо-сервер с выбором протокола
Упражнение
Заключение
В этом разделе мы познакомимся с модулем std::net
— частью стандартной библиотеки Rust, которая предоставляет инструменты для работы с сетью: поддержку протоколов TCP, UDP и IP. Этот модуль позволяет создавать серверы, клиенты и взаимодействовать с сетью на низком уровне. Лекция подойдёт как новичкам, только начинающим изучать сетевые аспекты Rust, так и опытным разработчикам, желающим углубить свои знания. Мы разберём основные компоненты с примерами, нюансами и практическими советами.
std::net
: Поддержка TCP, UDP и IPМодуль std::net
предоставляет низкоуровневый доступ к сетевым примитивам. Он не зависит от внешних библиотек и работает на всех платформах, поддерживаемых Rust. Основное внимание уделено трём ключевым областям: TCP (надёжная передача данных), UDP (быстрая передача без гарантий) и IP (адресация). Все операции в std::net
возвращают std::io::Result
, что требует обработки ошибок.
Основные типы:
TcpListener
и TcpStream
: Для серверов и клиентов TCP.UdpSocket
: Для работы с UDP.IpAddr
, Ipv4Addr
, Ipv6Addr
: Представление IP-адресов.SocketAddr
: Комбинация IP и порта.TCP (Transmission Control Protocol) обеспечивает надёжную, упорядоченную передачу данных между двумя точками. В std::net
это реализовано через TcpListener
(сервер) и TcpStream
(соединение).
Пример: Простой TCP-сервер и клиент
Сервер:
use std::net::TcpListener;
use std::io::{Read, Write};
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080")?;
println!("Сервер запущен на 127.0.0.1:8080");
for stream in listener.incoming() {
let mut stream = stream?;
println!("Новое соединение: {:?}", stream.peer_addr()?);
let mut buffer = [0; 1024];
stream.read(&mut buffer)?;
println!("Получено: {}", String::from_utf8_lossy(&buffer));
stream.write_all(b"Hello from server!")?;
}
Ok(())
}
Клиент:
use std::net::TcpStream;
use std::io::{Read, Write};
fn main() -> std::io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:8080")?;
println!("Подключено к серверу");
stream.write_all(b"Hello from client!")?;
let mut buffer = [0; 1024];
stream.read(&mut buffer)?;
println!("Ответ сервера: {}", String::from_utf8_lossy(&buffer));
Ok(())
}
Комментарии:
TcpListener::bind
привязывает сервер к адресу и порту.incoming
возвращает итератор по входящим соединениям.TcpStream
реализует Read
и Write
для обмена данными.peer_addr
возвращает адрес клиента.Заметка: Запустите сервер в одном терминале, а клиент — в другом, чтобы увидеть взаимодействие.
UDP (User Datagram Protocol) отправляет данные без установления соединения, что быстрее, но не гарантирует доставку. Используется UdpSocket
.
Пример: UDP-сервер и клиент
Сервер:
use std::net::UdpSocket;
fn main() -> std::io::Result<()> {
let socket = UdpSocket::bind("127.0.0.1:8081")?;
println!("UDP-сервер запущен на 127.0.0.1:8081");
let mut buffer = [0; 1024];
let (size, src) = socket.recv_from(&mut buffer)?;
println!("Получено {} байт от {:?}: {}",
size, src, String::from_utf8_lossy(&buffer[..size]));
socket.send_to(b"Echo from UDP server", src)?;
Ok(())
}
Клиент:
use std::net::UdpSocket;
fn main() -> std::io::Result<()> {
let socket = UdpSocket::bind("0.0.0.0:0")?; // Любой свободный порт
socket.connect("127.0.0.1:8081")?;
socket.send(b"Hello from UDP client")?;
let mut buffer = [0; 1024];
let (size, src) = socket.recv_from(&mut buffer)?;
println!("Ответ от {:?}: {}", src, String::from_utf8_lossy(&buffer[..size]));
Ok(())
}
Комментарии:
recv_from
возвращает размер данных и адрес отправителя.send_to
отправляет данные по указанному адресу.0.0.0.0:0
позволяет системе выбрать свободный порт для клиента.std::net
предоставляет типы для работы с IP-адресами: IpAddr
(общий тип), Ipv4Addr
и Ipv6Addr
.
Пример: работа с IP-адресами:
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
fn main() {
let ipv4 = Ipv4Addr::new(127, 0, 0, 1);
let ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
let ip: IpAddr = Ipv4Addr::LOCALHOST.into();
println!("IPv4? {}, IPv6? {}", ip.is_ipv4(), ip.is_ipv6());
let socket_addr = SocketAddr::new(ip, 8080);
println!("Сокет: {}", socket_addr);
}
Комментарии:
Ipv4Addr::new
создаёт адрес из октетов.into
преобразует Ipv4Addr
в IpAddr
.SocketAddr
объединяет IP и порт.Предупреждение: std::net
блокирует поток при ожидании данных. Для асинхронных операций используйте крейты вроде tokio
.
Result
от сетевых операций — сеть ненадёжна.std::io::BufReader
или BufWriter
с TcpStream
для повышения производительности.127.0.0.1
, но для реальных приложений учитывайте внешние IP.Программа, которая запускает TCP или UDP сервер в зависимости от аргумента:
use std::net::{TcpListener, UdpSocket, SocketAddr};
use std::io::{Read, Write};
use std::env;
fn tcp_server(addr: SocketAddr) -> std::io::Result<()> {
let listener = TcpListener::bind(addr)?;
println!("TCP сервер на {}", addr);
for stream in listener.incoming() {
let mut stream = stream?;
let mut buffer = [0; 1024];
let size = stream.read(&mut buffer)?;
stream.write_all(&buffer[..size])?;
}
Ok(())
}
fn udp_server(addr: SocketAddr) -> std::io::Result<()> {
let socket = UdpSocket::bind(addr)?;
println!("UDP сервер на {}", addr);
let mut buffer = [0; 1024];
let (size, src) = socket.recv_from(&mut buffer)?;
socket.send_to(&buffer[..size], src)?;
Ok(())
}
fn main() -> std::io::Result<()> {
let args: Vec = env::args().collect();
if args.len() < 2 {
println!("Укажите протокол: tcp или udp");
return Ok(());
}
let addr = "127.0.0.1:8080".parse()?;
match args[1].as_str() {
"tcp" => tcp_server(addr),
"udp" => udp_server(addr),
_ => {
println!("Неизвестный протокол");
Ok(())
}
}
}
Этот пример демонстрирует TCP и UDP эхо-серверы с общей логикой обработки.
Напишите программу, которая:
server
или client
.server
, запускает TCP-сервер, принимающий сообщения и отправляющий их в верхнем регистре.client
, подключается к серверу, отправляет строку и выводит ответ.Result
.Подсказка: Используйте to_uppercase()
для строки.
Модуль std::net
предоставляет базовые средства для работы с сетью в Rust. Он прост, надёжен и кроссплатформенен, но ограничен блокирующими операциями. Освоив TCP, UDP и IP-адресацию, вы сможете создавать сетевые приложения, от простых чатов до серверов. Для более сложных задач рассмотрите асинхронные библиотеки, о которых мы поговорим позже.
Выполните упражнение или переходите к следующему разделу!