130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
// /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()
|
||
}
|