DEV Community

Cover image for Socket Programming in Go
Leapcell
Leapcell

Posted on

Socket Programming in Go

Cover

Basic Principles of Socket Programming

Socket programming refers to the process of developing applications using the Socket interface in network programming. A Socket is a fundamental concept in network communication. It is an abstract data structure used to describe an endpoint in a network. A Socket contains information such as an IP address, port number, and protocol type, and it is used to identify a specific process or device in the network.

Socket programming is based on two concepts: client and server. The client is the party that initiates the request, while the server is the party that provides the service. By establishing a connection over the network, the client can send requests to the server, and the server processes the requests and returns responses to the client.

Socket programming requires a special protocol that defines a set of data formats and interaction rules. The most commonly used Socket protocols today are TCP and UDP. The TCP protocol is a connection-oriented protocol that ensures reliable data transmission. The UDP protocol, on the other hand, is a connectionless protocol, which is faster but unreliable.

  • TCP (Transmission Control Protocol): Connection-oriented, reliable, ordered, and based on byte streams.
  • UDP (User Datagram Protocol): Connectionless, unreliable, unordered, and based on datagrams.

The Go language provides a powerful net package, which simplifies Socket programming. With it, developers can implement network communication without directly manipulating low-level system calls.

TCP Socket Programming

TCP is a connection-oriented protocol, suitable for scenarios that require reliable data transmission.

Server-Side Implementation

The following is a simple TCP server example. It listens on a specified port, accepts client connections, and echoes back the received messages.

package main

import (
    "bufio"
    "fmt"
    "net"
)

func handleConnection(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    for {
        // Read data sent by client
        message, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("Error reading data:", err)
            break
        }
        fmt.Printf("Received message: %s", message)
        // Echo the message back to the client
        conn.Write([]byte("Echo: " + message))
    }
}

func main() {
    // Listen on local port 8080
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Failed to listen on port:", err)
        return
    }
    defer listener.Close()
    fmt.Println("Server is listening on port 8080...")

    for {
        // Accept client connection
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Failed to accept connection:", err)
            continue
        }
        // Handle the connection (can handle multiple concurrently)
        go handleConnection(conn)
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Use net.Listen to listen on the specified address and port.
  2. Use listener.Accept to accept client connections, returning a net.Conn object.
  3. Start a new goroutine for each connection to handle them concurrently.

Client-Side Implementation

The following is a simple TCP client example. It connects to the server and sends messages.

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // Connect to the server (localhost:8080)
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        fmt.Println("Failed to connect to server:", err)
        return
    }
    defer conn.Close()

    reader := bufio.NewReader(os.Stdin)
    for {
        // Read user input from standard input
        fmt.Print("Enter message: ")
        message, _ := reader.ReadString('\n')
        // Send the message to the server
        _, err := conn.Write([]byte(message))
        if err != nil {
            fmt.Println("Failed to send message:", err)
            return
        }
        // Receive echoed message from the server
        response, err := bufio.NewReader(conn).ReadString('\n')
        if err != nil {
            fmt.Println("Failed to receive message:", err)
            return
        }
        fmt.Print("Server echo: " + response)
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Use net.Dial to connect to the server.
  2. Read user input from standard input and send it to the server.
  3. Receive the echoed message from the server and display it.

UDP Socket Programming

UDP is a connectionless protocol, suitable for scenarios where real-time performance is more important than reliability. Below, we will introduce how to implement a UDP server and client using Go.

Server-Side Implementation

The following is a simple UDP server example. It listens on a specified port, receives data from clients, and echoes back the messages.

package main

import (
    "fmt"
    "net"
)

func main() {
    // Listen on local port 8081 using UDP
    addr, err := net.ResolveUDPAddr("udp", ":8081")
    if err != nil {
        fmt.Println("Failed to resolve address:", err)
        return
    }

    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        fmt.Println("Failed to listen on UDP:", err)
        return
    }
    defer conn.Close()
    fmt.Println("UDP server is listening on port 8081...")

    buffer := make([]byte, 1024)
    for {
        // Read data sent by client
        n, clientAddr, err := conn.ReadFromUDP(buffer)
        if err != nil {
            fmt.Println("Error reading data:", err)
            continue
        }
        message := string(buffer[:n])
        fmt.Printf("Received message from %s: %s", clientAddr, message)

        // Echo the message back to the client
        _, err = conn.WriteToUDP([]byte("Echo: "+message), clientAddr)
        if err != nil {
            fmt.Println("Failed to send message:", err)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Use net.ResolveUDPAddr to resolve the UDP address.
  2. Use net.ListenUDP to listen on the UDP port.
  3. Use ReadFromUDP to receive data and obtain the client’s address.
  4. Use WriteToUDP to send data back to the client.

Client-Side Implementation

The following is a simple UDP client example. It sends messages to the server and receives the echoed responses.

package main

import (
    "bufio"
    "fmt"
    "net"
    "os"
)

func main() {
    // Resolve server address
    serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8081")
    if err != nil {
        fmt.Println("Failed to resolve server address:", err)
        return
    }

    // Create a UDP connection (actually connectionless)
    conn, err := net.DialUDP("udp", nil, serverAddr)
    if err != nil {
        fmt.Println("Failed to connect to UDP server:", err)
        return
    }
    defer conn.Close()

    reader := bufio.NewReader(os.Stdin)
    for {
        // Read user input from standard input
        fmt.Print("Enter message: ")
        message, _ := reader.ReadString('\n')
        // Send message to the server
        _, err := conn.Write([]byte(message))
        if err != nil {
            fmt.Println("Failed to send message:", err)
            return
        }
        // Receive echoed message from the server
        buffer := make([]byte, 1024)
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Failed to receive message:", err)
            return
        }
        fmt.Print("Server echo: " + string(buffer[:n]))
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. Use net.ResolveUDPAddr to resolve the server address.
  2. Use net.DialUDP to create a UDP connection (although it is essentially connectionless).
  3. Sending and receiving data is similar to the TCP client.

We are Leapcell, your top choice for hosting Go projects.

Leapcell

Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:

Multi-Language Support

  • Develop with Node.js, Python, Go, or Rust.

Deploy unlimited projects for free

  • pay only for usage — no requests, no charges.

Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the Documentation!

Try Leapcell

Follow us on X: @LeapcellHQ


Read on our blog

Top comments (0)