Files
FlexDXClusterGui/TCPServer.go
2025-10-19 10:15:11 +02:00

165 lines
3.5 KiB
Go

package main
import (
"bufio"
"net"
"os"
"strings"
"sync"
"time"
log "github.com/sirupsen/logrus"
)
var (
err error
)
type TCPServer struct {
Address string
Port string
Clients map[net.Conn]*ClientInfo // ✅ Map avec structure ClientInfo
Mutex *sync.Mutex
LogWriter *bufio.Writer
Reader *bufio.Reader
Writer *bufio.Writer
Conn net.Conn
Listener net.Listener
MsgChan chan string
CmdChan chan string
Log *log.Logger
Config *Config
}
// ✅ Structure pour stocker les infos client
type ClientInfo struct {
ConnectedAt time.Time
}
func NewTCPServer(address string, port string) *TCPServer {
return &TCPServer{
Address: address,
Port: port,
Clients: make(map[net.Conn]*ClientInfo),
MsgChan: make(chan string, 100),
CmdChan: make(chan string),
Mutex: new(sync.Mutex),
}
}
func (s *TCPServer) StartServer() {
s.LogWriter = bufio.NewWriter(os.Stdout)
s.Listener, err = net.Listen("tcp", Cfg.TelnetServer.Host+":"+Cfg.TelnetServer.Port)
if err != nil {
Log.Info("Could not create telnet server")
}
defer s.Listener.Close()
Log.Infof("Telnet server listening on %s:%s", Cfg.TelnetServer.Host, Cfg.TelnetServer.Port)
go func() {
for message := range s.MsgChan {
s.broadcastMessage(message)
}
}()
for {
s.Conn, err = s.Listener.Accept()
Log.Info("Client connected: ", s.Conn.RemoteAddr().String())
if err != nil {
Log.Error("Could not accept connections to telnet server")
continue
}
s.Mutex.Lock()
s.Clients[s.Conn] = &ClientInfo{
ConnectedAt: time.Now(), // ✅ Enregistre l'heure de connexion
}
s.Mutex.Unlock()
go s.handleConnection()
}
}
func (s *TCPServer) handleConnection() {
conn := s.Conn
conn.Write([]byte("Welcome to the FlexDXCluster telnet server! Type 'bye' to exit.\n"))
reader := bufio.NewReader(conn)
defer func() {
s.Mutex.Lock()
delete(s.Clients, conn)
s.Mutex.Unlock()
conn.Close()
Log.Infof("Client %s disconnected", conn.RemoteAddr().String())
}()
for {
message, err := reader.ReadString('\n')
if err != nil {
Log.Debugf("Error reading from client %s: %v", conn.RemoteAddr().String(), err)
return
}
message = strings.TrimSpace(message)
if message == "bye" {
Log.Infof("Client %s sent bye command", conn.RemoteAddr().String())
return
}
if strings.Contains(message, "DX") || strings.Contains(message, "SH/DX") ||
strings.Contains(message, "set") || strings.Contains(message, "SET") {
select {
case s.CmdChan <- message:
Log.Debugf("Command from client %s: %s", conn.RemoteAddr().String(), message)
default:
Log.Warn("Command channel is full, dropping command")
}
}
}
}
func (s *TCPServer) Write(message string) (n int, err error) {
_, err = s.Writer.Write([]byte(message))
if err == nil {
err = s.Writer.Flush()
}
return
}
func (s *TCPServer) broadcastMessage(message string) {
s.Mutex.Lock()
defer s.Mutex.Unlock()
if len(s.Clients) == 0 {
return
}
// ✅ Si un client vient de se connecter (< 3 secondes), NE RIEN ENVOYER
for _, info := range s.Clients {
if time.Since(info.ConnectedAt) < 3*time.Second {
// ✅ Client trop récent, on DROP le spot
return
}
}
var failedClients []net.Conn
for client := range s.Clients {
_, err := client.Write([]byte(message + "\r\n"))
if err != nil {
Log.Warnf("Error sending to client %s: %v", client.RemoteAddr(), err)
failedClients = append(failedClients, client)
}
}
for _, client := range failedClients {
delete(s.Clients, client)
client.Close()
}
}