A simple, high-performance TCP server framework for Go.
- Simple API - Easy to use with functional options pattern
- Custom Codec - Pluggable message encoding/decoding via
io.Reader - Graceful Shutdown - Context-based cancellation support
- Idle Timeout - Automatic read/write deadline management for connection health
- Error Handling - Flexible error handling with
DisconnectorContinueactions - Structured Logging - Built-in
slogintegration
- Go 1.23+
go get github.com/Zereker/socketpackage main
import (
"context""io""log""net""github.com/Zereker/socket"
)
// Define your message typetypeMessagestruct {
Data []byte
}
func (mMessage) Length() int { returnlen(m.Data) }
func (mMessage) Body() []byte { returnm.Data }
// Implement the Codec interfacetypeSimpleCodecstruct{}
func (c*SimpleCodec) Decode(r io.Reader) (socket.Message, error) {
buf:=make([]byte, 1024)
n, err:=r.Read(buf)
iferr!=nil {
returnnil, err
}
returnMessage{Data: buf[:n]}, nil
}
func (c*SimpleCodec) Encode(msg socket.Message) ([]byte, error) {
returnmsg.Body(), nil
}
// Implement the Handler interfacetypeEchoHandlerstruct{}
func (h*EchoHandler) Handle(tcpConn*net.TCPConn) {
conn, err:=socket.NewConn(tcpConn,
socket.CustomCodecOption(&SimpleCodec{}),
socket.OnMessageOption(func(msg socket.Message) error {
// Echo the message backreturnconn.Write(msg)
}),
)
iferr!=nil {
log.Printf("failed to create connection: %v", err)
return
}
conn.Run(context.Background())
}
funcmain() {
addr, _:=net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
server, err:=socket.New(addr)
iferr!=nil {
log.Fatal(err)
}
log.Println("server listening on", addr)
server.Serve(context.Background(), &EchoHandler{})
}| Option | Description | Default |
|---|---|---|
CustomCodecOption(codec) | Set message codec (required) | - |
OnMessageOption(handler) | Set message handler (required) | - |
OnErrorOption(handler) | Set error handler | Disconnect on error |
IdleTimeoutOption(duration) | Set idle timeout for read/write deadlines | 30s |
BufferSizeOption(size) | Set send channel buffer size | 1 |
MessageMaxSize(size) | Set max message size | 1MB |
LoggerOption(logger) | Set custom logger | slog default |
Note: The idle timeout sets TCP read/write deadlines but does not send ping/pong packets. For active connection health checking, implement heartbeat messages in your application protocol.
Control how errors are handled with OnErrorOption:
socket.OnErrorOption(func(errerror) socket.ErrorAction {
ifisTemporaryError(err) {
returnsocket.Continue// Suppress error and continue
}
returnsocket.Disconnect// Close the connection
})// Gracefully close the connectionconn.Close()
// Check if connection is closedifconn.IsClosed() {
// Handle closed connection
}
// Get remote addressaddr:=conn.Addr()Three ways to send messages with different blocking behaviors:
// Non-blocking write (fire-and-forget)// Returns ErrBufferFull immediately if channel is full// Best for: non-critical data, custom backpressure handlingerr:=conn.Write(msg)
iferrors.Is(err, socket.ErrBufferFull) {
// Handle backpressure: drop, retry, or use blocking write
}
// Blocking write with context cancellation// Waits for buffer space, respects context timeout/cancellation// Best for: critical messages that must be deliveredctx, cancel:=context.WithTimeout(context.Background(), 5*time.Second)
defercancel()
conn.WriteBlocking(ctx, msg)
// Write with timeout// Waits up to the specified duration for buffer space// Best for: simple timeout without context managementconn.WriteTimeout(msg, 5*time.Second)All write methods return ErrConnectionClosed if the connection is closed.
When ErrBufferFull is returned, it indicates the receiver is not consuming messages fast enough. Strategies:
- Drop: Acceptable for metrics, heartbeats, or non-critical updates
- Retry with backoff: For important but delay-tolerant messages
- Switch to blocking: Use
WriteBlockingwhen delivery is critical - Flow control: Implement application-level rate limiting
Implement the Logger interface or use slog:
typeLoggerinterface {
Debug(msgstring, args...any)
Info(msgstring, args...any)
Warn(msgstring, args...any)
Error(msgstring, args...any)
}MIT License - see LICENSE for details.