CSE224 - Go I/O

Read and Write binary files

Go follows the Reader/Writer interface model:

  1. io.Reader: Can read data (Read([]byte) (int, error))
  2. io.Writer: Can write data (Write([]byte) (int, error))

Most I/O types (files, TCP connections, buffers) implement these interfaces.

I/O types

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.

Read a binary file

Open a file:

file, err := os.Open("file.bin")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

Read specific number of bytes

buf := make([]byte, 8) // read 8 bytes
_, err := file.Read(buf)
if err != nil {
    log.Fatal(err)
}
  • 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

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

Write a binary file

Create a binary file

file, err := os.Create("data.bin")
if err != nil {
    log.Fatal(err)
}
defer file.Close()
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
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.

Client and Server

Server:

package main

import (
  "fmt"
  "net"
)

func main() {
 // Start listening on TCP port 9000
 listener, err := net.Listen("tcp", "127.0.0.1:9000")
  if err != nil {
    fmt.Println("Error starting server:", err)
   return
  }
  defer listener.Close()

  fmt.Println("Server is listening on 127.0.0.1:9000")

 for {
    conn, err := listener.Accept()
    if err != nil {
      fmt.Println("Failed to accept connection:", err)
     continue
    }
    go handleConnection(conn) // Handle each connection concurrently
  }
}

func handleConnection(conn net.Conn) {
 defer conn.Close()

  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
  }

  message := string(buffer[:n])
  fmt.Printf("Received message: %s\n", message)

  // Send a response back
 response := "Hello Client! I got your message: " + message
 conn.Write([]byte(response))
}

Client:

package main

import (
  "fmt"
  "net"
)

func main() {
 // Connect to the server
  conn, err := net.Dial("tcp", "127.0.0.1:9000")
  if err != nil {
    fmt.Println("Error connecting to server:", err)
    return
  }
  defer conn.Close()

  // Send a message
 message := "Hello Server!"
 _, err = conn.Write([]byte(message))
 if err != nil {
    fmt.Println("Failed to send message:", err)
    return
  }

  // Read the response
  buffer := make([]byte, 1024)
 n, err := conn.Read(buffer)
 if err != nil {
    fmt.Println("Failed to read response:", err)
   return
  }

  response := string(buffer[:n])
 fmt.Printf("Server replied: %s\n", response)
}

CSE224 - Go I/O
http://example.com/2025/04/29/CSE224/go-io/
Author
Songlin Zhao
Posted on
April 29, 2025
Licensed under