Documentation
¶
Overview ¶
Package unix provides a robust, production-ready Unix domain socket server implementation for Go applications. It's designed to handle local inter-process communication (IPC) with a focus on reliability, performance, and ease of use.
Overview ¶
This package implements a high-performance Unix domain socket server that supports:
- SOCK_STREAM (connection-oriented) Unix sockets for reliable IPC
- File permissions and group ownership control for access management
- Graceful shutdown with connection draining and timeout management
- Context-aware operations with cancellation propagation
- Configurable idle timeouts for inactive connections
- Thread-safe concurrent connection handling (goroutine-per-connection)
- Connection counting and tracking
- Customizable connection configuration via UpdateConn callback
- Comprehensive event callbacks for monitoring and logging
Architecture ¶
## Component Diagram
The server follows a layered architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────┐ │ Unix Socket Server │ ├─────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌───────────────────┐ │ │ │ Listener │ │ Context Manager │ │ │ │ (Listen) │ │ (ctx tracking) │ │ │ └──────┬───────┘ └─────────┬─────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────────────┐ │ │ │ Connection Acceptor │ │ │ │ (Accept loop + file setup) │ │ │ └──────────────┬───────────────────────────┘ │ │ │ │ │ ▼ │ │ Per-Connection Goroutine │ │ ┌─────────────────────┐ │ │ │ Connection Context │ │ │ │ - sCtx (I/O wrap) │ │ │ │ - Idle timeout │ │ │ │ - State tracking │ │ │ └──────────┬──────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────┐ │ │ │ User Handler Func │ │ │ │ (custom logic) │ │ │ └─────────────────────┘ │ │ │ │ Socket File Management: │ │ - Creation with permissions │ │ - Group ownership assignment │ │ - Automatic cleanup on shutdown │ │ │ │ Optional Callbacks: │ │ - UpdateConn: Connection configuration │ │ - FuncError: Error reporting │ │ - FuncInfo: Connection state changes │ │ - FuncInfoSrv: Server lifecycle events │ │ │ └─────────────────────────────────────────────────────┘
## Data Flow
- Server.Listen() starts the accept loop
- For each new connection: a. net.Listener.Accept() receives the connection b. Connection counter incremented atomically c. UpdateConn callback invoked (if registered) d. Connection wrapped in sCtx (context + I/O) e. Handler goroutine spawned f. Idle timeout monitoring started (if configured)
- Handler processes the connection
- On close: a. Connection closed b. Context cancelled c. Counter decremented d. Goroutine terminates
## Lifecycle States
The server maintains two atomic state flags:
IsRunning: Server is accepting new connections
false → true: Listen() called successfully
true → false: Shutdown/Close initiated
IsGone: Server is draining existing connections
false → true: Shutdown() called
Used to signal accept loop to stop
## Thread Safety Model
Synchronization primitives used:
- atomic.Bool: run, gon (server state)
- atomic.Int64: nc (connection counter)
- libatm.Value: fe, fi, fs, ad (atomic storage)
- No mutexes: All state changes are lock-free
Concurrency guarantees:
- All exported methods are safe for concurrent use
- Connection handlers run in isolated goroutines
- No shared mutable state between connections
- Atomic counters prevent race conditions
Features ¶
## Unix Socket Benefits
- Zero network overhead: Communication within the same host
- File system permissions for access control
- No TCP/IP stack overhead: Lower latency than TCP loopback
- Higher throughput than TCP for local communication
- Process credentials passing on Linux (SCM_CREDENTIALS)
- File descriptor passing capability (SCM_RIGHTS)
## Security
- File system permissions for access control (chmod)
- Group ownership for fine-grained access (chown)
- No network exposure: Not accessible over the network
- Automatic socket file cleanup on shutdown
- Configurable file permissions (0600, 0660, 0666, etc.)
## Reliability
- Graceful shutdown with configurable timeouts
- Connection draining during shutdown (wait for active connections)
- Automatic resource reclamation (goroutines, memory, file descriptors)
- Idle connection timeout with automatic cleanup
- Context-aware operations with deadline and cancellation support
- Error recovery and propagation
## Monitoring & Observability
- Connection state change callbacks (new, read, write, close)
- Error reporting through callback functions
- Server lifecycle notifications
- Real-time connection counting (OpenConnections)
- Server state queries (IsRunning, IsGone)
## Performance
- Goroutine-per-connection model (suitable for 100s-1000s of connections)
- Lock-free atomic operations for state management
- Zero-copy I/O where possible
- Minimal memory overhead per connection (~10KB)
- Efficient connection tracking without locks
- Lower latency than TCP loopback (typically 2-5x faster)
Usage Example ¶
Basic echo server:
import (
"context"
"io"
"github.com/nabbar/golib/socket"
"github.com/nabbar/golib/socket/config"
"github.com/nabbar/golib/socket/server/unix"
"github.com/nabbar/golib/file/perm"
)
func main() {
handler := func(c socket.Context) {
defer c.Close()
io.Copy(c, c) // Echo back received data
}
cfg := config.Server{
Network: protocol.NetworkUnix,
Address: "/tmp/myapp.sock",
PermFile: perm.New(0660),
GroupPerm: -1, // Use default group
}
srv, err := unix.New(nil, handler, cfg)
if err != nil {
panic(err)
}
// Start the server
if err := srv.Listen(context.Background()); err != nil {
panic(err)
}
}
Concurrency Model ¶
## Goroutine-Per-Connection
The server uses a goroutine-per-connection model, where each accepted connection spawns a dedicated goroutine to handle it. This model is well-suited for:
- Low to medium concurrent connections (100s to low 1000s)
- Long-lived connections (persistent IPC, service communication)
- Applications requiring per-connection state and context
- Connections with varying processing times
- Connections requiring blocking I/O operations
## Scalability Characteristics
Typical performance profile:
┌─────────────────┬──────────────┬────────────────┬──────────────┐
│ Connections │ Goroutines │ Memory Usage │ Throughput │
├─────────────────┼──────────────┼────────────────┼──────────────┤
│ 10 │ ~12 │ ~100 KB │ Excellent │
│ 100 │ ~102 │ ~1 MB │ Excellent │
│ 1,000 │ ~1,002 │ ~10 MB │ Good │
│ 10,000 │ ~10,002 │ ~100 MB │ Fair* │
│ 100,000+ │ ~100,002+ │ ~1 GB+ │ Not advised │
└─────────────────┴──────────────┴────────────────┴──────────────┘
* At 10K+ connections, consider profiling and potentially switching to
an event-driven model or worker pool architecture.
## Memory Overhead
Per-connection memory allocation:
Base overhead: ~8 KB (goroutine stack) Connection context: ~1 KB (sCtx structure) Buffers (handler): Variable (depends on implementation) ───────────────────────────────── Total minimum: ~10 KB per connection
Example calculation for 1000 connections:
1000 connections × 10 KB = ~10 MB base + application buffers (e.g., 4KB read buffer × 1000 = 4 MB) = ~14 MB total for connections
Performance Considerations ¶
## Throughput
Unix sockets typically outperform TCP loopback for local IPC:
- Unix socket: ~2-5x faster than TCP loopback
- Lower latency: ~50% less than TCP loopback
- Higher bandwidth: No TCP/IP stack overhead
The server's throughput is primarily limited by:
- Handler function complexity (CPU-bound operations)
- Disk I/O if using file-based operations
- System limits: File descriptors, kernel buffers
Typical throughput (echo handler on localhost):
- Unix socket: ~1M requests/sec (small payloads)
- TCP loopback: ~500K requests/sec (same conditions)
## Latency
Expected latency profile:
┌──────────────────────┬─────────────────┐ │ Operation │ Typical Time │ ├──────────────────────┼─────────────────┤ │ Connection accept │ <500 µs │ │ Handler spawn │ <100 µs │ │ Context creation │ <10 µs │ │ Read/Write syscall │ <50 µs │ │ Graceful shutdown │ 100 ms - 1 s │ └──────────────────────┴─────────────────┘
## Resource Limits
System-level limits to consider:
File Descriptors: - Each connection uses 1 file descriptor - Check: ulimit -n (default often 1024 on Linux) - Increase: ulimit -n 65536 or via /etc/security/limits.conf
Socket Buffer Memory: - Per-connection send/receive buffers (typically smaller than TCP) - Tune: sysctl net.unix.max_dgram_qlen (for datagram sockets)
Filesystem: - Socket file must be in a writable directory - Path length limited (typically 108 characters on Linux) - Cleanup required if process crashes
Limitations ¶
## Known Limitations
- No built-in rate limiting or connection throttling
- No support for connection pooling or multiplexing
- Goroutine-per-connection model limits scalability >10K connections
- No built-in protocol framing (implement in handler)
- No built-in metrics export (Prometheus, etc.)
- Platform support: Linux and macOS only (not Windows)
## Not Suitable For
- Remote connections (use TCP instead)
- Ultra-high concurrency scenarios (>50K simultaneous connections)
- Low-latency HFT applications (<10µs response time)
- Systems requiring protocol multiplexing (use gRPC)
- Short-lived connections at very high rates (>100K conn/sec)
- Windows platforms (Unix sockets not supported)
## Comparison with Alternatives
┌──────────────────┬────────────────┬──────────────────┬──────────────┐ │ Feature │ Unix Socket │ TCP Loopback │ Named Pipe │ ├──────────────────┼────────────────┼──────────────────┼──────────────┤ │ Overhead │ Minimal │ TCP/IP stack │ Minimal │ │ Throughput │ Very High │ High │ High │ │ Latency │ Very Low │ Low │ Low │ │ Permissions │ Filesystem │ Firewall │ Filesystem │ │ Network Access │ No │ Yes (loopback) │ No │ │ Platform │ Unix/Linux │ All platforms │ Windows │ │ Best For │ Local IPC │ Network compat │ Windows IPC │ └──────────────────┴────────────────┴──────────────────┴──────────────┘
Best Practices ¶
## Resource Management
Always use defer for cleanup:
defer srv.Close() // Server defer ctx.Close() // Connection (in handler)
Implement graceful shutdown:
sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan log.Println("Shutting down...")
shutdownCtx, cancel := context.WithTimeout( context.Background(), 30*time.Second) defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil { log.Printf("Shutdown error: %v", err) }
Monitor connection count:
go func() { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop()
for range ticker.C { count := srv.OpenConnections() if count > warnThreshold { log.Printf("WARNING: High connection count: %d", count) } } }()
## Security
Set restrictive file permissions:
cfg.PermFile = perm.New(0600) // Owner only cfg.PermFile = perm.New(0660) // Owner + group
Use group ownership for access control:
cfg.GroupPerm = getGidForService()
Configure idle timeouts to prevent resource exhaustion:
cfg.ConIdleTimeout = 5 * time.Minute
Validate input in handlers (prevent injection, DoS, etc.)
Clean up socket files on unexpected termination
## Testing
Test with concurrent connections:
for i := 0; i < numClients; i++ { go func() { conn, _ := net.Dial("unix", socketPath) defer conn.Close() // Test logic... }() }
Test graceful shutdown under load
Test with slow/misbehaving clients
Run with race detector: go test -race
Platform Support ¶
This package is only available on:
- Linux (all architectures)
- macOS/Darwin (all architectures)
For Windows or other platforms, the package provides stub implementations that return nil. See ignore.go for unsupported platform handling.
Related Packages ¶
- github.com/nabbar/golib/socket: Base interfaces and types
- github.com/nabbar/golib/socket/config: Server configuration
- github.com/nabbar/golib/file/perm: File permission management
- github.com/nabbar/golib/network/protocol: Protocol constants
See the example_test.go file for runnable examples covering common use cases.
Package unix provides a Unix domain socket server implementation with session support.
This package implements the github.com/nabbar/golib/socket.Server interface for Unix domain sockets (AF_UNIX), providing a connection-oriented server with features including:
- Unix domain socket file creation and management
- File permissions and group ownership control
- Persistent connections with session handling
- Connection lifecycle management (accept, read, write, close)
- Callback hooks for errors and connection events
- Graceful shutdown with connection draining
- Atomic state management
- Context-aware operations
Unix domain sockets provide inter-process communication (IPC) on the same host with lower overhead than TCP sockets. They appear as special files in the filesystem.
Platform support: Linux and Darwin (macOS). See ignore.go for other platforms.
See github.com/nabbar/golib/socket for the Server interface definition.
Example ¶
Example demonstrates a basic echo server. This is the simplest possible Unix socket server implementation.
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
// Create handler function that echoes back received data
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
buf := make([]byte, 1024)
for {
n, err := c.Read(buf)
if err != nil {
return
}
if n > 0 {
_, _ = c.Write(buf[:n])
}
}
}
// Create temporary socket file
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("example-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
// Create server configuration
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
}
// Create server
srv, err := scksru.New(nil, handler, cfg)
if err != nil {
panic(err)
}
// Start server
ctx := context.Background()
go func() {
_ = srv.Listen(ctx)
}()
// Wait for server to start
time.Sleep(100 * time.Millisecond)
// Shutdown after demonstration
_ = srv.Shutdown(ctx)
}
Example (Complete) ¶
Example_complete demonstrates a production-ready server with all features. This example shows error handling, monitoring, graceful shutdown, and logging.
package main
import (
"context"
"fmt"
"net"
"os"
"path/filepath"
"time"
libdur "github.com/nabbar/golib/duration"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
// Handler with proper error handling
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
buf := make([]byte, 4096)
for c.IsConnected() {
n, err := c.Read(buf)
if err != nil {
return
}
if n > 0 {
if _, err := c.Write(buf[:n]); err != nil {
return
}
}
}
}
// Create temporary socket file
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("complete-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
// Create configuration with idle timeout
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0660),
GroupPerm: -1,
ConIdleTimeout: libdur.Minutes(5),
}
// Create server
srv, err := scksru.New(nil, handler, cfg)
if err != nil {
fmt.Printf("Failed to create server: %v\n", err)
return
}
// Register monitoring callbacks
srv.RegisterFuncError(func(errs ...error) {
for _, e := range errs {
fmt.Printf("Server error: %v\n", e)
}
})
srv.RegisterFuncInfo(func(local, remote net.Addr, state libsck.ConnState) {
fmt.Printf("Connection %s from %s\n", state, remote)
})
// Start server
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
if err := srv.Listen(ctx); err != nil {
fmt.Printf("Server stopped: %v\n", err)
}
}()
// Wait for server to be ready
time.Sleep(50 * time.Millisecond)
fmt.Printf("Server running with %d connections\n", srv.OpenConnections())
// Graceful shutdown
shutdownCtx, shutdownCancel := context.WithTimeout(
context.Background(), 10*time.Second)
defer shutdownCancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
fmt.Printf("Shutdown error: %v\n", err)
}
fmt.Println("Server stopped gracefully")
}
Output: Server running with 0 connections Server stopped gracefully
Index ¶
Examples ¶
Constants ¶
const MaxGID = 32767
MaxGID defines the maximum allowed Unix group ID value (32767). Group IDs must be within this range to be valid on Linux systems.
Variables ¶
var ( ErrInvalidUnixFile = fmt.Errorf("invalid unix file for socket listening") // ErrInvalidGroup is returned when the specified GID exceeds the maximum allowed value (32767). // Unix group IDs must be within the valid range for the operating system. ErrInvalidGroup = fmt.Errorf("invalid unix group for socket group permission") // ErrInvalidHandler is returned when attempting to start a server without a valid handler function. // A handler must be provided via the New() constructor. ErrInvalidHandler = fmt.Errorf("invalid handler") // ErrShutdownTimeout is returned when the server shutdown exceeds the context timeout. // This typically happens when StopListen() takes longer than expected. ErrShutdownTimeout = fmt.Errorf("timeout on stopping socket") // ErrInvalidInstance is returned when operating on a nil server instance. ErrInvalidInstance = fmt.Errorf("invalid socket instance") )
Functions ¶
This section is empty.
Types ¶
type ServerUnix ¶
type ServerUnix interface {
libsck.Server
// RegisterSocket sets the Unix socket file path, permissions, and group ownership.
//
// Parameters:
// - unixFile: Absolute or relative path to the socket file (e.g., "/tmp/app.sock")
// - perm: File permissions (e.g., 0600 for user-only, 0660 for user+group)
// - gid: Group ID for the socket file, or -1 to use default group
//
// The socket file will be created when Listen() is called and removed on shutdown.
// If the file exists, it will be deleted before creating the new socket.
//
// Returns ErrInvalidGroup if gid exceeds MaxGID (32767).
//
// This method must be called before Listen().
RegisterSocket(unixFile string, perm libprm.Perm, gid int32) error
}
ServerUnix defines the interface for a Unix domain socket server implementation. It extends the base github.com/nabbar/golib/socket.Server interface with Unix socket-specific functionality.
The server operates in connection-oriented mode (SOCK_STREAM):
- Creates a Unix socket file in the filesystem
- Accepts persistent connections from clients
- Each connection is handled independently in a separate goroutine
- Supports file permissions and group ownership
- Graceful shutdown with connection draining
- Configurable idle timeouts
- Comprehensive event callbacks
Thread Safety ¶
All exported methods are safe for concurrent use by multiple goroutines. The server maintains internal synchronization to handle concurrent connections.
Error Handling ¶
The server provides multiple ways to handle errors:
- Return values from methods
- Error callback function
- Context cancellation
Lifecycle ¶
1. Create server with New() 2. Configure with RegisterSocket() 3. Start with Listen() 4. Handle connections in handler function 5. Shut down with Shutdown() or Close()
See github.com/nabbar/golib/socket.Server for inherited methods:
- Listen(context.Context) error - Start accepting connections
- Shutdown(context.Context) error - Graceful shutdown
- Close() error - Immediate shutdown
- IsRunning() bool - Check if server is accepting connections
- IsGone() bool - Check if all connections are closed
- OpenConnections() int64 - Get active connection count
- Done() <-chan struct{} - Channel closed when listener stops
- Gone() <-chan struct{} - Channel closed when all connections close
- SetTLS(bool, TLSConfig) error - No-op for Unix sockets (always returns nil)
- RegisterFuncError(FuncError) - Register error callback
- RegisterFuncInfo(FuncInfo) - Register connection event callback
- RegisterFuncInfoServer(FuncInfoSrv) - Register server event callback
Example (ContextValues) ¶
ExampleServerUnix_contextValues demonstrates using context values
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
type contextKey string
const requestIDKey contextKey = "requestID"
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
// Access context value
if requestID := c.Value(requestIDKey); requestID != nil {
fmt.Printf("Processing request ID: %v\n", requestID)
}
}
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("context-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
}
srv, _ := scksru.New(nil, handler, cfg)
fmt.Println("Server with context values ready")
_ = srv.Shutdown(context.Background())
}
Output: Server with context values ready
Example (IdleTimeout) ¶
ExampleServerUnix_idleTimeout demonstrates idle connection timeout
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
libdur "github.com/nabbar/golib/duration"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
// Handler that doesn't read/write (connection will idle)
time.Sleep(200 * time.Millisecond)
}
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("idle-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
ConIdleTimeout: libdur.ParseDuration(100 * time.Millisecond),
}
srv, _ := scksru.New(nil, handler, cfg)
ctx := context.Background()
go func() {
_ = srv.Listen(ctx)
}()
time.Sleep(50 * time.Millisecond)
fmt.Println("Server with idle timeout running")
_ = srv.Shutdown(ctx)
}
Output: Server with idle timeout running
Example (Monitoring) ¶
ExampleServerUnix_monitoring demonstrates complete monitoring setup
package main
import (
"context"
"fmt"
"net"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
}
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("monitor-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
}
srv, _ := scksru.New(nil, handler, cfg)
// Register all callbacks
srv.RegisterFuncError(func(errs ...error) {
fmt.Println("Error callback registered")
})
srv.RegisterFuncInfo(func(local, remote net.Addr, state libsck.ConnState) {
fmt.Println("Connection callback registered")
})
srv.RegisterFuncInfoServer(func(msg string) {
fmt.Println("Server info callback registered")
})
fmt.Println("All monitoring callbacks configured")
_ = srv.Shutdown(context.Background())
}
Output: All monitoring callbacks configured
func New ¶
func New(upd libsck.UpdateConn, hdl libsck.HandlerFunc, cfg sckcfg.Server) (ServerUnix, error)
New creates a new Unix domain socket server instance with the specified connection handler and optional connection updater.
Parameters:
- u: Optional UpdateConn callback invoked for each accepted connection. Can be used to set socket options, configure timeouts, or track connections. If nil, no per-connection configuration is performed.
- h: Required HandlerFunc that processes each connection. The handler receives io.Reader and io.Writer interfaces for the connection and runs in its own goroutine. The handler should handle the entire lifecycle of the connection.
The returned server must be configured with RegisterSocket() before calling Listen().
Example ¶
srv := unix.New(
// Optional connection updater
func(conn net.Conn) {
if tcpConn, ok := conn.(*net.UnixConn); ok {
_ = tcpConn.SetReadBuffer(8192)
}
},
// Required connection handler
func(r io.Reader, w io.Writer) {
// Handle connection
_, _ = io.Copy(w, r) // Echo server example
},
)
Performance Considerations ¶
The handler function should be designed to handle multiple concurrent connections efficiently. For CPU-bound work, consider using a worker pool pattern.
Error Handling ¶
Any panics in the handler will be recovered and logged, but the connection will be closed. For better error handling, use recover() in your handler.
Example usage:
handler := func(r socket.Reader, w socket.Writer) {
defer r.Close()
defer w.Close()
buf := make([]byte, 4096)
for {
n, err := r.Read(buf)
if err != nil {
break
}
w.Write(buf[:n])
}
}
srv := unix.New(nil, handler)
srv.RegisterSocket("/tmp/myapp.sock", 0600, -1)
srv.Listen(context.Background())
The server is safe for concurrent use and manages connection lifecycle properly. Connections persist until explicitly closed, unlike UDP.
See github.com/nabbar/golib/socket.HandlerFunc and socket.UpdateConn for callback function signatures.
Example ¶
ExampleNew demonstrates creating a Unix socket server
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
// Define connection handler
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
_, _ = io.Copy(c, c) // Echo
}
// Create temporary socket file
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("new-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
// Create configuration
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
}
// Create server
srv, err := scksru.New(nil, handler, cfg)
if err != nil {
fmt.Printf("Failed to create server: %v\n", err)
return
}
fmt.Printf("Server created successfully\n")
_ = srv
}
Output: Server created successfully
Example (Permissions) ¶
ExampleNew_permissions demonstrates file permission configuration
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
}
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("perms-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
// Configure different permission modes
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600), // Owner only
GroupPerm: -1, // Default group
}
srv, err := scksru.New(nil, handler, cfg)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Server with custom permissions created")
_ = srv.Shutdown(context.Background())
}
Output: Server with custom permissions created
Example (WithUpdateConn) ¶
ExampleNew_withUpdateConn demonstrates custom connection configuration
package main
import (
"context"
"fmt"
"net"
"os"
"path/filepath"
"time"
libprm "github.com/nabbar/golib/file/perm"
libptc "github.com/nabbar/golib/network/protocol"
libsck "github.com/nabbar/golib/socket"
sckcfg "github.com/nabbar/golib/socket/config"
scksru "github.com/nabbar/golib/socket/server/unix"
)
func main() {
// UpdateConn callback to configure socket options
upd := func(c net.Conn) {
if unixConn, ok := c.(*net.UnixConn); ok {
_ = unixConn.SetReadBuffer(8192)
_ = unixConn.SetWriteBuffer(8192)
}
}
handler := func(c libsck.Context) {
defer func() { _ = c.Close() }()
}
tmpDir := os.TempDir()
socketPath := filepath.Join(tmpDir, fmt.Sprintf("update-%d.sock", time.Now().UnixNano()))
defer os.Remove(socketPath)
cfg := sckcfg.Server{
Network: libptc.NetworkUnix,
Address: socketPath,
PermFile: libprm.Perm(0600),
GroupPerm: -1,
}
srv, err := scksru.New(upd, handler, cfg)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Server with custom connection config created")
_ = srv.Shutdown(context.Background())
}
Output: Server with custom connection config created