Трайт ToSocketAddrs
#![allow(unused)] fn main() { pub trait ToSocketAddrs { type Iter: Iterator<Item = SocketAddr>; fn to_socket_addrs(&self) -> Result<Self::Iter>; } }
Трайт для объектов, которые могут быть преобразованы в SocketAddr.
Этот трайт используется для обобщенного разрешения адресов в объекты SocketAddr. По умолчанию он реализован для следующих типов:
SocketAddr-to_socket_addrsявляется тождественной функциейSocketAddrV4,SocketAddrV6- преобразуются вSocketAddr- (
IpAddr,u16) - создает адрес сокета из IP-адреса и порта - (
Ipv4Addr,u16), (Ipv6Addr,u16) - аналогично предыдущему &str- строка в формате"host:port"или"host"(с портом по умолчанию 0)&String- то же самое, что и&str&[SocketAddr]- итератор по уже разрешенным адресам
Обязательные ассоциированные типы
Iter
#![allow(unused)] fn main() { type Iter: Iterator<Item = SocketAddr> }
Возвращаемый итератор по адресам сокетов, который может создавать несколько значений SocketAddr из одного входного значения.
Обязательные методы
to_socket_addrs
#![allow(unused)] fn main() { fn to_socket_addrs(&self) -> Result<Self::Iter> }
Преобразует объект в итератор по разрешенным SocketAddr.
Возвращаемый итератор может не возвращать адреса в определенном порядке. Порт по умолчанию - 0.
Реализации по умолчанию
Для SocketAddr
#![allow(unused)] fn main() { impl ToSocketAddrs for SocketAddr { type Iter = option::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> Result<option::IntoIter<SocketAddr>> { Ok(Some(*self).into_iter()) } } }
Примеры
#![allow(unused)] fn main() { use std::net::{SocketAddr, ToSocketAddrs}; let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); for socket_addr in addr.to_socket_addrs().unwrap() { println!("Адрес: {}", socket_addr); } }
Для &[SocketAddr]
#![allow(unused)] fn main() { impl<'a> ToSocketAddrs for &'a [SocketAddr] { type Iter = iter::Cloned<slice::Iter<'a, SocketAddr>>; fn to_socket_addrs(&self) -> Result<iter::Cloned<slice::Iter<'a, SocketAddr>>> { Ok(self.iter().cloned()) } } }
Примеры
#![allow(unused)] fn main() { use std::net::{SocketAddr, ToSocketAddrs}; let addrs = [ "127.0.0.1:8080".parse().unwrap(), "127.0.0.1:8081".parse().unwrap(), ]; for socket_addr in addrs.to_socket_addrs().unwrap() { println!("Адрес: {}", socket_addr); } }
Для кортежа (IpAddr, u16)
#![allow(unused)] fn main() { impl ToSocketAddrs for (IpAddr, u16) { type Iter = option::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> Result<option::IntoIter<SocketAddr>> { let (ip, port) = *self; Ok(Some(SocketAddr::new(ip, port)).into_iter()) } } }
Примеры
#![allow(unused)] fn main() { use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs}; let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); let port = 8080; for socket_addr in (ip, port).to_socket_addrs().unwrap() { println!("Адрес: {}", socket_addr); } }
Для строк &str и String
#![allow(unused)] fn main() { impl ToSocketAddrs for &str { type Iter = vec::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> Result<vec::IntoIter<SocketAddr>> { // Реализация разрешения имен хостов } } impl ToSocketAddrs for String { type Iter = vec::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> Result<vec::IntoIter<SocketAddr>> { (&**self).to_socket_addrs() } } }
Примеры
#![allow(unused)] fn main() { use std::net::ToSocketAddrs; // Разрешение IPv4 адреса for socket_addr in "127.0.0.1:8080".to_socket_addrs().unwrap() { println!("IPv4 адрес: {}", socket_addr); } // Разрешение IPv6 адреса for socket_addr in "[::1]:8080".to_socket_addrs().unwrap() { println!("IPv6 адрес: {}", socket_addr); } // Разрешение имени хоста if let Ok(addrs) = "example.com:80".to_socket_addrs() { for socket_addr in addrs { println!("Адрес хоста: {}", socket_addr); } } // Только порт (localhost с портом 0) for socket_addr in ":0".to_socket_addrs().unwrap() { println!("Адрес: {}", socket_addr); } }
Примеры использования
Использование в сетевых функциях
use std::net::{TcpStream, TcpListener, UdpSocket}; use std::io; fn connect_to_server<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { TcpStream::connect(addr) } fn create_listener<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { TcpListener::bind(addr) } fn create_udp_socket<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { UdpSocket::bind(addr) } fn main() -> io::Result<()> { // Различные способы указания адресов let _stream1 = connect_to_server("127.0.0.1:8080")?; let _stream2 = connect_to_server(("127.0.0.1".parse().unwrap(), 8080))?; let _stream3 = connect_to_server("[::1]:8080")?; let _listener = create_listener("0.0.0.0:8080")?; let _udp_socket = create_udp_socket("localhost:0")?; Ok(()) }
Обработка множественных адресов
use std::net::{TcpStream, ToSocketAddrs}; use std::io; fn try_connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { let mut last_error = None; for socket_addr in addr.to_socket_addrs()? { match TcpStream::connect(socket_addr) { Ok(stream) => return Ok(stream), Err(e) => last_error = Some(e), } } Err(last_error.unwrap_or_else(|| { io::Error::new(io::ErrorKind::Other, "не удалось разрешить адрес") })) } fn main() -> io::Result<()> { // Попытка подключения к нескольким адресам let stream = try_connect("example.com:80")?; println!("Успешно подключились!"); Ok(()) }
Создание пользовательских типов, реализующих ToSocketAddrs
use std::net::{SocketAddr, ToSocketAddrs}; use std::vec; #[derive(Debug)] struct ServerConfig { host: String, port: u16, backup_ports: Vec<u16>, } impl ToSocketAddrs for ServerConfig { type Iter = vec::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> std::io::Result<vec::IntoIter<SocketAddr>> { let mut addrs = Vec::new(); // Основной порт let main_addr = format!("{}:{}", self.host, self.port); addrs.extend(main_addr.to_socket_addrs()?); // Резервные порты for &port in &self.backup_ports { let backup_addr = format!("{}:{}", self.host, port); addrs.extend(backup_addr.to_socket_addrs()?); } Ok(addrs.into_iter()) } } fn main() -> std::io::Result<()> { let config = ServerConfig { host: "localhost".to_string(), port: 8080, backup_ports: vec![8081, 8082], }; println!("Доступные адреса сервера:"); for addr in config.to_socket_addrs()? { println!(" {}", addr); } Ok(()) }
Использование с UDP сокетами
use std::net::{UdpSocket, ToSocketAddrs}; use std::io; fn send_message<A: ToSocketAddrs>(socket: &UdpSocket, msg: &[u8], addr: A) -> io::Result<usize> { let mut last_error = None; for target_addr in addr.to_socket_addrs()? { match socket.send_to(msg, target_addr) { Ok(size) => return Ok(size), Err(e) => last_error = Some(e), } } Err(last_error.unwrap_or_else(|| { io::Error::new(io::ErrorKind::Other, "не удалось отправить на любой адрес") })) } fn main() -> io::Result<()> { let socket = UdpSocket::bind("0.0.0.0:0")?; // Отправка на различные типы адресов send_message(&socket, b"Hello", "127.0.0.1:8080")?; send_message(&socket, b"Hello", ("::1".parse().unwrap(), 8080))?; send_message(&socket, b"Hello", "localhost:8080")?; Ok(()) }
Особенности реализации
Разрешение имен хостов
При использовании строк для разрешения адресов:
- Блокирующая операция: разрешение имен хостов может блокировать текущий поток
- Множественные адреса: один хост может вернуть несколько IP-адресов
- Порядок: адреса могут возвращаться в произвольном порядке
- Таймауты: разрешение имен подвержено таймаутам сети
Порт по умолчанию
Когда порт не указан (например, "localhost"), используется порт 0.
Ошибки разрешения
Метод возвращает io::Result, который может содержать ошибки:
io::ErrorKind::InvalidInput- неверный формат адресаio::ErrorKind::Other- ошибки разрешения именio::ErrorKind::TimedOut- таймаут разрешения
Рекомендации по использованию
- Всегда обрабатывайте ошибки - разрешение адресов может завершиться неудачно
- Итерируйтесь по результатам - один вызов может вернуть несколько адресов
- Используйте для гибкости - принимайте
ToSocketAddrsв своих API - Помните о блокировках - разрешение имен хостов блокирует поток