Merge branch 'main' of https://git.ahtamov.ru/SharikovPP/LINUX
This commit is contained in:
570
go/main.go
Normal file
570
go/main.go
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerState - состояния сервера
|
||||||
|
type ServerState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateStopped ServerState = iota
|
||||||
|
StateRunning
|
||||||
|
StatePaused
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config содержит конфигурацию сервера
|
||||||
|
type Config struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server представляет TCP-сервер
|
||||||
|
type Server struct {
|
||||||
|
config Config
|
||||||
|
listener net.Listener
|
||||||
|
clients map[net.Conn]bool
|
||||||
|
clientsMux sync.RWMutex
|
||||||
|
wg sync.WaitGroup
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
state ServerState
|
||||||
|
stateMux sync.RWMutex
|
||||||
|
pauseCh chan struct{} // Канал для уведомления о паузе/продолжении
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer создает новый экземпляр сервера
|
||||||
|
func NewServer(cfg Config) *Server {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &Server{
|
||||||
|
config: cfg,
|
||||||
|
clients: make(map[net.Conn]bool),
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
state: StateStopped,
|
||||||
|
pauseCh: make(chan struct{}, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start запускает сервер
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
s.stateMux.Lock()
|
||||||
|
if s.state == StateRunning {
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
return fmt.Errorf("сервер уже запущен")
|
||||||
|
}
|
||||||
|
s.state = StateRunning
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
|
||||||
|
addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
s.stateMux.Lock()
|
||||||
|
s.state = StateStopped
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
return fmt.Errorf("ошибка запуска сервера: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.listener = listener
|
||||||
|
|
||||||
|
// Восстанавливаем контекст если он был отменен
|
||||||
|
if s.ctx.Err() != nil {
|
||||||
|
s.ctx, s.cancel = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Сервер запущен на %s:%d", s.config.Host, s.config.Port)
|
||||||
|
log.Print("Ожидание подключений...")
|
||||||
|
|
||||||
|
// Запускаем обработку входящих подключений
|
||||||
|
s.wg.Add(1)
|
||||||
|
go s.acceptConnections()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop останавливает сервер
|
||||||
|
func (s *Server) Stop() {
|
||||||
|
s.stateMux.Lock()
|
||||||
|
if s.state == StateStopped {
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Остановка сервера...")
|
||||||
|
s.state = StateStopped
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
|
||||||
|
// Отменяем контекст для завершения всех операций
|
||||||
|
s.cancel()
|
||||||
|
|
||||||
|
// Останавливаем прием новых подключений
|
||||||
|
if s.listener != nil {
|
||||||
|
s.listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрываем все активные подключения
|
||||||
|
s.clientsMux.Lock()
|
||||||
|
for client := range s.clients {
|
||||||
|
client.Close()
|
||||||
|
delete(s.clients, client)
|
||||||
|
}
|
||||||
|
s.clientsMux.Unlock()
|
||||||
|
|
||||||
|
// Ждем завершения всех горутин с таймаутом
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
s.wg.Wait()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
log.Print("Сервер корректно остановлен")
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
log.Print("Предупреждение: таймаут при ожидании остановки сервера")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause приостанавливает прием новых подключений
|
||||||
|
func (s *Server) Pause() {
|
||||||
|
s.stateMux.Lock()
|
||||||
|
if s.state != StateRunning {
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
log.Print("Сервер не запущен или уже на паузе")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.state = StatePaused
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
|
||||||
|
log.Print("Прием новых подключений приостановлен")
|
||||||
|
|
||||||
|
// Останавливаем listener
|
||||||
|
if s.listener != nil {
|
||||||
|
s.listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оповещаем acceptConnections о паузе
|
||||||
|
select {
|
||||||
|
case s.pauseCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue возобновляет прием новых подключений
|
||||||
|
func (s *Server) Continue() {
|
||||||
|
s.stateMux.Lock()
|
||||||
|
if s.state != StatePaused {
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
log.Print("Сервер не на паузе")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.state = StateRunning
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
|
||||||
|
log.Print("Прием подключений возобновлен")
|
||||||
|
|
||||||
|
// Пересоздаем listener
|
||||||
|
addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
|
||||||
|
listener, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Ошибка при возобновлении сервера: %v", err)
|
||||||
|
s.stateMux.Lock()
|
||||||
|
s.state = StateStopped
|
||||||
|
s.stateMux.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.listener = listener
|
||||||
|
|
||||||
|
// Оповещаем acceptConnections о продолжении
|
||||||
|
select {
|
||||||
|
case s.pauseCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart перезапускает сервер
|
||||||
|
func (s *Server) Restart() {
|
||||||
|
log.Print("Перезапуск сервера...")
|
||||||
|
s.Stop()
|
||||||
|
time.Sleep(100 * time.Millisecond) // Небольшая задержка для корректной остановки
|
||||||
|
|
||||||
|
// Восстанавливаем контекст
|
||||||
|
s.ctx, s.cancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
if err := s.Start(); err != nil {
|
||||||
|
log.Printf("Ошибка при перезапуске сервера: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Print("Сервер перезапущен")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState возвращает текущее состояние сервера
|
||||||
|
func (s *Server) GetState() ServerState {
|
||||||
|
s.stateMux.RLock()
|
||||||
|
defer s.stateMux.RUnlock()
|
||||||
|
return s.state
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptConnections принимает входящие подключения
|
||||||
|
func (s *Server) acceptConnections() {
|
||||||
|
defer s.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Проверяем состояние сервера
|
||||||
|
s.stateMux.RLock()
|
||||||
|
state := s.state
|
||||||
|
s.stateMux.RUnlock()
|
||||||
|
|
||||||
|
if state != StateRunning {
|
||||||
|
// Ждем изменения состояния
|
||||||
|
select {
|
||||||
|
case <-s.pauseCh:
|
||||||
|
continue
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем таймаут для Accept, чтобы можно было реагировать на отмену
|
||||||
|
s.listener.(*net.TCPListener).SetDeadline(time.Now().Add(time.Second))
|
||||||
|
|
||||||
|
conn, err := s.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
// Проверяем, была ли ошибка из-за таймаута (ожидаемая ситуация)
|
||||||
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не был ли листенер закрыт (при паузе или остановке)
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Если это не таймаут и не отмена, логируем ошибку
|
||||||
|
log.Printf("Ошибка при принятии подключения: %v", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Регистрируем клиента и запускаем обработчик
|
||||||
|
s.clientsMux.Lock()
|
||||||
|
s.clients[conn] = true
|
||||||
|
s.clientsMux.Unlock()
|
||||||
|
|
||||||
|
s.wg.Add(1)
|
||||||
|
go s.handleConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanString очищает строку от невидимых и управляющих символов
|
||||||
|
func cleanString(s string) string {
|
||||||
|
// Удаляем пробелы и символы новой строки с обоих концов
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
|
// Удаляем невидимые символы с помощью Map
|
||||||
|
cleaned := strings.Map(func(r rune) rune {
|
||||||
|
// Проверяем, является ли символ управляющим
|
||||||
|
if unicode.IsControl(r) {
|
||||||
|
// Разрешаем только некоторые управляющие символы
|
||||||
|
if r == '\t' || r == '\n' || r == '\r' {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
// Удаляем другие управляющие символы
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем специфичные невидимые символы
|
||||||
|
switch r {
|
||||||
|
case '\uFEFF', // Zero Width No-Break Space (BOM)
|
||||||
|
'\u200B', // Zero Width Space
|
||||||
|
'\u200E', // Left-to-Right Mark
|
||||||
|
'\u200F', // Right-to-Left Mark
|
||||||
|
'\u202A', // Left-to-Right Embedding
|
||||||
|
'\u202B', // Right-to-Left Embedding
|
||||||
|
'\u202C', // Pop Directional Formatting
|
||||||
|
'\u202D', // Left-to-Right Override
|
||||||
|
'\u202E': // Right-to-Left Override
|
||||||
|
return -1
|
||||||
|
default:
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}, s)
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleConnection обрабатывает подключение клиента
|
||||||
|
func (s *Server) handleConnection(conn net.Conn) {
|
||||||
|
defer func() {
|
||||||
|
// Удаляем клиента из списка при закрытии соединения
|
||||||
|
s.clientsMux.Lock()
|
||||||
|
delete(s.clients, conn)
|
||||||
|
s.clientsMux.Unlock()
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
|
s.wg.Done()
|
||||||
|
|
||||||
|
log.Printf("Клиент отключен: %s", conn.RemoteAddr())
|
||||||
|
}()
|
||||||
|
|
||||||
|
clientAddr := conn.RemoteAddr().String()
|
||||||
|
log.Printf("Подключен клиент: %s", clientAddr)
|
||||||
|
|
||||||
|
// Получаем IP и порт клиента
|
||||||
|
clientIP, clientPort, err := net.SplitHostPort(clientAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Ошибка парсинга адреса клиента: %v", err)
|
||||||
|
clientIP = "неизвестно"
|
||||||
|
clientPort = "неизвестно"
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
writer := bufio.NewWriter(conn)
|
||||||
|
|
||||||
|
// Отправляем приветственное сообщение клиенту
|
||||||
|
welcomeMessage := fmt.Sprintf("hello %s:%s\n", clientIP, clientPort)
|
||||||
|
_, err = writer.WriteString(welcomeMessage)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Ошибка отправки приветствия клиенту %s: %v", clientAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Flush()
|
||||||
|
log.Printf("Отправлено приветствие клиенту: %s", welcomeMessage[:len(welcomeMessage)-1])
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Устанавливаем таймаут на чтение
|
||||||
|
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||||
|
|
||||||
|
// Читаем строку до символа новой строки
|
||||||
|
message, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
log.Printf("Клиент отключился: %s", clientAddr)
|
||||||
|
} else if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
|
// Таймаут - продолжаем цикл
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
log.Printf("Ошибка чтения от клиента %s: %v", clientAddr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Очищаем строку от невидимых символов
|
||||||
|
message = cleanString(message)
|
||||||
|
|
||||||
|
if message == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отладочный вывод
|
||||||
|
log.Printf("Получено от %s: '%s' (длина: %d, байты: %v)",
|
||||||
|
clientAddr, message, len(message), []byte(message))
|
||||||
|
|
||||||
|
// Переворачиваем строку
|
||||||
|
reversed := reverseString(message)
|
||||||
|
log.Printf("Отправка: '%s' (длина: %d, байты: %v)",
|
||||||
|
reversed, len(reversed), []byte(reversed))
|
||||||
|
|
||||||
|
// Отправляем перевернутую строку обратно клиенту
|
||||||
|
_, err = writer.WriteString(reversed + "\n")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Ошибка отправки клиенту %s: %v", clientAddr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverseString переворачивает строку (безопасная версия)
|
||||||
|
func reverseString(s string) string {
|
||||||
|
// Проверяем на пустую строку
|
||||||
|
if len(s) == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Преобразуем в руны для корректной работы с Unicode
|
||||||
|
runes := []rune(s)
|
||||||
|
n := len(runes)
|
||||||
|
|
||||||
|
// Переворачиваем массив рун
|
||||||
|
for i := 0; i < n/2; i++ {
|
||||||
|
runes[i], runes[n-1-i] = runes[n-1-i], runes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Преобразуем обратно в строку
|
||||||
|
return string(runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// showStatus показывает текущий статус сервера
|
||||||
|
func (s *Server) showStatus() {
|
||||||
|
state := s.GetState()
|
||||||
|
var status string
|
||||||
|
switch state {
|
||||||
|
case StateStopped:
|
||||||
|
status = "ОСТАНОВЛЕН"
|
||||||
|
case StateRunning:
|
||||||
|
status = "РАБОТАЕТ"
|
||||||
|
case StatePaused:
|
||||||
|
status = "НА ПАУЗЕ"
|
||||||
|
}
|
||||||
|
|
||||||
|
s.clientsMux.RLock()
|
||||||
|
clientCount := len(s.clients)
|
||||||
|
s.clientsMux.RUnlock()
|
||||||
|
|
||||||
|
fmt.Print("\n=== Статус сервера ===\n")
|
||||||
|
fmt.Printf("Состояние: %s\n", status)
|
||||||
|
fmt.Printf("Подключенных клиентов: %d\n", clientCount)
|
||||||
|
fmt.Printf("Адрес: %s:%d\n", s.config.Host, s.config.Port)
|
||||||
|
fmt.Print("====================\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandHandler обрабатывает команды из консоли
|
||||||
|
func CommandHandler(server *Server) {
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|
||||||
|
// Список доступных команд
|
||||||
|
fmt.Print("\n=== Управление сервером ===\n")
|
||||||
|
fmt.Print("Доступные команды:\n")
|
||||||
|
fmt.Print(" start - запустить сервер\n")
|
||||||
|
fmt.Print(" stop - остановить сервер\n")
|
||||||
|
fmt.Print(" pause - приостановить прием новых подключений\n")
|
||||||
|
fmt.Print(" continue - возобновить прием подключений\n")
|
||||||
|
fmt.Print(" restart - перезапустить сервер\n")
|
||||||
|
fmt.Print(" status - показать статус сервера\n")
|
||||||
|
fmt.Print(" help - показать эту справку\n")
|
||||||
|
fmt.Print(" exit - завершить работу\n")
|
||||||
|
fmt.Print("==========================\n")
|
||||||
|
|
||||||
|
fmt.Print("\n> ")
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
input := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
|
switch strings.ToLower(input) {
|
||||||
|
case "start":
|
||||||
|
if err := server.Start(); err != nil {
|
||||||
|
fmt.Printf("Ошибка: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Print("Сервер запущен\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
case "stop":
|
||||||
|
server.Stop()
|
||||||
|
fmt.Print("Сервер остановлен\n")
|
||||||
|
|
||||||
|
case "pause":
|
||||||
|
server.Pause()
|
||||||
|
fmt.Print("Сервер на паузе\n")
|
||||||
|
|
||||||
|
case "continue":
|
||||||
|
server.Continue()
|
||||||
|
fmt.Print("Сервер возобновил работу\n")
|
||||||
|
|
||||||
|
case "restart":
|
||||||
|
server.Restart()
|
||||||
|
fmt.Print("Сервер перезапущен\n")
|
||||||
|
|
||||||
|
case "status":
|
||||||
|
server.showStatus()
|
||||||
|
|
||||||
|
case "help":
|
||||||
|
fmt.Print("\nДоступные команды:\n")
|
||||||
|
fmt.Print(" start - запустить сервер\n")
|
||||||
|
fmt.Print(" stop - остановить сервер\n")
|
||||||
|
fmt.Print(" pause - приостановить прием новых подключений\n")
|
||||||
|
fmt.Print(" continue - возобновить прием подключений\n")
|
||||||
|
fmt.Print(" restart - перезапустить сервер\n")
|
||||||
|
fmt.Print(" status - показать статус сервера\n")
|
||||||
|
fmt.Print(" help - показать эту справку\n")
|
||||||
|
fmt.Print(" exit - завершить работу\n")
|
||||||
|
|
||||||
|
case "exit":
|
||||||
|
fmt.Print("Завершение работы...\n")
|
||||||
|
server.Stop()
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
case "":
|
||||||
|
// Пустая строка - ничего не делаем
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Print("Неизвестная команда. Введите 'help' для списка команд.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("> ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Printf("Ошибка чтения команд: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Настройка логгера
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
|
||||||
|
// Парсинг аргументов командной строки
|
||||||
|
var (
|
||||||
|
host string
|
||||||
|
port int
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&host, "host", "0.0.0.0", "IP адрес для прослушивания")
|
||||||
|
flag.StringVar(&host, "h", "0.0.0.0", "IP адрес для прослушивания (сокращенно)")
|
||||||
|
flag.IntVar(&port, "port", 1771, "Порт для прослушивания")
|
||||||
|
flag.IntVar(&port, "p", 1771, "Порт для прослушивания (сокращенно)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Проверка порта
|
||||||
|
if port < 1 || port > 65535 {
|
||||||
|
log.Fatal("Порт должен быть в диапазоне 1-65535")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем сервер
|
||||||
|
config := Config{
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
|
||||||
|
server := NewServer(config)
|
||||||
|
|
||||||
|
// Обработка сигналов ОС (Ctrl+C)
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sig := <-sigChan
|
||||||
|
log.Printf("Получен сигнал: %v", sig)
|
||||||
|
server.Stop()
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Автозапуск сервера при старте
|
||||||
|
if err := server.Start(); err != nil {
|
||||||
|
log.Printf("Ошибка автозапуска сервера: %v", err)
|
||||||
|
fmt.Print("Используйте команду 'start' для ручного запуска.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запускаем обработчик команд
|
||||||
|
CommandHandler(server)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user