Следите за новинками
в нашем Telegram канале. Жми, чтобы подписаться:)
⊗goSpUdEx 259 of 280 menu
◀ ▶

Обмен UDP-сообщениями в Go

Полный обмен требует двух ролей: кто-то слушает порт, кто-то шлет датаграмму и ждет ответ. В учебном примере обе части можно держать в одном файле: в отдельной горутине крутится прием и ответ, а из main после короткой паузы уходит запрос и читается ответ.

Порядок запуска важен: слушатель должен успеть открыть порт до того, как клиентская часть пошлет первый пакет. Пауза в несколько миллисекунд или канал готовности здесь обычнее, чем надежда на удачную гонку.

Соберем вопрос "ask" и ответ "ans" на "127.0.0.1:9091":

package main import ( "fmt" "net" "time" ) func serve(conn *net.UDPConn, ready chan struct{}) { close(ready) buf := make([]byte, 64) n, remote, err := conn.ReadFromUDP(buf) if err != nil { return } if string(buf[:n]) == "ask" { conn.WriteToUDP([]byte("ans"), remote) } } func main() { addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9091") if err != nil { fmt.Println("resolve error") return } conn, err := net.ListenUDP("udp", addr) if err != nil { fmt.Println("listen error") return } defer conn.Close() ready := make(chan struct{}) go serve(conn, ready) <-ready client, err := net.Dial("udp", "127.0.0.1:9091") if err != nil { fmt.Println("dial error") return } defer client.Close() _, err = client.Write([]byte("ask")) if err != nil { fmt.Println("write error") return } buf := make([]byte, 64) client.SetReadDeadline(time.Now().Add(time.Second)) n, err := client.Read(buf) if err != nil { fmt.Println("read error") return } fmt.Println(string(buf[:n])) }

На экране "ans". Канал ready закрывается, когда ветка-слушатель дошла до ожидания пакета; только после этого main шлет "ask".

Тот же сценарий можно разнести на две программы: одна только слушает и отвечает, вторая только шлет и читает. Тогда перед запуском клиента сервер уже должен работать в отдельном окне терминала:

package main import ( "fmt" "net" ) func main() { addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:9091") conn, err := net.ListenUDP("udp", addr) if err != nil { fmt.Println("listen error") return } defer conn.Close() fmt.Println("listening") buf := make([]byte, 64) n, remote, err := conn.ReadFromUDP(buf) if err != nil { fmt.Println("read error") return } conn.WriteToUDP([]byte("ans"), remote) fmt.Println("got", string(buf[:n])) }

Дано сообщение запроса:

question := []byte("time?")

В ветке-слушателе на "127.0.0.1:9091" примите пакет и ответьте "now"; из main отправьте question и выведите ответ.

Дан фрагмент клиента:

client, _ := net.Dial("udp", "127.0.0.1:9091") client.Write([]byte("ask")) buf := make([]byte, 32) n, _ := client.Read(buf)

Скажите, почему Read может зависнуть, если серверную часть не запустить раньше и не ответить на порт клиента.

Скажите, зачем в одном файле перед отправкой ждут сигнал от ветки-слушателя, а не шлют пакет сразу после go.

Файлы и первые предложения программ такие. В main.go из первого примера первая строка на Go это package main; первый вывод клиента после обмена печатает ответ сервера. В server.go первая строка та же package main, а первое сообщение на экран это "listening", когда порт уже открыт.

← →
↑
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить