Go language network programming

Network programming is an indispensable technical field in the modern Internet. In Golang, we can easily build efficient network communication applications.
In Golang, we can use the net package in the standard library to implement network programming. The net package has many common network protocols and functions, such as TCP, UDP, HTTP, etc.

1. TCP server and client

Server:

package main

import (
 "net"
 "fmt"
)

func main() {<!-- -->
listen, err := net.Listen("tcp", ":8080")
if err != nil {<!-- -->
fmt.Println(err)
return
}
 defer listen.Close()

for {<!-- -->
conn, err := listen.Accept()
if err != nil {<!-- -->
continue
}
go handleConnection(conn) // Use new goroutine for each connection to improve concurrency
}
}

func handleConnection(conn net.Conn) {<!-- -->
defer conn.Close()
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {<!-- -->
return
}
msg := string(buf[:n])
fmt.Println("Received message:", msg)
//Send data to client
conn.Write([]byte("Server response"))
}

We can use the telnet tool to connect to the server

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello, server!
Server response

The client sends a message to the server, and the server receives the message and sends a response.

Client:

func main() {<!-- -->
//Connect to port 8080
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {<!-- -->
fmt.Println(err)
return
}
defer conn.Close()

//Send data to server
conn.Write([]byte("Hello, server!"))
buffer := make([]byte, 1024)
\t
//Read the server's response information
conn.Read(buffer)
fmt.Println(string(buffer))
}

2. UDP server and client

Unlike TCP, UDP is connectionless. Below is the implementation of UDP server and client.
Server:

package main

import (
 "net"
 "fmt"
)

func main() {<!-- -->
conn, err := net.ListenPacket("udp", ":8080")
if err != nil {<!-- -->
fmt.Println(err)
return
}
defer conn.Close()

buffer := make([]byte, 1024)
// read data
n, addr, err := conn.ReadFrom(buffer)
if err != nil {<!-- -->
fmt.Println(err)
return
}
fmt.Println("Received: ", string(buffer[:n]))
//Send data to the specified address
conn.WriteTo([]byte("Message received!"), addr)
}

Client:

func main() {<!-- -->
addr, err := net.ResolveUDPAddr("udp", "localhost:8080")
if err != nil {<!-- -->
fmt.Println(err)
return
}
\t
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {<!-- -->
return
}
defer conn.Close()

// Write a message to the server.
conn.Write([]byte("Hello, server!"))
buffer := make([]byte, 1024)
// Read the response from the server.
conn.Read(buffer)
fmt.Println(string(buffer))
}

The server reads messages from any client and sends responses. The client sends the message and waits for a response.

3. HTTP Programming

package main

import (
    "fmt"
    "net/http"
)

func main() {<!-- -->
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {<!-- -->
        fmt.Fprintf(w, "Hello, client!")
    })
    err := http.ListenAndServe(":1234", nil)
    if err != nil {<!-- -->
        fmt.Println("Error:", err)
        return
    }
}

Use the curl tool to send an HTTP request to the server:

$ curl http://localhost:1234
Hello, client!

Gorilla Mux library simplifies HTTP request routing:

package main

import (
 "fmt"
 "github.com/gorilla/mux"
 "net/http"
)

func main() {<!-- -->
r := mux.NewRouter()
r.HandleFunc("/", homeHandler)
http.ListenAndServe(":8080", r)
}

func homeHandler(w http.ResponseWriter, r *http.Request) {<!-- -->
fmt.Fprint(w, "Welcome to Home!")
}

4. HTTPS server

package main

import (
 "net/http"
 "log"
)

func main() {<!-- -->
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {<!-- -->
w.Write([]byte("Hello, this is an HTTPS server!"))
})
// Use the cert.pem and key.pem files to secure the server.
http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil)
}

Implementing an HTTPS server ensures secure communication. The server uses TLS (Transport Layer Security) to encrypt communications.

5. WebSocket communication

Using the Gorilla websocket library WebSockets provide real-time, full-duplex communication over a single connection.

package main

import (
 "github.com/gorilla/websocket"
 "net/http"
)

var upgrader = websocket.Upgrader{<!-- -->
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}

func handler(w http.ResponseWriter, r *http.Request) {<!-- -->
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {<!-- -->
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
return
}
defer conn.Close()

for {<!-- -->
messageType, p, err := conn.ReadMessage()
if err != nil {<!-- -->
return
}
conn.WriteMessage(messageType, p)
}
}

func main() {<!-- -->
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

6. Connection timeout

Connection timeouts can be managed using the context package.

package main

import (
 "context"
 "fmt"
 "net"
 "time"
)

func main() {<!-- -->
// Create a context with a timeout of 2 seconds
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// Dialer using the context
dialer := net.Dialer{<!-- -->}
conn, err := dialer.DialContext(ctx, "tcp", "localhost:8080")
if err != nil {<!-- -->
panic(err)
}

buffer := make([]byte, 1024)
_, err = conn.Read(buffer)
if err == nil {<!-- -->
fmt.Println("Received:", string(buffer))
} else {<!-- -->
fmt.Println("Connection error:", err)
}
}

7. Rate Limiting

Use golang.org/x/time/rate for rate limiting

package main

import (
 "golang.org/x/time/rate"
 "net/http"
 "time"
)

var limiter = rate.NewLimiter(2, 5)

func handler(w http.ResponseWriter, r *http.Request) {<!-- -->
if !limiter.Allow() {<!-- -->
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
w.Write([]byte("Welcome!"))
}

func main() {<!-- -->
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

This example uses a rate limiter to limit the request rate to two requests per second with a burst capacity of five.