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)
    }
}
Explanation:
- Use 
net.Listento listen on the specified address and port. - Use 
listener.Acceptto accept client connections, returning anet.Connobject. - 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)
    }
}
Explanation:
- Use 
net.Dialto connect to the server. - Read user input from standard input and send it to the server.
 - 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)
        }
    }
}
Explanation:
- Use 
net.ResolveUDPAddrto resolve the UDP address. - Use 
net.ListenUDPto listen on the UDP port. - Use 
ReadFromUDPto receive data and obtain the client’s address. - Use 
WriteToUDPto 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]))
    }
}
Explanation:
- Use 
net.ResolveUDPAddrto resolve the server address. - Use 
net.DialUDPto create a UDP connection (although it is essentially connectionless). - Sending and receiving data is similar to the TCP client.
 
We are Leapcell, your top choice for hosting Go projects.
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!
Follow us on X: @LeapcellHQ
              


    
Top comments (0)