Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Курсовая / serverONE

.py
Скачиваний:
0
Добавлен:
12.02.2024
Размер:
11.83 Кб
Скачать
import sys, socket, datetime, threading, os
from PyQt5.QtCore import * 
from PyQt5.QtWidgets import *
from GUI.server_gui import Ui_MainWindow  # импорт сгенерированного файла

HOST = "127.0.0.1"
PORT = 5400

run = True # Флаг остановки дочерних потоков
servSocket = None
BUF_SIZE = 256 # Размер буфера сообшения
coords = [] # Координаты окна
lastRecv = [] # Последний запрос информации
movingStatus = 0 # произошла ли смена окна

#  отправка сообщения клиенту через сокет
def sendToClient(answer, servSocket, addr):
    ''' Параметры:
    - answer: ответное сообщение
    - servSocket: сокет сервера для отправки сообщения
    - addr: адрес клиента'''
    try:
        servSocket.sendall(answer.encode()) # Отправка ответа клиенту через сокет, предварительно закодировав его в байты
        print(f"Отправлено: {answer} кому: {addr}") # вывод в консоль
        window.addItem('Сервер: ', answer) # Метода addItem у объекта window для добавления информации о переданном сообщении

    except ConnectionError:
        print("Ошибка! Клиент отключился во время передачи сообщения!")
        global  run
        run = False

# Обработка полученного сообщения
def threadFunc(servSocket, addr, window):
    global lastRecv
    curr = []

    # Получение предыдущей информации
    for item in lastRecv:
        if (item[0] == addr): # Выводится сообщение о подключении клиента через print и метод window.addItem(), который обновляет интерфейс приложения.
            curr = item
            break

    with servSocket:
        answer = f'Клиент {addr} подключен'
        print(answer)
        window.addItem('Сервер: ', answer)# Выводится сообщение о подключении клиента через print и метод window.addItem(), который обновляет интерфейс приложения.
        
        while run:
            # Получение сообщения от Клиента
            try:
                # Получаем данные из соединения через сокет
                data = servSocket.recv(BUF_SIZE).decode()

            except ConnectionError:
                print("Ошибка! Клиент отключился во время передачи сообщения!")
                break
            
            print(f"Получено: {data} от: {addr}")
            
            if not data:
                break

            # Добавление времени в ответное сообщение 
            now = datetime.datetime.now()
            answer = now.strftime("%d-%m-%Y %H:%M:%S")
            answer += " получено"
            # Если пришел запрос на вывод имен комьюетра и пользователя
            if (data == 'send names'):
                laptop_name = socket.gethostname() # Имя компьютера 
                user_name = os.getlogin() # Имя пользователя
                data = f"\nИмя ноута: {laptop_name}\nИмя юзера: {user_name}"
                window.addItem('Клиент: ', data)
                answer += data
            else:
                # Если пришел запрос на изменение координат (-1 значит что если он ничего не нашел)
                if(data.find(" x ") != -1):
                    new_coords = data.split(" x ")
                    # проверка на ввод (нельзя вводить дробные числа и не числа)
                    if (new_coords[0].lstrip('-').isdigit() and (new_coords[1].lstrip('-')).isdigit()):
                        # Проверка изменений
                        if(curr[1] != new_coords[0] or curr[2] != new_coords[1]):
                            curr[1] = new_coords[0]
                            curr[2] = new_coords[1]
                            # Отправляем сообщение
                            window.addItem('Клиент: ', data)

                            global coords
                            coords = data
                            # перемещаем окно
                            window.changeWindowPosition()
                            QThread.msleep(100)

                            # Проверка смены координат окна
                            global movingStatus
                            if(movingStatus == 1):
                                movingStatus = 0
                                answer += " x Смена координат: Успешно"
                            else:
                                answer += " x Смена координат: Ошибка x Неправильный формат координат. Введите целочисленные значения."
                    else:
                    # Введенные координаты не являются целыми числами
                        answer = " x Смена координат: Ошибка x Неправильный формат координат. Введите целочисленные значения."
                
            # отправляем данные клиенту
            sendToClient(answer, servSocket, addr)

        answer = f'Клиент {addr} отключился'
        print(answer)
        window.addItem('Сервер: ', answer)

# Запуск сервера
def runServer():
    '''socket.AF_INET - это константа, которая указывает на использование сокета семейства IPv4.
    socket.SOCK_STREAM - это константа, которая указывает на использование потокового (TCP) сокета.
    socket.SOL_SOCKET - это уровень сокета, который указывает, что опция применяется к сокету в целом.
    socket.SO_REUSEADDR - это опция сокета, которая позволяет повторно использовать локальный адрес для привязки на серверной стороне, 
    даже если он все еще находится в состоянии TIME_WAIT после закрытия предыдущего соединения.
    ''' 
    global HOST, PORT
    
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serv_sock:
        serv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # Привязка сокета к указанному хосту (адресу) и порту 
        serv_sock.bind((HOST, PORT))
        serv_sock.listen(1)
        print("Сервер запущен")

        try:
            while True:
                global servSocket

                print("Ожидает подключения...")
                # Принятие подключения от клиента, возвращается сокет клиента и его адрес
                servSocket, addr = serv_sock.accept()
                # Добавление информации о клиенте в список lastRecv
                lastRecv.append([addr, 0, 0])
                # Создание и запуск нового потока для обработки клиента
                t = threading.Thread(target=threadFunc, args=(servSocket, addr, window))
                t.start()

        except KeyboardInterrupt:
            print("Сервер остановлен!")

        finally:
            global run
            # Установка флага run в False для остановки основного потока
            run = False
            serv_sock.close()

# Класс, необходимый для смены координат окна сервера
class generate_insert_frame(QThread):
    threadSignal = pyqtSignal(str) # Создание сигнала для передачи строки

    def __init__(self):
        super().__init__()
    # отправка сигнала и завершение работы потока
    def run(self):
        global coords

        self.threadSignal.emit(coords) # Отправка сигнала со значением координат
        self.msleep(100)
        self.stop()

    def stop(self):
        self.quit() # Завершение работы потока

# Пользовательский интерфейс
class mywindow(QMainWindow):
    def __init__(self):
        super(mywindow, self).__init__()
        self.ui = Ui_MainWindow() # Создание экземпляра пользовательского интерфейса
        self.ui.setupUi(self) # Настройка пользовательского интерфейса
        self.setWindowTitle("Сервер 1") # Установка заголовка окна

    def changeWindowPosition(self):
    # Механизм сигналов/слотов передает информацию в главный поток из дочернего 
    # так как moveWindow() работает только из главного потока
        self.thread = generate_insert_frame() # Создание экземпляра потока
        self.thread.threadSignal.connect(self.moveWindow)
        self.thread.start()

    def moveWindow(self):
        global coords
        # Разделение координат на отдельные значения
        coords = coords.split(' x ')
        # рпеобразование в целочисленный тип 
        coords[0] = int(coords[0])
        coords[1] = int(coords[1])
        # перемещение кона
        self.move(coords[0], coords[1])
    # функция-обработчик события перемещения окна
    def moveEvent(self, e):
        global movingStatus
        # Установка статуса перемещения
        movingStatus = 1
        # Вызов родительского метода moveEvent()
        super(mywindow, self).moveEvent(e)

    def addItem(self, str, data):
        self.ui.listWidget.addItem(str + data)
# Запуск сервера в главном потоке
if __name__ == '__main__':
    # Проверка на запушенный экземпляр приложения
    lockfile = QLockFile(QDir.tempPath() + '/server_1.lock')
    # Попытка блокировки файла блокировки (чтобы мог запускаться только 1 экземпляр сервера)
    if lockfile.tryLock(100):
        app = QApplication(sys.argv)
        
        window = mywindow()
        # Создание и запуск потока сервера в отдельном потоке
        serverThread = threading.Thread(None, runServer)
        serverThread.start()
        window.show()
        # Завершение программы вместе с выполнением цикла событий приложения
        sys.exit(app.exec_())
    else:
        print("Сервер 1 уже запущен!")
Соседние файлы в папке Курсовая