Files
SimpleRemoter/server/go/connection/context.go

199 lines
4.3 KiB
Go
Raw Normal View History

2025-12-19 14:12:29 +01:00
package connection
import (
"fmt"
"net"
"sync"
"sync/atomic"
"time"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/buffer"
)
// ClientInfo stores client metadata
type ClientInfo struct {
ClientID string // Client ID from login (MasterID)
IP string
ComputerName string
OS string
CPU string
HasCamera bool
Version string
InstallTime string
LoginTime time.Time
ClientType string
FilePath string
GroupName string
}
// Context represents a client connection context
type Context struct {
ID uint64
Conn net.Conn
RemoteAddr string
// Buffers
InBuffer *buffer.Buffer // Received compressed data
OutBuffer *buffer.Buffer // Decompressed data for processing
// Client info
Info ClientInfo
IsLoggedIn atomic.Bool
IsAuthorized atomic.Bool // Whether client is authorized via heartbeat
2025-12-19 14:12:29 +01:00
// Connection state
OnlineTime time.Time
lastActiveNs atomic.Int64 // Unix nanoseconds for thread-safe access
// Protocol state
CompressMethod int
FlagType FlagType
HeaderLen int
FlagLen int
HeaderEncType int // Header encryption type (0-7)
HeaderParams []byte // Header parameters for decoding (flag bytes)
// User data - for storing dialog/handler references
UserData interface{}
// Internal
mu sync.RWMutex
closed atomic.Bool
sendLock sync.Mutex
server *Manager
}
// FlagType represents the protocol flag type
type FlagType int
const (
FlagUnknown FlagType = iota
FlagShine
FlagFuck
FlagHello
FlagHell
FlagWinOS
)
// Compression methods
const (
CompressUnknown = -2
CompressZlib = -1
CompressZstd = 0
CompressNone = 1
)
// NewContext creates a new connection context
func NewContext(conn net.Conn, mgr *Manager) *Context {
now := time.Now()
ctx := &Context{
Conn: conn,
RemoteAddr: conn.RemoteAddr().String(),
InBuffer: buffer.New(8192),
OutBuffer: buffer.New(8192),
OnlineTime: now,
CompressMethod: CompressZstd,
FlagType: FlagUnknown,
server: mgr,
}
ctx.lastActiveNs.Store(now.UnixNano())
return ctx
}
// Send sends data to the client (thread-safe)
func (c *Context) Send(data []byte) error {
if c.closed.Load() {
return ErrConnectionClosed
}
c.sendLock.Lock()
defer c.sendLock.Unlock()
_, err := c.Conn.Write(data)
if err != nil {
return err
}
c.UpdateLastActive()
return nil
}
// UpdateLastActive updates the last active time (thread-safe)
func (c *Context) UpdateLastActive() {
c.lastActiveNs.Store(time.Now().UnixNano())
}
// LastActive returns the last active time (thread-safe)
func (c *Context) LastActive() time.Time {
return time.Unix(0, c.lastActiveNs.Load())
}
// TimeSinceLastActive returns duration since last activity (thread-safe)
func (c *Context) TimeSinceLastActive() time.Duration {
return time.Since(c.LastActive())
}
// Close closes the connection
func (c *Context) Close() error {
if c.closed.Swap(true) {
return nil // Already closed
}
return c.Conn.Close()
}
// IsClosed returns whether the connection is closed
func (c *Context) IsClosed() bool {
return c.closed.Load()
}
// GetPeerIP returns the peer IP address
func (c *Context) GetPeerIP() string {
if host, _, err := net.SplitHostPort(c.RemoteAddr); err == nil {
return host
}
return c.RemoteAddr
}
// AliveTime returns how long the connection has been alive
func (c *Context) AliveTime() time.Duration {
return time.Since(c.OnlineTime)
}
// SetInfo sets the client info
func (c *Context) SetInfo(info ClientInfo) {
c.mu.Lock()
defer c.mu.Unlock()
c.Info = info
}
// GetInfo returns the client info
func (c *Context) GetInfo() ClientInfo {
c.mu.RLock()
defer c.mu.RUnlock()
return c.Info
}
// SetUserData stores user-defined data
func (c *Context) SetUserData(data interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.UserData = data
}
// GetUserData retrieves user-defined data
func (c *Context) GetUserData() interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
return c.UserData
}
// GetClientID returns the client ID for logging
// If ClientID is set (from login), returns it; otherwise returns connection ID as fallback
func (c *Context) GetClientID() string {
c.mu.RLock()
defer c.mu.RUnlock()
if c.Info.ClientID != "" {
return c.Info.ClientID
}
return fmt.Sprintf("conn-%d", c.ID)
}