mirror of
https://github.com/nabbar/golib.git
synced 2025-12-24 11:51:02 +08:00
[file/bandwidth] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [file/perm] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases - ADD function to parse form "rwx-wxr-x" or "-rwx-w-r-x" - ADD function to ParseFileMode to convert os.FileMode to file.Perm [file/progress] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [ioutils/...] - UPDATE documentation: update enhanced README and TESTING guidelines - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases [logger/...] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - UPDATE config: remove FileBufferSize from OptionFile (rework hookfile) - UPDATE fields: expose Store function in interface - REWORK hookfile: rework package, use aggregator to allow multi write and single file - FIX hookstderr: fix bug with NonColorable - FIX hookstdout: fix bug with NonColorable - FIX hookwriter: fix bug with NonColorable [network/protocol] - ADD function IsTCP, IsUDP, IsUnixLike to check type of protocol [runner] - FIX typo [socket] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - REWORK server: use context compatible io.reader, io.writer, io.closer instead of reader / writer - REWORK server: simplify, optimize server - REMOVE reader, writer type - ADD context: add new interface in root socket interface to expose context interface that extend context, io reader/writer/closer, dediacted function to server (IsConnected, ...)
448 lines
19 KiB
Go
448 lines
19 KiB
Go
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2025 Nicolas JUHEL
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
// Package udp provides a UDP server implementation with connectionless datagram support.
|
|
//
|
|
// # Overview
|
|
//
|
|
// This package implements the github.com/nabbar/golib/socket.Server interface
|
|
// for the UDP protocol, providing a stateless datagram server with features including:
|
|
// - Connectionless UDP datagram handling
|
|
// - Single handler for all incoming datagrams
|
|
// - Callback hooks for errors and informational messages
|
|
// - Graceful shutdown support
|
|
// - Atomic state management for thread safety
|
|
// - Context-aware operations
|
|
// - Optional socket configuration via UpdateConn callback
|
|
//
|
|
// Unlike TCP servers which maintain persistent connections, UDP servers operate
|
|
// in a stateless mode where each datagram is processed independently without
|
|
// maintaining connection state. This makes UDP ideal for scenarios requiring
|
|
// low latency, multicast/broadcast communication, or where connection overhead
|
|
// is undesirable.
|
|
//
|
|
// # Design Philosophy
|
|
//
|
|
// The package follows these core design principles:
|
|
//
|
|
// 1. **Stateless by Design**: No per-client connection tracking, minimal memory footprint
|
|
// 2. **Thread Safety**: All operations use atomic primitives and are safe for concurrent use
|
|
// 3. **Context Integration**: Full support for context-based cancellation and deadlines
|
|
// 4. **Callback-Based**: Flexible event notification through registered callbacks
|
|
// 5. **Standard Compliance**: Implements standard socket.Server interface
|
|
// 6. **Zero Dependencies**: Only standard library and golib packages
|
|
//
|
|
// # Architecture
|
|
//
|
|
// ## Component Structure
|
|
//
|
|
// ┌─────────────────────────────────────────────────────────────┐
|
|
// │ ServerUdp Interface │
|
|
// │ (extends socket.Server interface) │
|
|
// └────────────────────────┬────────────────────────────────────┘
|
|
// │
|
|
// │ implements
|
|
// │
|
|
// ┌────────────────────────▼────────────────────────────────────┐
|
|
// │ srv (internal struct) │
|
|
// │ ┌─────────────────────────────────────────────────────────┐ │
|
|
// │ │ Atomic Fields: │ │
|
|
// │ │ - run: Server running state (atomic.Bool) │ │
|
|
// │ │ - gon: Server shutdown state (atomic.Bool) │ │
|
|
// │ │ - ad: Listen address (libatm.Value[string]) │ │
|
|
// │ │ - fe: Error callback (libatm.Value[FuncError]) │ │
|
|
// │ │ - fi: Info callback (libatm.Value[FuncInfo]) │ │
|
|
// │ │ - fs: Server info callback (libatm.Value[FuncInfoSrv]) │ │
|
|
// │ └─────────────────────────────────────────────────────────┘ │
|
|
// │ ┌─────────────────────────────────────────────────────────┐ │
|
|
// │ │ Callbacks (immutable after construction): │ │
|
|
// │ │ - upd: UpdateConn callback (socket setup) │ │
|
|
// │ │ - hdl: HandlerFunc (datagram processor) │ │
|
|
// │ └─────────────────────────────────────────────────────────┘ │
|
|
// └─────────────────────────────────────────────────────────────┘
|
|
// │
|
|
// │ uses
|
|
// │
|
|
// ┌────────────────────────▼────────────────────────────────────┐
|
|
// │ sCtx (context wrapper) │
|
|
// │ ┌─────────────────────────────────────────────────────────┐ │
|
|
// │ │ Fields: │ │
|
|
// │ │ - ctx: Parent context (cancellation) │ │
|
|
// │ │ - cnl: Cancel function │ │
|
|
// │ │ - con: *net.UDPConn (UDP socket) │ │
|
|
// │ │ - clo: Closed state (atomic.Bool) │ │
|
|
// │ │ - loc: Local address string │ │
|
|
// │ └─────────────────────────────────────────────────────────┘ │
|
|
// │ Implements: context.Context, io.ReadCloser, io.Writer │
|
|
// └─────────────────────────────────────────────────────────────┘
|
|
//
|
|
// ## Data Flow
|
|
//
|
|
// The server follows this execution flow:
|
|
//
|
|
// 1. New() creates server instance with handler and optional UpdateConn
|
|
// ↓
|
|
// 2. RegisterServer() sets the listen address
|
|
// ↓
|
|
// 3. Listen() called:
|
|
// a. Creates UDP socket (net.ListenUDP)
|
|
// b. Calls UpdateConn callback (if provided)
|
|
// c. Wraps socket in sCtx (context wrapper)
|
|
// d. Sets server to running state
|
|
// e. Starts handler goroutine
|
|
// f. Waits for shutdown or context cancellation
|
|
// ↓
|
|
// 4. Handler goroutine:
|
|
// - Calls HandlerFunc with sCtx
|
|
// - sCtx provides Read/Write for datagram I/O
|
|
// - Runs until context cancelled
|
|
// ↓
|
|
// 5. Shutdown() or context cancellation:
|
|
// - Sets gon (shutdown) flag
|
|
// - Waits for handler to complete
|
|
// - Closes UDP socket
|
|
// - Cleans up resources
|
|
// - Returns from Listen()
|
|
//
|
|
// ## State Machine
|
|
//
|
|
// ┌─────────┐ New() ┌─────────────┐
|
|
// │ Start │───────────────▶│ Created │
|
|
// └─────────┘ └──────┬──────┘
|
|
// │ RegisterServer()
|
|
// │
|
|
// ┌──────▼──────┐
|
|
// │ Configured │
|
|
// └──────┬──────┘
|
|
// │ Listen()
|
|
// │
|
|
// ┌──────▼──────┐
|
|
// │ Running │◀────┐
|
|
// │ (IsRunning) │ │ still running
|
|
// └──────┬──────┘ │
|
|
// │ │
|
|
// │ Shutdown() │
|
|
// │ │
|
|
// ┌──────▼──────┐ │
|
|
// │ Draining │─────┘
|
|
// │ (IsGone) │
|
|
// └──────┬──────┘
|
|
// │ all cleaned
|
|
// │
|
|
// ┌──────▼──────┐
|
|
// │ Stopped │
|
|
// └─────────────┘
|
|
//
|
|
// # Key Features
|
|
//
|
|
// ## Connectionless Operation
|
|
//
|
|
// UDP is fundamentally connectionless. Unlike TCP:
|
|
// - No handshake or connection establishment
|
|
// - No persistent connection state
|
|
// - Each datagram is independent
|
|
// - No guarantee of delivery or ordering
|
|
// - Lower latency, less overhead
|
|
//
|
|
// The server reflects this by:
|
|
// - OpenConnections() returns 1 when running, 0 when stopped
|
|
// - No per-client connection tracking
|
|
// - Single handler processes all datagrams
|
|
// - No connection lifecycle events (New/Close)
|
|
//
|
|
// ## Thread Safety
|
|
//
|
|
// All mutable state uses atomic operations:
|
|
// - run: Atomic boolean for running state
|
|
// - gon: Atomic boolean for shutdown state
|
|
// - ad: Atomic value for address
|
|
// - fe, fi, fs: Atomic values for callbacks
|
|
//
|
|
// This ensures thread-safe access without locks, allowing:
|
|
// - Concurrent calls to IsRunning(), IsGone(), OpenConnections()
|
|
// - Safe callback registration while server is running
|
|
// - Safe shutdown from any goroutine
|
|
//
|
|
// ## Context Integration
|
|
//
|
|
// The server fully supports Go's context.Context:
|
|
// - Listen() accepts context for cancellation
|
|
// - Context cancellation triggers immediate shutdown
|
|
// - sCtx implements context.Context interface
|
|
// - Deadline and value propagation through context chain
|
|
//
|
|
// ## Callback System
|
|
//
|
|
// Three types of callbacks for event notification:
|
|
//
|
|
// 1. **FuncError**: Error notifications
|
|
// - Called on any error during operation
|
|
// - Receives variadic errors
|
|
// - Should not block
|
|
//
|
|
// 2. **FuncInfo**: Datagram events
|
|
// - Called for Read/Write events
|
|
// - Receives local addr, remote addr, state
|
|
// - Useful for monitoring and logging
|
|
//
|
|
// 3. **FuncInfoSrv**: Server lifecycle
|
|
// - Called for server state changes
|
|
// - Receives formatted string messages
|
|
// - Useful for startup/shutdown logging
|
|
//
|
|
// # Performance Characteristics
|
|
//
|
|
// ## Memory Usage
|
|
//
|
|
// Base overhead: ~500 bytes (struct + atomics)
|
|
// Per UDP socket: OS-dependent (~4KB typical)
|
|
// Total idle: ~5KB
|
|
//
|
|
// Since UDP is connectionless, memory usage is constant regardless of
|
|
// traffic volume (assuming handler doesn't accumulate state).
|
|
//
|
|
// ## Throughput
|
|
//
|
|
// UDP throughput is primarily limited by:
|
|
// - Network bandwidth
|
|
// - Handler processing speed
|
|
// - OS socket buffer size
|
|
//
|
|
// The package itself adds minimal overhead (<1% typical).
|
|
//
|
|
// ## Latency
|
|
//
|
|
// Operation latencies (typical):
|
|
// - New(): ~1µs (struct allocation)
|
|
// - RegisterServer(): ~10µs (address resolution)
|
|
// - Listen() startup: ~1-5ms (socket creation)
|
|
// - Shutdown(): ~1-10ms (cleanup)
|
|
// - Datagram handling: ~100ns (handler overhead)
|
|
//
|
|
// # Limitations and Trade-offs
|
|
//
|
|
// ## Protocol Limitations (UDP inherent)
|
|
//
|
|
// 1. **No Reliability**: Datagrams may be lost, duplicated, or reordered
|
|
// - Workaround: Implement application-level acknowledgments
|
|
//
|
|
// 2. **No Flow Control**: No backpressure mechanism
|
|
// - Workaround: Application-level rate limiting
|
|
//
|
|
// 3. **No Congestion Control**: Can overwhelm network
|
|
// - Workaround: Application-level bandwidth management
|
|
//
|
|
// 4. **Limited Datagram Size**: Typically 65,507 bytes max (IPv4)
|
|
// - Workaround: Fragment at application level
|
|
//
|
|
// 5. **No Encryption**: UDP has no native encryption (unlike TLS for TCP)
|
|
// - Workaround: Use DTLS (not implemented) or application-level encryption
|
|
// - Note: SetTLS() is a no-op for UDP servers
|
|
//
|
|
// ## Implementation Limitations
|
|
//
|
|
// 1. **Single Handler**: One handler for all datagrams
|
|
// - Cannot dispatch based on remote address
|
|
// - Handler must multiplex internally if needed
|
|
//
|
|
// 2. **No Connection Events**: ConnectionNew/ConnectionClose not fired
|
|
// - UDP is stateless, no connection concept
|
|
// - Only ConnectionRead/ConnectionWrite events
|
|
//
|
|
// 3. **No Per-Client State**: Server maintains no client information
|
|
// - Handler must track state if needed
|
|
// - RemoteAddr() available per datagram only
|
|
//
|
|
// 4. **No TLS Support**: SetTLS() always returns nil (no-op)
|
|
// - UDP does not support TLS
|
|
// - Use DTLS externally if encryption needed
|
|
//
|
|
// # Use Cases
|
|
//
|
|
// UDP servers are ideal for:
|
|
//
|
|
// ## Real-time Applications
|
|
// - Gaming servers (low latency critical)
|
|
// - Voice/video streaming (occasional loss acceptable)
|
|
// - Live sports updates
|
|
// - Real-time sensor data
|
|
//
|
|
// ## Broadcast/Multicast
|
|
// - Service discovery protocols
|
|
// - Network monitoring
|
|
// - Live event distribution
|
|
//
|
|
// ## High-Frequency Data
|
|
// - Time synchronization (NTP)
|
|
// - Network time distribution
|
|
// - Metrics collection
|
|
// - Log aggregation
|
|
//
|
|
// ## Request-Response Protocols
|
|
// - DNS queries
|
|
// - SNMP monitoring
|
|
// - DHCP configuration
|
|
// - Lightweight RPC
|
|
//
|
|
// # Best Practices
|
|
//
|
|
// ## Handler Implementation
|
|
//
|
|
// // Good: Non-blocking, stateless handler
|
|
// handler := func(ctx socket.Context) {
|
|
// buf := make([]byte, 65507) // Max UDP datagram
|
|
// for {
|
|
// n, err := ctx.Read(buf)
|
|
// if err != nil {
|
|
// return // Exit on error or closure
|
|
// }
|
|
//
|
|
// // Process datagram
|
|
// response := process(buf[:n])
|
|
//
|
|
// // Send response (optional)
|
|
// ctx.Write(response)
|
|
// }
|
|
// }
|
|
//
|
|
// ## Error Handling
|
|
//
|
|
// // Register error callback for logging
|
|
// srv.RegisterFuncError(func(errs ...error) {
|
|
// for _, err := range errs {
|
|
// if err != nil {
|
|
// log.Printf("UDP error: %v", err)
|
|
// }
|
|
// }
|
|
// })
|
|
//
|
|
// ## Graceful Shutdown
|
|
//
|
|
// // Use context for clean shutdown
|
|
// ctx, cancel := context.WithCancel(context.Background())
|
|
// defer cancel()
|
|
//
|
|
// // Start server in goroutine
|
|
// go func() {
|
|
// if err := srv.Listen(ctx); err != nil {
|
|
// log.Printf("Server error: %v", err)
|
|
// }
|
|
// }()
|
|
//
|
|
// // Shutdown on signal
|
|
// <-sigChan
|
|
// cancel() // Triggers graceful shutdown
|
|
//
|
|
// // Wait with timeout
|
|
// shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
// defer shutdownCancel()
|
|
// srv.Shutdown(shutdownCtx)
|
|
//
|
|
// ## Socket Configuration
|
|
//
|
|
// // Use UpdateConn to set socket options
|
|
// updateFn := func(conn net.Conn) {
|
|
// if udpConn, ok := conn.(*net.UDPConn); ok {
|
|
// // Set read buffer size
|
|
// udpConn.SetReadBuffer(1024 * 1024) // 1MB
|
|
//
|
|
// // Set write buffer size
|
|
// udpConn.SetWriteBuffer(1024 * 1024)
|
|
// }
|
|
// }
|
|
//
|
|
// srv := udp.New(updateFn, handler, cfg)
|
|
//
|
|
// # Comparison with TCP Server
|
|
//
|
|
// ┌─────────────────────┬──────────────────┬──────────────────┐
|
|
// │ Feature │ UDP Server │ TCP Server │
|
|
// ├─────────────────────┼──────────────────┼──────────────────┤
|
|
// │ Connection Model │ Connectionless │ Connection-based │
|
|
// │ Reliability │ None │ Guaranteed │
|
|
// │ Ordering │ Not guaranteed │ Guaranteed │
|
|
// │ Flow Control │ None │ Yes (TCP) │
|
|
// │ Congestion Control │ None │ Yes (TCP) │
|
|
// │ Handshake │ None │ 3-way handshake │
|
|
// │ Per-client state │ No │ Yes │
|
|
// │ OpenConnections() │ 0 or 1 │ Actual count │
|
|
// │ TLS Support │ No (no-op) │ Yes │
|
|
// │ Latency │ Lower │ Higher │
|
|
// │ Overhead │ Minimal │ Higher │
|
|
// │ Multicast │ Supported │ Not supported │
|
|
// │ Use Cases │ Real-time, IoT │ Reliable transfer│
|
|
// └─────────────────────┴──────────────────┴──────────────────┘
|
|
//
|
|
// # Error Handling
|
|
//
|
|
// The package defines these specific errors:
|
|
//
|
|
// - ErrInvalidAddress: Empty or malformed listen address
|
|
// - ErrInvalidHandler: Handler function is nil
|
|
// - ErrShutdownTimeout: Shutdown exceeded context timeout
|
|
// - ErrInvalidInstance: Operation on nil server instance
|
|
//
|
|
// All errors are logged via the registered FuncError callback if set.
|
|
//
|
|
// # Thread Safety
|
|
//
|
|
// **Concurrent-safe operations:**
|
|
// - IsRunning(), IsGone(), OpenConnections(): Always safe
|
|
// - RegisterFuncError/Info/InfoServer(): Safe at any time
|
|
// - RegisterServer(): Safe before Listen() only
|
|
// - Shutdown(), Close(): Safe from any goroutine
|
|
//
|
|
// **Not concurrent-safe:**
|
|
// - Multiple Listen() calls: Will fail with error
|
|
// - RegisterServer() during Listen(): Ignored
|
|
//
|
|
// # Examples
|
|
//
|
|
// See example_test.go for comprehensive usage examples including:
|
|
// - Basic UDP echo server
|
|
// - Server with callbacks
|
|
// - Socket configuration
|
|
// - Graceful shutdown
|
|
// - Error handling
|
|
// - Integration with config package
|
|
//
|
|
// # See Also
|
|
//
|
|
// - github.com/nabbar/golib/socket - Base interfaces and types
|
|
// - github.com/nabbar/golib/socket/config - Configuration builder
|
|
// - github.com/nabbar/golib/socket/server/tcp - TCP server implementation
|
|
// - github.com/nabbar/golib/socket/client/udp - UDP client implementation
|
|
// - github.com/nabbar/golib/network/protocol - Network protocol definitions
|
|
//
|
|
// # Package Status
|
|
//
|
|
// This package is production-ready and stable. It has been tested in various
|
|
// production environments handling millions of datagrams.
|
|
//
|
|
// For security vulnerabilities, please report privately via GitHub Security
|
|
// Advisories rather than public issues.
|
|
package udp
|