Networking | Go - Wyatt's Notes
TCP/UDP
Section titled “TCP/UDP”TCP Connections
Section titled “TCP Connections”The net package provides TCP support via net.Dial and net.Listen:
// Clientconn, err := net.Dial("tcp", "localhost:8080")if err != nil { log.Fatal(err)}defer conn.Close()
_, err = conn.Write([]byte("hello"))if err != nil { log.Fatal(err)}
buf := make([]byte, 1024)n, err := conn.Read(buf)fmt.Println(string(buf[:n]))// Serverlistener, err := net.Listen("tcp", ":8080")if err != nil { log.Fatal(err)}defer listener.Close()
for { conn, err := listener.Accept() if err != nil { log.Println(err) continue } go handleConnection(conn)}Timeouts and Deadlines
Section titled “Timeouts and Deadlines”Always set timeouts. Without them, reads and writes can block indefinitely:
conn, err := net.DialTimeout("tcp", "localhost:8080", 5*time.Second)
conn.SetReadDeadline(time.Now().Add(10 * time.Second))conn.SetWriteDeadline(time.Now().Add(10 * time.Second))UDP Packets
Section titled “UDP Packets”addr, err := net.ResolveUDPAddr("udp", "localhost:9090")conn, err := net.DialUDP("udp", nil, addr)
conn.Write([]byte("hello"))buf := make([]byte, 1024)n, _, err := conn.ReadFromUDP(buf)HTTP Client
Section titled “HTTP Client”Advanced http.Client
Section titled “Advanced http.Client”client := &http.Client{ Timeout: 30 * time.Second, Transport: &http.Transport{ MaxIdleConns: 100, MaxIdleConnsPerHost: 10, IdleConnTimeout: 90 * time.Second, TLSClientConfig: &tls.Config{ MinVersion: tls.VersionTLS12, }, },}Timeouts
Section titled “Timeouts”Three separate timeout controls:
client := &http.Client{ Timeout: 30 * time.Second, // overall request timeout Transport: &http.Transport{ DialContext: (&net.Dialer{ Timeout: 5 * time.Second, // connection timeout }).DialContext, TLSHandshakeTimeout: 10 * time.Second, // TLS handshake ResponseHeaderTimeout: 10 * time.Second, // first response byte },}Redirects and Cookies
Section titled “Redirects and Cookies”client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { if len(via) >= 5 { return fmt.Errorf("stopped after 5 redirects") } return nil }, Jar: http.CookieJar(nil),}HTTP Server
Section titled “HTTP Server”Advanced Routing
Section titled “Advanced Routing”mux := http.NewServeMux()mux.HandleFunc("/users/", usersHandler)mux.HandleFunc("/users/create", createUserHandler)mux.HandleFunc("/health", healthHandler)
srv := &http.Server{ Addr: ":8080", Handler: mux,}srv.ListenAndServe()Note: ServeMux uses longest-prefix matching. /users/ matches /users/123 but /users matches only exactly /users.
Middleware Chains
Section titled “Middleware Chains”type Middleware func(http.Handler) http.Handler
func Logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start)) })}
func Auth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token == "" { http.Error(w, "unauthorized", http.StatusUnauthorized) return } next.ServeHTTP(w, r) })}
func Chain(h http.Handler, mws ...Middleware) http.Handler { for i := len(mws) - 1; i >= 0; i-- { h = mws[i](h) } return h}
handler := Chain(mux, Logging, Auth)Graceful Shutdown
Section titled “Graceful Shutdown”srv := &http.Server{Addr: ":8080", Handler: mux}
go func() { sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() srv.Shutdown(ctx)}()
srv.ListenAndServe()Context Propagation
Section titled “Context Propagation”Always propagate context.Context through handlers:
func handler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel()
data, err := fetchData(ctx) // ...}WebSockets
Section titled “WebSockets”Using gorilla/websocket:
var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true },}
func wsHandler(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } defer conn.Close()
for { msgType, msg, err := conn.ReadMessage() if err != nil { break } err = conn.WriteMessage(msgType, msg) if err != nil { break } }}Ping/Pong
Section titled “Ping/Pong”func readPump(conn *websocket.Conn) { defer conn.Close() conn.SetReadLimit(512) conn.SetReadDeadline(time.Now().Add(60 * time.Second)) conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(60 * time.Second)) return nil }) for { _, _, err := conn.ReadMessage() if err != nil { return } }}
func writePump(conn *websocket.Conn) { ticker := time.NewTicker(54 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil { return } } }}Protocol Buffers
Section titled “Protocol Buffers”service UserService { rpc GetUser (GetUserRequest) returns (UserResponse); rpc CreateUser (CreateUserRequest) returns (UserResponse);}
message GetUserRequest { string id = 1;}
message UserResponse { string id = 1; string name = 2; string email = 3;}Server Implementation
Section titled “Server Implementation”type server struct { pb.UnimplementedUserServiceServer repo Repository}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.UserResponse, error) { user, err := s.repo.GetByID(ctx, req.Id) if err != nil { return nil, status.Errorf(codes.NotFound, "user %q not found", req.Id) } return &pb.UserResponse{Id: user.ID, Name: user.Name, Email: user.Email}, nil}Client
Section titled “Client”conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithTimeout(5*time.Second),)defer conn.Close()
client := pb.NewUserServiceClient(conn)resp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: "123"})Custom DNS Lookups
Section titled “Custom DNS Lookups”resolver := &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { d := net.Dialer{Timeout: 5 * time.Second} return d.DialContext(ctx, "udp", "8.8.8.8:53") },}
ips, err := resolver.LookupHost(ctx, "example.com")Reverse Lookup
Section titled “Reverse Lookup”names, err := net.LookupAddr("93.184.216.34")TLS/SSL
Section titled “TLS/SSL”Basic TLS Server
Section titled “Basic TLS Server”cert, err := tls.LoadX509KeyPair("server.crt", "server.key")if err != nil { log.Fatal(err)}
config := &tls.Config{ Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS12, CurvePreferences: []tls.CurveID{ tls.X25519, tls.CurveP256, },}
srv := &http.Server{ Addr: ":443", Handler: mux, TLSConfig: config,}srv.ListenAndServeTLS("", "")Mutual TLS (mTLS)
Section titled “Mutual TLS (mTLS)”caCert, _ := os.ReadFile("ca.crt")caPool := x509.NewCertPool()caPool.AppendCertsFromPEM(caCert)
config := &tls.Config{ ClientCAs: caPool, ClientAuth: tls.RequireAndVerifyClientCert, MinVersion: tls.VersionTLS12,}TLS Client with Custom CA
Section titled “TLS Client with Custom CA”caCert, _ := os.ReadFile("ca.crt")caPool := x509.NewCertPool()caPool.AppendCertsFromPEM(caCert)
client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: caPool, MinVersion: tls.VersionTLS12, }, },}Common Pitfalls
Section titled “Common Pitfalls”No timeouts on connections. TCP connections without deadlines block indefinitely. Always set read/write deadlines or use
DialTimeout.Response body not closed.
resp.Bodymust always be closed withdefer resp.Body.Close(). Forgetting this leaks connections and exhausts file descriptors.Default transport in high-throughput servers. The default
http.Transporthas conservative connection pool limits. TuneMaxIdleConnsPerHostfor your workload.Not propagating context. Handler code that calls external services without context cannot be cancelled on client disconnect. Always use
r.Context().Insecure TLS in production.
insecure.NewCredentials()is for development only. Production clients must use proper CA verification.WebSocket origin check disabled.
CheckOrigin: func(r *http.Request) bool { return true }is convenient but opens the server to cross-site WebSocket hijacking. Validate origins in production.HTTP/2 server misconfiguration.
http.Serverautomatically supports HTTP/2 when usingListenAndServeTLS. Do not configure HTTP/2 viah2package separately unless you have a specific reason.
flowchart TD
A[Networking] --> B[Key Concepts]
A --> C[Core Principles]
A --> D[Practical Applications]
B --> E[Fundamental definitions]
C --> F[Design patterns]
D --> G[Real-world usage]Summary
Section titled “Summary”net.Dial/net.Listenhandle TCP;net.DialUDPhandles UDP. Always set deadlines.http.Clientsupports configurable timeouts, connection pooling, redirect policies, and TLS.http.ServeMuxprovides longest-prefix routing; middleware chains wrap handlers.- Graceful shutdown via
srv.Shutdown(ctx)drains in-flight requests on signal. gorilla/websockethandles upgrade, ping/pong keepalives, and message framing.- gRPC uses protocol buffers for schema and
grpc.Dial/grpc.NewServerfor implementation. net.ResolverwithPreferGo: truebypasses the system resolver for custom DNS.crypto/tlshandles server certs, client certs (mTLS), and custom CA pools.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.
Intuition
Section titled “Intuition”Networking in Go is built on the net package which provides low-level TCP and UDP primitives, and the net/http package which layers HTTP semantics on top. TCP connections are persistent bidirectional streams — you always set timeouts to prevent goroutines from blocking forever on a dead connection. The HTTP server uses a multiplexer that routes requests to handlers based on URL patterns, and middleware wraps handlers in a chain to add cross-cutting behavior like logging or authentication. Context propagation lets you cancel in-flight work when a client disconnects. WebSockets upgrade a standard HTTP connection into a persistent bidirectional channel for real-time communication.
Cross-References
Section titled “Cross-References”- Functions — first-class functions and closures used in middleware
- Goroutines and Synchronization — lightweight threads for handling connections
- Channels and Concurrency Patterns — fan-out, fan-in, and pipelines