futriis/internal/server/server.go
2026-02-27 22:04:04 +03:00

130 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /futriis/internal/server/server.go
// Пакет server реализует TCP-сервер для клиент-серверной архитектуры СУБД.
// Server обрабатывает входящие соединения, принимает команды в текстовом или JSON формате, выполняет их через движок и возвращает результаты клиентам.
// Поддерживает множественные одновременные подключения, каждый обрабатывается в отдельной горутине.
// Обеспечивает корректное завершение работы с закрытием всех соединений и освобождением порта.
package server
import (
"bufio"
"encoding/json"
"net"
"sync"
"futriis/internal/engine"
"futriis/pkg/utils"
)
// Server представляет сервер СУБД
type Server struct {
address string
engine *engine.Engine
listener net.Listener
clients map[net.Conn]bool
mu sync.RWMutex
stopChan chan struct{}
}
// NewServer создаёт новый сервер
func NewServer(address string, engine *engine.Engine) *Server {
return &Server{
address: address,
engine: engine,
clients: make(map[net.Conn]bool),
stopChan: make(chan struct{}),
}
}
// Start запускает сервер
func (s *Server) Start() error {
listener, err := net.Listen("tcp", s.address)
if err != nil {
return err
}
s.listener = listener
utils.PrintSuccess("Сервер запущен на %s", s.address)
go s.acceptLoop()
return nil
}
// acceptLoop принимает входящие соединения
func (s *Server) acceptLoop() {
for {
select {
case <-s.stopChan:
return
default:
conn, err := s.listener.Accept()
if err != nil {
continue
}
s.mu.Lock()
s.clients[conn] = true
s.mu.Unlock()
go s.handleClient(conn)
}
}
}
// handleClient обрабатывает клиентское соединение
func (s *Server) handleClient(conn net.Conn) {
defer func() {
s.mu.Lock()
delete(s.clients, conn)
s.mu.Unlock()
conn.Close()
}()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
line := scanner.Text()
// Парсим JSON запрос
var req map[string]interface{}
if err := json.Unmarshal([]byte(line), &req); err != nil {
// Если не JSON, обрабатываем как простую команду
result, err := s.engine.Execute(line)
s.sendResponse(conn, result, err)
} else {
// Обрабатываем JSON запрос
cmd, _ := req["command"].(string)
result, err := s.engine.Execute(cmd)
s.sendResponse(conn, result, err)
}
}
}
// sendResponse отправляет ответ клиенту
func (s *Server) sendResponse(conn net.Conn, result string, err error) {
response := make(map[string]interface{})
if err != nil {
response["error"] = err.Error()
} else {
response["result"] = result
}
data, _ := json.Marshal(response)
conn.Write(append(data, '\n'))
}
// Stop останавливает сервер
func (s *Server) Stop() {
close(s.stopChan)
if s.listener != nil {
s.listener.Close()
}
s.mu.Lock()
for conn := range s.clients {
conn.Close()
}
s.mu.Unlock()
}