Go-фича: Dialer с контекстом

Часть серии Принято! В ней простыми словами объясняются новые фичи Go.

Методы net.Dialer для подключения по конкретным протоколам с поддержкой контекста.

Версия 1.26 • Станд. библиотека • Мелочь

Что

Тип net.Dialer подключается к сетевому адресу, используя указанный протокол (network) — TCP, UDP, IP или Unix-сокеты.

Новые методы Dialer с поддержкой контекста — DialTCP, DialUDP, DialIP и DialUnix — объединяют эффективную реализацию (как в существующих Dial-функциях) с возможностью отмены (как в Dialer.DialContext).

Зачем

В пакете net уже есть отдельные функции для разных протоколов (DialTCP, DialUDP, DialIP и DialUnix), но их сделали до появления context.Context, поэтому они не поддерживают отмену:

func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error)
func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error)

С другой стороны, у типа net.Dialer есть универсальный метод DialContext. Он поддерживает отмену и может использоваться для подключения по любому из поддерживаемых протоколов:

func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error)

Если вы уже знаете протокол и адрес, использовать DialContext менее эффективно, чем специализированные функции вроде DialTCP, по следующим причинам:

  • Резолв адреса: DialContext сам выполняет разрешение адреса (например, DNS-запросы и преобразование в net.TCPAddr или net.UDPAddr) на основе названия протокола и исходного адреса. Специализированные функции принимают уже готовый объект адреса, поэтому этот шаг пропускается.

  • Роутинг по протоколу: DialContext должен определить, какой протокол использовать, и направить вызов нужному обработчику. Специализированные функции уже знают, какой протокол нужен, и этот шаг тоже пропускается.

Таким образом, функции net.Dial* работают быстрее, но не поддерживают отмену операций. Тип net.Dialer поддерживает отмену, но работает медленнее. Решение — добавить в Dialer методы, которые учитывают контекст и работают с конкретными протоколами.

Заодно это позволит использовать новые типы адресов из пакета netip (например, netip.AddrPort вместо net.TCPAddr), которые более предпочтительны в современном Go-коде.

Как

Добавить четыре новых метода в net.Dialer:

DialTCP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*TCPConn, error)
DialUDP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*UDPConn, error)
DialIP(ctx context.Context, network string, laddr, raddr netip.Addr) (*IPConn, error)
DialUnix(ctx context.Context, network string, laddr, raddr *UnixAddr) (*UnixConn, error)

Сигнатуры методов похожи на существующие функции пакета net, но дополнительно принимают контекст и используют новые типы адресов из пакета netip.

Пример

Используем метод DialTCP, чтобы подключиться к TCP-серверу:

var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Подключение не пройдет, потому что сервер не запущен.
raddr := netip.MustParseAddrPort("127.0.0.1:12345")
conn, err := d.DialTCP(ctx, "tcp", netip.AddrPort{}, raddr)
if err != nil {
    log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()

if _, err := conn.Write([]byte("Hello, World!")); err != nil {
    log.Fatal(err)
}
Failed to dial: dial tcp 127.0.0.1:12345: connect: connection refused (exit status 1)

Используем метод DialUnix, чтобы подключиться к Unix-сокету:

var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Подключение не пройдет, потому что сервер не запущен.
raddr := &net.UnixAddr{Name: "/path/to/unix.sock", Net: "unix"}
conn, err := d.DialUnix(ctx, "unix", nil, raddr)
if err != nil {
    log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()

if _, err := conn.Write([]byte("Hello, socket!")); err != nil {
    log.Fatal(err)
}
Failed to dial: dial unix /path/to/unix.sock: connect: no such file or directory (exit status 1)

В обоих случаях подключение не проходит, потому что я не запустил сервер в песочнице :)

Ссылки

𝗣 49097 • 𝗖𝗟 657296

★ Подписывайтесь на канал и проходите курсы.