Невозможно прочитать из сокета UNIX с помощью net.Conn.Read

Я пытаюсь установить простую связь сокета UNIX со службой с помощью GO. Для тестирования я создал такой сокет:

$ nc -vlU /tmp/sock
Bound on /tmp/sock
Listening on /tmp/sock

А в GO я net.Dial и пытаюсь что-то написать, потом читаю. Я вижу записанные данные в консоли nc и знаю, что это работает. Но операция net.Conn.Read кажется неблокирующей и немедленно вернется с нулевой длиной. Из всего, что я прочитал, и из примеров, которые я видел, эта операция должна блокироваться.

buf := make([]byte, 0, 4096)
ctl, err := net.Dial("unix", "/tmp/sock")
for {
    ctl.Write([]byte("test write\n"))
    n, err := ctl.Read(buf)
    fmt.Printf("Len:%v, Data:%v, err:%v", n, buf, err)
}

Я вижу, что соединение установлено и данные записаны ...

Connection received on /tmp/sock
test write
test write
test write
test write
...

Но консоль GO зацикливается без блокировки, сообщая о нулевой длине и отсутствии ошибок.

Len:0, Data:[], err:<nil>
Len:0, Data:[], err:<nil>
Len:0, Data:[], err:<nil>
Len:0, Data:[], err:<nil>
...

И, конечно, если я что-нибудь наберу в консоли nc, в выводе программы GO ничего интересного не произойдет.

Есть идеи, что я делаю не так?


person sherrellbc    schedule 19.01.2021    source источник
comment
Каков err результат Read?   -  person Burak Serdar    schedule 20.01.2021
comment
@BurakSerdar nil. Ошибки нет. Он просто возвращает нулевую длину и без ошибок в цикле.   -  person sherrellbc    schedule 20.01.2021
comment
@sherrellbc: нет ошибки и чтение 0 байтов указывает на то, что одноранговое соединение установлено.   -  person Steffen Ullrich    schedule 20.01.2021
comment
@SteffenUllrich. Я заметил, что он продолжает повторять такой цикл, если я убиваю процесс nc. Но пока он зацикливается, я продолжаю видеть записанные данные в nc консоли (если я добавлю net.Conn.Write вызов в цикл). Так что соединение остается в силе. Я обновил сообщение, чтобы отразить это.   -  person sherrellbc    schedule 20.01.2021
comment
@SteffenUllrich, закрытие соединения должно возвращать io.EOF   -  person Burak Serdar    schedule 20.01.2021
comment
@SteffenUllrich У меня есть проверка в моем коде (которую я не публиковал), когда net.Conn.Read возвращает ошибку. Но ошибки нет. Чтение не блокируется, и ошибка не возвращается. Я вижу записанные данные в консоли nc, поэтому соединение действительно, потому что данные проходят. Я просто почему-то ничего не могу читать.   -  person sherrellbc    schedule 20.01.2021
comment
@JimB Я тоже так делаю (в коде не опубликовано). Это прекрасно работает. Я могу записывать данные без проблем. Только чтение кажется неудачным.   -  person sherrellbc    schedule 20.01.2021
comment
@sherrellbc: strace показывает, что сокет открыт как неблокирующий, epoll используется для опроса и никогда не возникает read (вероятно, поскольку epoll никогда не возвращается). Таким образом, ctl.Read, похоже, на самом деле не вызывает чтения, а просто заставляет текущие считываемые данные опрашивать сокет, чего нет. То же самое и с TCP, кстати   -  person Steffen Ullrich    schedule 20.01.2021
comment
@SteffenUllrich Odd. Я пробовал что-то подобное около года назад, и в то время это работало нормально (просто использование сокетов UNIX в целом). Я использовал strace и заметил то же самое.   -  person sherrellbc    schedule 20.01.2021


Ответы (1)


короткий ответ

buf := make([]byte, 0, 4096)

этот код создает буфер с _2 _ !!!

сделай это как

buf := make([]byte, 4096)

пример

это пример, который я использовал, и все они работают.

На практике чтение и запись сокета должны происходить в two goroutines

package main

import (
    "bufio"
    "fmt"
    "io"
    "net"
    "time"
)

func main() {
    buf := make([]byte, 0, 4096)
    fmt.Println(buf)
    main03()
}

func main01() {
    // nc -vlU /tmp/sock0120
    ctl, err := net.Dial("unix", "/tmp/sock0120")
    if err != nil {
        fmt.Println(err)
        return
    }
    reader := bufio.NewReader(ctl)
    for {
        time.Sleep(time.Second)
        ctl.Write([]byte("test write\n"))
        msg, err := reader.ReadString('\n')
        fmt.Printf("Len:%v, Data:%v, err:%v\n", len(msg), msg, err)
    }
}

func main02() {
    // nc -vlU /tmp/sock0120
    buf := make([]byte, 4)
    ctl, err := net.Dial("unix", "/tmp/sock0120")
    if err != nil {
        fmt.Println(err)
        return
    }
    reader := bufio.NewReader(ctl)
    for {
        time.Sleep(time.Second)
        ctl.Write([]byte("test write\n"))
        n, err := io.ReadFull(reader, buf)
        fmt.Printf("Len:%v, Data:%v, err:%v\n", n, buf, err)
    }
}

func main03() {
    // nc -vlU /tmp/sock0120
    buf := make([]byte, 4)
    ctl, err := net.Dial("unix", "/tmp/sock0120")
    if err != nil {
        fmt.Println(err)
        return
    }
    for {
        time.Sleep(time.Second)
        ctl.Write([]byte("test write\n"))
        n, err := ctl.Read(buf)
        fmt.Printf("Len:%v, Data:%v, err:%v\n", n, buf, err)
    }
}

person Frank Wang    schedule 20.01.2021
comment
Это было. Уф. Какая трата времени на такой простой вопрос. Я смотрел на код, который использовал как срез фиксированного размера (для каждого recv), так и с нулевой длиной (буфер данных), и просто дважды скопировал make([]byte, 0, N). - person sherrellbc; 20.01.2021
comment
@sherrellbc Несколько раз у меня был опыт, похожий на ваш. Странное поведение всегда приводит к простой ошибке. - person Frank Wang; 21.01.2021