Zero-Copy File Server
io.Copy from an *os.File to a *net.TCPConn hands the transfer to the kernel's sendfile, so a megabyte moves to the socket without ever passing through your program's memory. The client counts the bytes it receives.
package main
import (
"fmt"
"io"
"net"
"os"
"strings"
)
func main() {
// A 1 MiB file to serve.
f, _ := os.CreateTemp("", "payload")
defer os.Remove(f.Name())
f.WriteString(strings.Repeat("x", 1<<20))
f.Close()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
fmt.Println("listen:", err)
return
}
defer ln.Close()
// Server: io.Copy(*os.File -> *net.TCPConn) uses sendfile, in-kernel.
go func() {
conn, err := ln.Accept()
if err != nil {
return
}
defer conn.Close()
file, _ := os.Open(f.Name())
defer file.Close()
n, _ := io.Copy(conn, file) // no user-space buffer involved
fmt.Println("server sent", n, "bytes via sendfile")
}()
// Client: read the whole stream, count the bytes.
conn, err := net.Dial("tcp", ln.Addr().String())
if err != nil {
fmt.Println("dial:", err)
return
}
defer conn.Close()
got, _ := io.Copy(io.Discard, conn)
fmt.Println("client received", got, "bytes") // client received 1048576 bytes
}Save the code as main.go and run it:
go run main.goExpected output:
server sent 1048576 bytes via sendfile
client received 1048576 bytesThe full megabyte moves from file to socket in the kernel. sendfile is used on Linux and macOS; on platforms without it, io.Copy still works through a normal buffer.