io.Reader: Can read data (Read([]byte) (int, error))
io.Writer: Can write data (Write([]byte) (int, error))
Most I/O types (files, TCP connections, buffers) implement these interfaces.
I/O types
1 2 3 4
file, _ := os.Open("hello.txt") // *os.File, used when read/write to files. conn, _ := net.Dial("tcp", "example.com:80") // net.Conn, r := strings.NewReader("hello world") // *strings.Reader r := bufio.NewReader(strings.NewReader("hello\nworld")) // bufio.Reader, wraps any io.Reader to add buffering and helper methods like ReadLine(), ReadString('\n'), etc.
All above implements both io.Reader and io.Writer.
n, err := file.Read(buf): Read to a slice of bytes, returns actual number of bytes read, may read less than you ask for, even though there’s more data available later. It blocks until at least some bytes are ready to be read.
n, err := io.ReadFull(reader, buf): Reads exactly len(buf) bytes, blocking until buffer is full or EOF.
data, err := io.ReadAll(reader): Reads entire stream until EOF into memory (until I receive EOF).
data, err := os.ReadFile(filePath): os.ReadFile is a convenience function that: Opens a file, Reads all of its contents into memory, Closes the file, Returns the file contents as a []byte.
Read and convert
1 2 3 4 5 6
var num int32 err := binary.Read(file, binary.LittleEndian, &num) if err != nil { log.Fatal(err) } fmt.Println("Read int32:", num)
err := binary.Read(): A high-level function from encoding/binary, read and then convert
data := []byte("Hello, binary world!\n") n, err := file.Write(data) if err != nil { log.Fatal(err) }
n, err := writer.Write(data): Writes len(data) bytes, returns how many were actually written (should be all, or an error).
error := os.WriteFile(name string, data []byte, perm fs.FileMode): writes data to a file in one shot
1 2 3 4 5
var number uint32 = 123456 err = binary.Write(file, binary.LittleEndian, number) if err != nil { log.Fatal(err) }
Communicate via network
When you call conn.Write(data) in Go (e.g., using a TCP connection), you’re not directly writing data onto the network wire. Instead, your data is copied into the OS’s send buffer. The OS then takes care of delivering that data over the network asynchronously.
When you call conn.Read(buffer), you’re trying to read data from the OS’s receive buffer. If no data is available yet, Read() will block (wait) until some data is available.
buffer := make([]byte, 1024) // Create a buffer to hold incoming data n, err := conn.Read(buffer) if err != nil { fmt.Println("Failed to read data:", err) return }