Recently, I explored Unix Sockets and Protobuf with Go. Here’s a brief overview of some of the things I learned.

Find the related source code here:

https://github.com/m7kvqbe1/unix-sockets-protobuf-play

Unix Sockets: Inter-Process Communication

Unix Sockets offer a method for inter-process communication (IPC) on the same machine. It’s faster and leaner than leveraging a network protocol like TCP. They’re straightforward to set up in Go:

listener, _ := net.Listen("unix", "/tmp/example.sock")

conn, _ := net.Dial("unix", "/tmp/example.sock")

Lets accept incoming connections and handle each one with its own Go routine:

for {
  conn, err := listener.Accept()
  if err != nil {
    fmt.Println("Error accepting:", err.Error())
    continue
  }

  go handleServerConnection(conn)
}

We can transmit and receive from the socket like so:

if _, err = conn.Write(data); err != nil {
  fmt.Println("Client write error:", err)
  return
}

buf := make([]byte, 1024)

n, err := conn.Read(buf)
if err != nil {
  fmt.Println("Server read error:", err)
  return
}

We read a stream from the socket into a buffer.

Lets read it in chunks to make sure we can handle a stream of arbitrary size:

buf := make([]byte, 1024)
var data []byte

for {
  // Reads the stream for len of buf,
  // n = number bytes read into the buffer
  n, err := conn.Read(buf)
  if err != nil {
    // Finished reading the stream
    if err == io.EOF {
      break
    }

    fmt.Println("Server read error:", err)
    return
  }

  // ... Spreads buffer into the data slice
  data = append(data, buf[:n]...)
}

Protobuf: Data Serialization

Protocol Buffers (protobuf) by Google are used for serializing structured data. Defining a data structure is also easy:

syntax = "proto3";

package main;

option go_package = "./pb";

message SimpleMessage {
  string content = 1;
}

Using protoc, this schema is turned into Go code, allowing for serialization and deserialization of Go structs.

protoc --go_out=. --go_opt=paths=source_relative ./pb/message.proto

Implementing Protobuf over Unix Sockets

Combining Unix Sockets with protobuf involved serializing data into protobuf format and transmitting it through the socket.

The google.golang.org/protobuf/proto package provides Marshal and Unmarshal methods similar to encoding/json from the Go standard library.

Lets encode it in our client:

msg := &pb.SimpleMessage{Content: "Hello from client"}

data, err := proto.Marshal(msg)
if err != nil {
  fmt.Println("Client protobuf encode error:", err)
  return
}

if _, err = conn.Write(data); err != nil {
  fmt.Println("Client write error:", err)
  return
}

We can then decode it like so on our server:

var msg pb.SimpleMessage

if err := proto.Unmarshal(data, &msg); err != nil {
  fmt.Println("Server protobuf decode error:", err)
  return
}