Documentation
¶
Overview ¶
Package udp provides a UDP client implementation with callback mechanisms for datagram communication.
Overview ¶
This package implements the github.com/nabbar/golib/socket.Client interface for the UDP protocol, providing a connectionless datagram client with features including:
- Connectionless UDP datagram sending and receiving
- Thread-safe state management using atomic operations
- Callback hooks for errors and informational messages
- Context-aware operations
- One-shot request/response operation support
- No TLS support (UDP doesn't support TLS natively; use DTLS if encryption is required)
Unlike TCP clients which maintain persistent connections with handshakes, UDP clients operate in a connectionless mode where each datagram is independent. 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. **Connectionless by Nature**: No persistent connection state, minimal overhead 2. **Thread Safety**: All operations use atomic primitives and are safe for concurrent use 3. **Callback-Based**: Flexible event notification through registered callbacks 4. **Context Integration**: Full support for context-based cancellation and deadlines 5. **Standard Compliance**: Implements standard socket.Client interface 6. **Zero Dependencies**: Only standard library and golib packages
Architecture ¶
## Component Structure
┌─────────────────────────────────────────────────────────────┐
│ ClientUDP Interface │
│ (extends socket.Client interface) │
└────────────────────────┬────────────────────────────────────┘
│
│ implements
│
┌────────────────────────▼────────────────────────────────────┐
│ cli (internal struct) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Atomic Map (libatm.Map[uint8]): │ │
│ │ - keyNetAddr: Remote address (string) │ │
│ │ - keyFctErr: Error callback (libsck.FuncError) │ │
│ │ - keyFctInfo: Info callback (libsck.FuncInfo) │ │
│ │ - keyNetConn: Active UDP socket (net.Conn) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ uses
│
┌────────────────────────▼────────────────────────────────────┐
│ *net.UDPConn │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Standard library UDP socket: │ │
│ │ - Read([]byte) (n int, err error) │ │
│ │ - Write([]byte) (n int, err error) │ │
│ │ - Close() error │ │
│ │ - LocalAddr() net.Addr │ │
│ │ - RemoteAddr() net.Addr │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
## Data Flow
The client follows this execution flow:
- New(address) creates client instance with remote address ↓
- RegisterFuncError/RegisterFuncInfo (optional) sets callbacks ↓
- Connect(ctx) called: a. Creates UDP socket using net.Dialer b. Associates socket with remote address c. Stores socket in atomic map d. Triggers ConnectionDial and ConnectionNew callbacks ↓
- Read/Write operations: - Write() sends complete datagram to remote address - Read() receives complete datagram from socket - Triggers ConnectionRead/ConnectionWrite callbacks ↓
- Close() or Once() completion: - Triggers ConnectionClose callback - Closes UDP socket - Removes socket from state
## State Machine
┌─────────┐ New() ┌─────────────┐
│ Start │───────────────▶│ Created │
└─────────┘ └──────┬──────┘
│ (optional)
│ RegisterFunc*
┌──────▼──────┐
│ Configured │
└──────┬──────┘
│ Connect()
│
┌──────▼──────┐
│ Associated │◀────┐
│(IsConnected)│ │ Read/Write
└──────┬──────┘ │
│ │
├────────────┘
│
│ Close()
┌──────▼──────┐
│ Closed │
└─────────────┘
Key Features ¶
## Connectionless Operation
UDP is fundamentally connectionless. Unlike TCP:
- No handshake or connection establishment
- No persistent connection state tracking
- Each datagram is independent
- No guarantee of delivery, ordering, or duplicate prevention
- Lower latency and overhead
The client reflects this by:
- Connect() only associates the socket with a remote address
- Write() sends one complete datagram per call
- Read() receives one complete datagram per call
- No connection lifecycle beyond socket creation/destruction
## Thread Safety
All mutable state uses atomic operations:
- Atomic map (libatm.Map[uint8]) for all client state
- Safe concurrent access to all methods
- No locks required for external synchronization
This ensures thread-safe access, allowing:
- Concurrent calls to IsConnected() from multiple goroutines
- Safe callback registration while operations are in progress
- Safe Close() from any goroutine
Important: While the client methods are thread-safe, the underlying UDP socket is NOT safe for concurrent Read() or concurrent Write() calls. Avoid calling Read() from multiple goroutines or Write() from multiple goroutines simultaneously.
## Context Integration
The client fully supports Go's context.Context:
- Connect() accepts context for timeout and cancellation
- Once() accepts context for the entire operation
- Context cancellation triggers immediate operation abort
## Callback System
Two types of callbacks for event notification:
1. **FuncError**: Error notifications
- Called on any error during operations (asynchronously)
- Receives variadic errors
- Should not block (executed in separate goroutine)
2. **FuncInfo**: Datagram operation events
- Called for Connect/Read/Write/Close events (asynchronously)
- Receives local addr, remote addr, and state
- Useful for monitoring, logging, and debugging
- Should not block (executed in separate goroutine)
Performance Characteristics ¶
## Memory Usage
Base overhead: ~200 bytes (struct + atomic map) Per UDP socket: OS-dependent (~4KB typical) Total idle: ~4KB
Since UDP is connectionless, memory usage is constant regardless of datagram count (assuming no buffering in handler).
## Throughput
UDP throughput is primarily limited by:
- Network bandwidth
- Maximum datagram size (typically 1472 bytes to avoid fragmentation)
- OS socket buffer size
The package itself adds minimal overhead (<1% typical).
## Latency
Operation latencies (typical):
- New(): ~1µs (struct allocation)
- Connect(): ~100µs to 1ms (socket creation)
- Write(): ~10-100µs (datagram send)
- Read(): ~10-100µs (datagram receive, blocking until data arrives)
- Close(): ~100µs (socket cleanup)
Limitations and Trade-offs ¶
## Protocol Limitations (UDP inherent)
1. **No Reliability**: Datagrams may be lost without notification
- Workaround: Implement application-level acknowledgments
2. **No Ordering**: Datagrams may arrive out of order
- Workaround: Add sequence numbers at application level
3. **No Duplicate Prevention**: Same datagram may arrive multiple times
- Workaround: Implement deduplication using unique identifiers
4. **Limited Datagram Size**: Typically 65,507 bytes max (IPv4)
- Practical limit: 1472 bytes to avoid IP fragmentation (Ethernet MTU)
- Workaround: Fragment large messages 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 clients
6. **No Flow Control**: No backpressure mechanism
- Workaround: Implement application-level rate limiting
7. **No Congestion Control**: Can overwhelm network
- Workaround: Implement application-level bandwidth management
## Implementation Limitations
1. **No TLS Support**: SetTLS() always returns nil (no-op)
- UDP does not support TLS
- Use DTLS externally if encryption needed
2. **Single Remote Address**: Each client instance targets one remote address
- Cannot send to multiple destinations
- Create multiple client instances for multiple targets
3. **No Concurrent Read/Write**: Underlying socket not safe for concurrent I/O
- Don't call Read() from multiple goroutines
- Don't call Write() from multiple goroutines
- Different operations (Read + Write) are safe concurrently
4. **Fire-and-Forget Nature**: Write() success doesn't mean data was received
- Write() only confirms datagram was queued for sending
- No confirmation of remote receipt
Use Cases ¶
UDP clients are ideal for:
## Real-time Applications
- Gaming clients (low latency critical)
- Voice/video streaming (occasional loss acceptable)
- Live data feeds (latest data more important than old)
- Real-time sensor data transmission
## Request-Response Protocols
- DNS queries (single request, single response)
- SNMP monitoring (simple queries)
- DHCP client (configuration requests)
- Lightweight RPC systems
## Broadcast/Multicast
- Service discovery clients
- Network monitoring agents
- Event distribution subscribers
## High-Frequency Data
- Time synchronization clients (NTP)
- Metrics collection (StatsD-like)
- Log shipping (lossy acceptable)
Best Practices ¶
## Client Creation and Lifecycle
// Good: Create client with proper lifecycle
client, err := udp.New("server.example.com:8080")
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := client.Connect(ctx); err != nil {
log.Fatalf("Failed to connect: %v", err)
}
## Datagram Size Management
// Good: Keep datagrams small to avoid fragmentation
const maxSafeDatagramSize = 1400 // Well below 1472 byte Ethernet limit
data := []byte("payload data")
if len(data) > maxSafeDatagramSize {
// Fragment at application level
log.Warn("Datagram too large, will fragment")
}
n, err := client.Write(data)
if err != nil {
log.Errorf("Write failed: %v", err)
} else if n != len(data) {
log.Warn("Partial write")
}
## Error Handling
// Register error callback for centralized error logging
client.RegisterFuncError(func(errs ...error) {
for _, err := range errs {
if err != nil {
log.Printf("UDP client error: %v", err)
}
}
})
## One-Shot Operations
// Use Once() for simple request/response patterns
request := bytes.NewBufferString("QUERY")
err := client.Once(ctx, request, func(reader io.Reader) {
buf := make([]byte, 1500)
n, err := reader.Read(buf)
if err != nil {
log.Printf("Read error: %v", err)
return
}
log.Printf("Response: %s", buf[:n])
})
// Socket automatically closed
## Timeout Management
// Good: Use context timeouts for operations
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := client.Connect(ctx); err != nil {
if ctx.Err() == context.DeadlineExceeded {
log.Warn("Connection timeout")
}
return err
}
## Callback Registration
// Register callbacks for monitoring
client.RegisterFuncInfo(func(local, remote net.Addr, state socket.ConnState) {
log.Printf("UDP state: %v (local: %v, remote: %v)",
state.String(), local, remote)
})
Comparison with TCP Client ¶
┌─────────────────────┬──────────────────┬──────────────────┐ │ Feature │ UDP Client │ TCP Client │ ├─────────────────────┼──────────────────┼──────────────────┤ │ 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 │ │ Message Boundaries │ Preserved │ Stream-based │ │ TLS Support │ No (no-op) │ Yes │ │ Latency │ Lower │ Higher │ │ Overhead │ Minimal │ Higher │ │ Use Cases │ Real-time, IoT │ Reliable transfer│ └─────────────────────┴──────────────────┴──────────────────┘
Error Handling ¶
The package defines these specific errors:
- ErrAddress: Empty or malformed remote address in New()
- ErrConnection: Operation attempted without connection (call Connect() first)
- ErrInstance: Operation on nil client instance (programming error)
All errors are logged via the registered FuncError callback if set.
Thread Safety ¶
**Concurrent-safe operations:**
- IsConnected(): Always safe
- RegisterFuncError/Info(): Safe at any time
- Connect(): Safe, replaces existing socket if called multiple times
- Close(): Safe from any goroutine
**Not concurrent-safe (underlying socket limitation):**
- Multiple concurrent Read() calls: Not safe
- Multiple concurrent Write() calls: Not safe
- Concurrent Read() + Write(): Safe (different operations)
Examples ¶
See example_test.go for comprehensive usage examples including:
- Basic UDP client usage
- Client with callbacks
- One-shot request/response
- Error handling
- Context cancellation
- Datagram size management
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/udp - UDP server implementation
- github.com/nabbar/golib/socket/client/tcp - TCP 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.
Example (BasicClient) ¶
Example_basicClient demonstrates the simplest UDP client setup.
This example shows minimal configuration for a UDP client that sends a datagram to a remote server.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
// Create client
client, err := udp.New("localhost:8080")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// Connect (associate socket with remote address)
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Send datagram
data := []byte("Hello, UDP!")
n, err := client.Write(data)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Sent %d bytes\n", n)
}
Output: Sent 11 bytes
Example (CallbackOrdering) ¶
Example_callbackOrdering demonstrates callback execution order.
This example shows the order in which callbacks are triggered during client lifecycle operations.
package main
import (
"context"
"fmt"
"log"
"net"
"sync"
"time"
libsck "github.com/nabbar/golib/socket"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8093")
if err != nil {
log.Fatal(err)
}
defer client.Close()
var (
events []string
mu sync.Mutex
)
client.RegisterFuncInfo(func(local, remote net.Addr, state libsck.ConnState) {
mu.Lock()
events = append(events, state.String())
mu.Unlock()
})
ctx := context.Background()
_ = client.Connect(ctx)
// Allow callbacks to execute (they're async)
time.Sleep(50 * time.Millisecond)
// Note: Exact order may vary due to async execution
mu.Lock()
eventCount := len(events)
mu.Unlock()
fmt.Printf("Events captured: %d\n", eventCount)
}
Output: Events captured: 2
Example (ClientWithCallbacks) ¶
Example_clientWithCallbacks demonstrates callback registration.
This example shows how to register callbacks for error handling and operation monitoring.
package main
import (
"context"
"fmt"
"net"
libsck "github.com/nabbar/golib/socket"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8081")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// Register error callback
client.RegisterFuncError(func(errs ...error) {
for _, e := range errs {
if e != nil {
fmt.Printf("Client error: %v\n", e)
}
}
})
// Register info callback
client.RegisterFuncInfo(func(local, remote net.Addr, state libsck.ConnState) {
fmt.Printf("State: %s\n", state.String())
})
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Callbacks registered")
// Note: Callback state changes are asynchronous and output may vary
}
Example (ContextTimeout) ¶
Example_contextTimeout demonstrates context timeout handling.
This example shows how to use context timeouts to limit operation duration.
package main
import (
"context"
"fmt"
"time"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8084")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
if err := client.Connect(ctx); err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Connection timeout")
return
}
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Connected successfully")
}
Output: Connected successfully
Example (DatagramSizeManagement) ¶
Example_datagramSizeManagement demonstrates proper datagram sizing.
This example shows how to manage datagram sizes to avoid IP fragmentation and ensure reliable delivery.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8085")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Safe datagram size (well below 1472 byte Ethernet MTU limit)
const maxSafeSize = 1400
data := make([]byte, maxSafeSize)
for i := range data {
data[i] = byte(i % 256)
}
n, err := client.Write(data)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Sent datagram of %d bytes (safe size)\n", n)
}
Output: Sent datagram of 1400 bytes (safe size)
Example (EmptyWrite) ¶
Example_emptyWrite demonstrates writing empty datagrams.
This example shows that writing empty data is allowed and can be used as a keepalive or ping mechanism.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8092")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Write empty datagram
n, err := client.Write([]byte{})
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Sent empty datagram: %d bytes\n", n)
}
Output: Sent empty datagram: 0 bytes
Example (ErrorHandling) ¶
Example_errorHandling demonstrates comprehensive error handling.
This example shows how to handle various error scenarios including connection errors and I/O errors.
package main
import (
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
// Attempt to create client with invalid address
_, err := udp.New("")
if err != nil {
fmt.Printf("Creation error: %v\n", err)
}
// Valid client
client, err := udp.New("localhost:8086")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// Attempt operation before connection
_, err = client.Write([]byte("test"))
if err != nil {
fmt.Printf("Write error before connect: %v\n", err)
}
}
Output: Creation error: invalid dial address Write error before connect: invalid connection
Example (FireAndForget) ¶
Example_fireAndForget demonstrates fire-and-forget pattern.
This example shows sending datagrams without waiting for responses, which is a common UDP pattern.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8083")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Send multiple datagrams
messages := []string{"msg1", "msg2", "msg3"}
for _, msg := range messages {
_, err := client.Write([]byte(msg))
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
}
fmt.Printf("Sent %d messages\n", len(messages))
// Note: This example requires a UDP server listening on localhost:8083
}
Example (Ipv6Address) ¶
Example_ipv6Address demonstrates using IPv6 addresses.
This example shows how to create a client with an IPv6 address.
package main
import (
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
// IPv6 addresses must be enclosed in brackets
client, err := udp.New("[::1]:8091")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
fmt.Println("IPv6 client created successfully")
}
Output: IPv6 client created successfully
Example (MultipleMessages) ¶
Example_multipleMessages demonstrates sending multiple messages.
This example shows sending a sequence of datagrams over the same associated socket.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8088")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Send sequence of messages
count := 0
for i := 0; i < 5; i++ {
msg := fmt.Sprintf("Message %d", i)
n, err := client.Write([]byte(msg))
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
if n > 0 {
count++
}
}
fmt.Printf("Successfully sent %d messages\n", count)
// Note: This example requires a UDP server listening on localhost:8088
}
Example (OneShotRequest) ¶
Example_oneShotRequest demonstrates one-shot request/response pattern.
This example shows the Once() method for simple request/response operations that don't require persistent socket association.
package main
import (
"bytes"
"context"
"fmt"
"io"
"time"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8082")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// Prepare request
request := bytes.NewBufferString("QUERY")
// Send and optionally receive response
err = client.Once(ctx, request, func(reader io.Reader) {
// In real usage, would read response here
// For example only, we skip the read
fmt.Println("Request sent successfully")
})
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Socket automatically closed after Once()
fmt.Printf("Connection closed: %v\n", !client.IsConnected())
}
Output: Request sent successfully Connection closed: true
Example (Reconnection) ¶
Example_reconnection demonstrates reconnecting a client.
This example shows that calling Connect() multiple times replaces the existing socket association.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8089")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
ctx := context.Background()
// Initial connection
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("First connect: %v\n", client.IsConnected())
// Reconnect (replaces socket)
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Second connect: %v\n", client.IsConnected())
}
Output: First connect: true Second connect: true
Example (StateMonitoring) ¶
Example_stateMonitoring demonstrates client state monitoring.
This example shows how to check client connection state using the IsConnected() method.
package main
import (
"context"
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8087")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Check initial state
fmt.Printf("Initially connected: %v\n", client.IsConnected())
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("After connect: %v\n", client.IsConnected())
client.Close()
fmt.Printf("After close: %v\n", client.IsConnected())
}
Output: Initially connected: false After connect: true After close: false
Example (TlsNoOp) ¶
Example_tlsNoOp demonstrates that TLS is not supported.
This example shows that SetTLS() is a no-op for UDP clients since UDP doesn't support TLS natively.
package main
import (
"fmt"
"github.com/nabbar/golib/socket/client/udp"
)
func main() {
client, err := udp.New("localhost:8090")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer client.Close()
// SetTLS is a no-op for UDP (returns nil)
err = client.SetTLS(true, nil, "localhost")
if err == nil {
fmt.Println("TLS not supported (no-op, returns nil)")
}
}
Output: TLS not supported (no-op, returns nil)
Index ¶
Examples ¶
- Package (BasicClient)
- Package (CallbackOrdering)
- Package (ClientWithCallbacks)
- Package (ContextTimeout)
- Package (DatagramSizeManagement)
- Package (EmptyWrite)
- Package (ErrorHandling)
- Package (FireAndForget)
- Package (Ipv6Address)
- Package (MultipleMessages)
- Package (OneShotRequest)
- Package (Reconnection)
- Package (StateMonitoring)
- Package (TlsNoOp)
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInstance is returned when a nil client instance is used for operations. // This typically indicates a programming error where a method is called on // a nil pointer or an uninitialized client. ErrInstance = fmt.Errorf("invalid instance") // ErrConnection is returned when attempting to perform I/O operations // on a client that hasn't called Connect(), or when the underlying socket // is nil or invalid. Call Connect() before performing operations. ErrConnection = fmt.Errorf("invalid connection") // ErrAddress is returned by New() when the provided address is empty, // malformed, or cannot be resolved as a valid UDP address. The address // must be in the format "host:port" (e.g., "localhost:8080", "192.168.1.1:9000"). ErrAddress = fmt.Errorf("invalid dial address") )
Functions ¶
This section is empty.
Types ¶
type ClientUDP ¶
ClientUDP represents a UDP client that implements the socket.Client interface.
This interface extends github.com/nabbar/golib/socket.Client and provides all standard socket operations for UDP datagram communication:
- Connect(ctx) - Associate socket with remote address
- IsConnected() - Check if socket is associated
- Read(p []byte) - Read datagram from socket
- Write(p []byte) - Write datagram to socket
- Close() - Close the socket
- Once(ctx, request, response) - One-shot request/response operation
- SetTLS(enable, config, serverName) - No-op for UDP (always returns nil)
- RegisterFuncError(f) - Register error callback
- RegisterFuncInfo(f) - Register datagram info callback
Important UDP characteristics:
- Connectionless: No handshake or persistent connection state
- Unreliable: Datagrams may be lost without notification
- Unordered: Datagrams may arrive out of order
- Message boundaries: Each Write() sends one datagram
- No TLS: UDP doesn't support encryption natively (use DTLS if needed)
All operations are thread-safe and use atomic operations internally. The client maintains minimal state for the associated remote address.
See github.com/nabbar/golib/socket package for interface details.
func New ¶
New creates a new UDP client for the specified address.
The address parameter must be in the format "host:port", where:
- host can be a hostname, IPv4 address, IPv6 address (in brackets), or empty
- port must be a valid port number (1-65535)
Examples of valid addresses:
- "localhost:8080"
- "192.168.1.1:9000"
- "[::1]:8080" (IPv6)
- ":8080" (binds to all interfaces)
The client is created in a disconnected state. Use Connect() to associate the socket with the remote address. The address is validated but no network operation is performed during construction.
UDP-specific notes:
- "Connect" doesn't establish a connection, it associates the socket
- Maximum datagram size is typically 65507 bytes (65535 - 8 byte header - 20 byte IP header)
- Consider using smaller sizes (< 1472 bytes) to avoid IP fragmentation
- No guarantee of delivery or ordering
Returns:
- ClientUDP: A new client instance if successful
- error: ErrAddress if address is empty or malformed, or a net.Error if the address cannot be resolved as a valid UDP address
Example:
client, err := udp.New("localhost:8080")
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
if err := client.Connect(ctx); err != nil {
log.Fatal(err)
}
// Send small datagram
data := []byte("Hello, UDP!")
n, err := client.Write(data)
if err != nil {
log.Fatal(err)
}