Socket Programming In Go

Socket helps in establishing communication between processes. The processes that use sockets for communication are from the same system or from systems over a remote network. The server forms the listener socket while the client reaches out to the server for connection.

Golang net package

The net package provides all APIs to implement socket communication. It provides low-level access to networking primitives but most clients will need only the basic interface provided by Dial, Listen, and Accept functions and the associated Conn and Listener interfaces.

  • Dial: It helps in connecting to a server.
  • Listen: It helps in creating a server.
  • Accept: It waits for the next call and returns a generic conn.

In socket programming, the communication is based on the client-server model where the client tries to connect to the server on a specific port. Once the connection is established, it sends messages and receives a response from a server.

Let’s understand the client-side code first.

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "net"
)

func main() {

   conn, err := net.Dial("tcp", "localhost:9988")
   if err != nil {
       panic(err)
   }
   serializedMessage, _ := Serialize(map[string]interface{}{"message": "Test message", "kind": "Test"})
   _, err = conn.Write([]byte(serializedMessage))
   if err != nil {
       fmt.Println("Error in sending a message:", err.Error())
   }
   reply := make([]byte, 1024)
   replyLen, err := conn.Read(reply)
   if err != nil {
       fmt.Println("Error in reading a reply:", err.Error())
   }
   fmt.Println("Reply from server: ", string(reply[:replyLen]))
   defer conn.Close()
}

func Serialize(msg map[string]interface{}) ([]byte, error) {
   var b bytes.Buffer
   encoder := json.NewEncoder(&b)
   err := encoder.Encode(msg)
   return b.Bytes(), err
}

In the above client-side code, we used the dial function to establish the connection, the write function to send the message, and the read function to read the reply from the server.

The Serialize function helps serialize the input data into an array of bytes.
Now, let’s understand the server-side code.

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "net"
   "os"
)

func main() {
   fmt.Println("Started socket server on port ", 9988)
   server, err := net.Listen("tcp", "localhost:9988")
   if err != nil {
       fmt.Println("Error while listening:", err.Error())
       os.Exit(1)
   }
   defer server.Close()
   fmt.Println("Waiting for client")
   for {
       connection, err := server.Accept()
       if err != nil {
           fmt.Println("Error while accepting: ", err.Error())
           os.Exit(1)
       }
       fmt.Println("Client connected successfully")
       go clientHandler(connection)
   }
}
func clientHandler(connection net.Conn) {
   buffer := make([]byte, 1024)
   mLen, err := connection.Read(buffer)
   if err != nil {
       fmt.Println("Error while reading:", err.Error())
   }
   var decodedMap map[string]interface{}
   decodedMap, _ = Deserialize(buffer)
   fmt.Println("Data received from client: ", string(buffer[:mLen]))
   _, err = connection.Write([]byte("Received data with message " + decodedMap["message"].(string) + " and kind " + decodedMap["kind"].(string)))
   if err != nil {
       fmt.Println("Error while Writing:", err.Error())
   }
   connection.Close()
}

func Deserialize(b []byte) (map[string]interface{}, error) {
   var msg map[string]interface{}
   buf := bytes.NewBuffer(b)
   decoder := json.NewDecoder(buf)
   err := decoder.Decode(&msg)
   return msg, err
}

In the above code, we started the socket server on port 9988 using the Listen function of the net package and then waited for the client to connect. When the connection is established, we pass the connection object to the clientHandler function which handles the requests from the client.The Deserialize function helps in client data deserialization.
To run the above code snippets, simply run them from two different terminals using the following commands.

$ go build
$ go run main.go

In this blog, we specifically covered the fundamentals of socket programming and discussed how data flows and client-server communication works in golang.

Vaibhav Dighe

Vaibhav Dighe

Software Engineer at PLG Works
Pune, maharashtra