Skip to content

net: reading a connectionless datagram socket after a CloseRead still blocks indefinitely #15250

@alexcesaro

Description

@alexcesaro

I was trying to cleanly close a Unix socket (i.e. stop receiving traffic, read all the socket buffer and then close the socket) but found it a bit cumbersome.

The issue is that the following code blocks indefinitely:

package main

import (
    "fmt"
    "net"
)

func main() {
    var socket = "/tmp/test.sock"
    addr, err := net.ResolveUnixAddr("unixgram", socket)
    if err != nil {
        panic(err)
    }

    conn, err := net.ListenUnixgram("unixgram", addr)
    if err != nil {
        panic(err)
    }

    w, err := net.DialUnix("unixgram", nil, conn.LocalAddr().(*net.UnixAddr))
    if err != nil {
        panic(err)
    }
    if _, err := w.Write([]byte("foo")); err != nil {
        panic(err)
    }

    if err := conn.CloseRead(); err != nil {
        panic(err)
    }

    buf := make([]byte, 10)
    n, err := conn.Read(buf)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(buf[:n])) // Prints foo.

    n, err = conn.Read(buf) // Hangs indefinitely. Should it return io.EOF?
    if err != nil {
        panic(err)
    }
    fmt.Println(string(buf[:n]))
}

To read all the socket buffer without blocking indefinitely you have to either:

  1. use conn.Close() after a timeout to unblock conn.Read()
  2. use conn.File() and syscall.SetNonblock() to read the connection buffer in a non-blocking way

I don't like 1. since it would make my tests slow or flaky. 2. works well but it makes the code more complex.

Since after a CloseRead(), a socket cannot receive more data I think it would make sense that Read returns an io.EOF when it reaches the end of the connection buffer.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions