diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a64dc92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Виртуальное окружение PyCharm +venv/ +venv_*/ +env/ +.env/ +ENV/ + +# Файлы PyCharm +.idea/ +*.iml +.DS_Store + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python + +# Логи и временные файлы +*.log +*.pcap +*.pcapng + +# Секреты и конфиги +.env +token.txt +config.py +secrets.py + +# Файлы отладки +.vscode/ +*.pid \ No newline at end of file diff --git a/gitea_api_client.py b/gitea_api_client.py new file mode 100644 index 0000000..e844678 --- /dev/null +++ b/gitea_api_client.py @@ -0,0 +1,207 @@ +# gitea_api_client.py +import os +import requests +import json +import logging +from pathlib import Path + +# Загружаем .env файл для PyCharm +try: + from dotenv import load_dotenv + + # Ищем .env файл в текущей директории + env_path = Path('.') / '.env' + if env_path.exists(): + load_dotenv() + logging.info("Загружен .env файл") + else: + logging.warning(".env файл не найден, используем системные переменные") +except ImportError: + logging.warning("python-dotenv не установлен, используем os.environ") + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) + + +class GiteaClient: + """Клиент для работы с Gitea API""" + + def __init__(self): + self.base_url = os.getenv('GITEA_URL', 'https://git.vyatsu.ru') + self.token = os.getenv('GITEA_TOKEN') + + if not self.token: + # Пробуем альтернативные имена переменных + self.token = os.getenv('GITEA_TOKEN') or os.getenv('TOKEN') + + if not self.token: + raise ValueError( + "Токен не найден!\n" + "Создайте файл .env с содержимым:\n" + "GITEA_TOKEN=ваш_токен\n" + "Или установите переменную окружения GITEA_TOKEN" + ) + + self.headers = { + 'Authorization': f'token {self.token}', + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + + logging.info(f"✓ Инициализирован клиент для {self.base_url}") + + def get_user_info(self): + """Получение информации о пользователе""" + try: + response = requests.get( + f"{self.base_url}/api/v1/user", + headers=self.headers, + timeout=10 + ) + response.raise_for_status() + + user = response.json() + logging.info(f"\n👤 Информация о пользователе:") + logging.info(f" Логин: {user.get('login')}") + logging.info(f" Полное имя: {user.get('full_name', 'Не указано')}") + logging.info(f" Email: {user.get('email')}") + logging.info(f" ID: {user.get('id')}") + logging.info(f" Админ: {user.get('is_admin', False)}") + + return user + + except requests.exceptions.RequestException as e: + logging.error(f"Ошибка получения пользователя: {e}") + return None + + def create_repository(self, name, description="", private=False): + """Создание нового репозитория""" + try: + data = { + 'name': name, + 'description': description, + 'private': private, + 'auto_init': True + } + + response = requests.post( + f"{self.base_url}/api/v1/user/repos", + headers=self.headers, + json=data, + timeout=30 + ) + response.raise_for_status() + + repo = response.json() + logging.info(f"\n✅ Репозиторий создан: {repo['html_url']}") + logging.info(f" Название: {repo['name']}") + logging.info(f" Приватный: {repo['private']}") + + return repo + + except requests.exceptions.RequestException as e: + logging.error(f"Ошибка создания репозитория: {e}") + if hasattr(e, 'response') and e.response: + logging.error(f"Ответ сервера: {e.response.text}") + return None + + def create_issue(self, owner, repo, title, body=""): + """Создание issue""" + try: + data = { + 'title': title, + 'body': body or "Создано через API PyCharm" + } + + response = requests.post( + f"{self.base_url}/api/v1/repos/{owner}/{repo}/issues", + headers=self.headers, + json=data, + timeout=30 + ) + response.raise_for_status() + + issue = response.json() + logging.info(f"\n📝 Issue создан: {issue['html_url']}") + logging.info(f" Номер: #{issue['number']}") + logging.info(f" Заголовок: {issue['title']}") + + return issue + + except requests.exceptions.RequestException as e: + logging.error(f"Ошибка создания issue: {e}") + return None + + +def main(): + """Основная функция""" + print("=" * 50) + print("Gitea API Клиент") + print("=" * 50) + + try: + client = GiteaClient() + + # Получаем информацию о пользователе + user = client.get_user_info() + if not user: + return + + username = user.get('login') + + print("\nВыберите действие:") + print("1. Прочитать информацию") + print("2. Создать репозиторий") + print("3. Создать issue (в существующем репозитории)") + print("4. Создать репозиторий + issue (полный тест)") + + choice = input("\nВаш выбор (1-4): ").strip() + + if choice == '1': + client.get_user_info() + + elif choice == '2': + name = input("Название репозитория: ") + desc = input("Описание: ") + private = input("Приватный? (y/n): ").lower() == 'y' + client.create_repository(name, desc, private) + + elif choice == '3': + owner = input("Владелец репозитория: ") + repo = input("Название репозитория: ") + title = input("Заголовок issue: ") + body = input("Текст issue: ") + client.create_issue(owner, repo, title, body) + + elif choice == '4': + repo_name = f"test-repo-{__import__('time').time()}" + print(f"\nСоздаю репозиторий {repo_name}...") + repo = client.create_repository( + name=repo_name, + description="Тестовый репозиторий из PyCharm" + ) + + if repo: + print("Создаю issue...") + client.create_issue( + owner=username, + repo=repo_name, + title="Тестовый issue от PyCharm", + body="Этот issue создан автоматически через Gitea API\n" + "Лабораторная работа: Работа с сетевыми соединениями" + ) + + except ValueError as e: + print(f"\n❌ Ошибка: {e}") + print("\nРешение:") + print("1. Создайте файл .env в корне проекта") + print("2. Добавьте в него: GITEA_TOKEN=ваш_токен") + print("3. Перезапустите программу") + except Exception as e: + print(f"\n❌ Неожиданная ошибка: {e}") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/http_requests_client.py b/http_requests_client.py new file mode 100644 index 0000000..63398d9 --- /dev/null +++ b/http_requests_client.py @@ -0,0 +1,40 @@ +# http_requests_client.py +import requests +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') + + +def http_get_requests(): + """HTTP GET запрос через библиотеку requests""" + + try: + url = "http://vyatsu.ru" + + # Выполняем GET запрос + logging.info(f"Запрос к {url}") + response = requests.get(url, timeout=10) + + # Информация об ответе + logging.info(f"Статус код: {response.status_code} {response.reason}") + logging.info(f"Время ответа: {response.elapsed.total_seconds():.3f} сек") + logging.info(f"Размер ответа: {len(response.content)} байт") + + # Заголовки ответа + logging.info("Основные заголовки:") + for header in ['content-type', 'server', 'date']: + if header in response.headers: + logging.info(f" {header}: {response.headers[header]}") + + # Тело ответа (первые 500 символов) + logging.info(f"Тело ответа:\n{response.text[:500]}") + + return response + + except requests.exceptions.RequestException as e: + logging.error(f"Ошибка запроса: {e}") + return None + + +if __name__ == '__main__': + http_get_requests() \ No newline at end of file diff --git a/http_socket_client.py b/http_socket_client.py new file mode 100644 index 0000000..c92ffcb --- /dev/null +++ b/http_socket_client.py @@ -0,0 +1,61 @@ +# http_socket_client.py +import socket +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') + + +def http_get_socket(): + """HTTP GET запрос через низкоуровневый socket""" + + HOST = 'vyatsu.ru' + PORT = 80 + + # HTTP запрос вручную + request = f"""GET / HTTP/1.1 +Host: {HOST} +User-Agent: PyCharm-Socket-Client +Accept: text/html +Connection: close + +""" + # Заменяем переносы строк на \r\n как требует HTTP + request = request.replace('\n', '\r\n') + + try: + # Создаем TCP соединение + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket: + logging.info(f"Подключение к {HOST}:{PORT}") + client_socket.connect((HOST, PORT)) + + logging.info("Отправка HTTP запроса...") + client_socket.sendall(request.encode()) + + # Получаем ответ + response = b'' + while True: + chunk = client_socket.recv(4096) + if not chunk: + break + response += chunk + + # Разбираем ответ + response_str = response.decode('utf-8', errors='replace') + + # Разделяем заголовки и тело + if '\r\n\r\n' in response_str: + headers, body = response_str.split('\r\n\r\n', 1) + else: + headers = response_str + body = '' + + logging.info(f"Статус: {headers.split(chr(10))[0]}") + logging.info(f"Заголовков получено: {len(headers)} символов") + logging.info(f"Тело ответа (первые 300 символов):\n{body[:300]}") + + except Exception as e: + logging.error(f"Ошибка: {e}") + + +if __name__ == '__main__': + http_get_socket() \ No newline at end of file diff --git a/run_all.py b/run_all.py new file mode 100644 index 0000000..aeb5a5e --- /dev/null +++ b/run_all.py @@ -0,0 +1,56 @@ +# run_all.py - универсальный запуск для PyCharm +import subprocess +import sys +import time +import os + + +def run_script(script_name): + """Запуск Python скрипта""" + print(f"\n{'=' * 50}") + print(f"Запуск {script_name}") + print(f"{'=' * 50}") + + result = subprocess.run([sys.executable, script_name]) + return result.returncode + + +def main(): + print("🚀 Запуск всех скриптов лабораторной работы") + print(f"Python: {sys.executable}") + + scripts = [ + ('tcp_server.py', False), # Сервер запускаем в фоне? + ('tcp_client.py', True), + ('udp_server.py', False), + ('udp_client.py', True), + ('http_socket_client.py', True), + ('http_requests_client.py', True), + ('gitea_api_client.py', True), + ] + + # Запускаем серверы + servers = [] + for script, is_client in scripts: + if not is_client: + print(f"\nЗапуск сервера: {script}") + proc = subprocess.Popen([sys.executable, script]) + servers.append(proc) + time.sleep(1) # Даем серверу время на запуск + + # Запускаем клиенты + for script, is_client in scripts: + if is_client: + time.sleep(0.5) + run_script(script) + + # Завершаем серверы + for proc in servers: + proc.terminate() + print(f"Сервер остановлен") + + print("\n✅ Все тесты завершены!") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/tcp_client.py b/tcp_client.py new file mode 100644 index 0000000..7fcc941 --- /dev/null +++ b/tcp_client.py @@ -0,0 +1,45 @@ + +import socket +import time + + +def run_tcp_client(): + SERVER_HOST = '127.0.0.1' + SERVER_PORT = 10000 + + try: + print(f"CLIENT: Подключение к {SERVER_HOST}:{SERVER_PORT}...") + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client_socket.connect((SERVER_HOST, SERVER_PORT)) + print("CLIENT: ✅ Соединение установлено\n") + + messages = [ + "Hello, TCP Server!", + "Как дела?", + "Это тестовое сообщение", + "EXIT" + ] + + for i, message in enumerate(messages, 1): + print(f"CLIENT: Отправка '{message}'") + client_socket.sendall(message.encode('utf-8')) + + response = client_socket.recv(1024) + print(f"CLIENT: Ответ '{response.decode('utf-8')}'\n") + + time.sleep(1) + + client_socket.close() + print("CLIENT: Соединение закрыто") + + except ConnectionRefusedError: + print("CLIENT: ❌ Ошибка - сервер не запущен!") + print("CLIENT: Сначала запустите 'python tcp_server_fixed.py'") + except ConnectionResetError: + print("CLIENT: ❌ Ошибка - соединение разорвано сервером") + except Exception as e: + print(f"CLIENT: ❌ Ошибка: {e}") + + +if __name__ == '__main__': + run_tcp_client() \ No newline at end of file diff --git a/tcp_server.py b/tcp_server.py new file mode 100644 index 0000000..87ab1c9 --- /dev/null +++ b/tcp_server.py @@ -0,0 +1,58 @@ + +import socket +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') + + +def run_tcp_server(): + HOST = '127.0.0.1' + PORT = 10000 + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind((HOST, PORT)) + server_socket.listen(5) + + print(f"SERVER: TCP сервер запущен на {HOST}:{PORT}") + print("SERVER: Ожидание подключений...") + print("SERVER: Нажмите Ctrl+C для остановки\n") + + try: + while True: + client_socket, client_address = server_socket.accept() + print(f"SERVER: Клиент {client_address} подключился") + + try: + while True: + data = client_socket.recv(1024) + + if not data: + print(f"SERVER: Клиент {client_address} отключился") + break + + received_text = data.decode('utf-8') + print(f"SERVER: Получено '{received_text}' от {client_address}") + + modified_data = data.upper() + client_socket.sendall(modified_data) + print(f"SERVER: Отправлено '{modified_data.decode('utf-8')}'") + + if modified_data == b'EXIT': + print(f"SERVER: Команда EXIT от {client_address}") + break + + except ConnectionResetError: + print(f"SERVER: Клиент {client_address} разорвал соединение") + except Exception as e: + print(f"SERVER: Ошибка с {client_address}: {e}") + finally: + client_socket.close() + print(f"SERVER: Соединение с {client_address} закрыто\n") + + except KeyboardInterrupt: + print("\nSERVER: Остановка сервера...") + + +if __name__ == '__main__': + run_tcp_server() \ No newline at end of file diff --git a/udp_client.py b/udp_client.py new file mode 100644 index 0000000..3b6650e --- /dev/null +++ b/udp_client.py @@ -0,0 +1,43 @@ +# udp_client.py +import socket +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') + + +def run_udp_client(): + """UDP клиент - подключение без установки соединения""" + + SERVER_HOST = '127.0.0.1' + SERVER_PORT = 10001 + + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client_socket: + # UDP не требует connect(), но можно вызвать для удобства + # client_socket.connect((SERVER_HOST, SERVER_PORT)) + + messages = [ + "Hello UDP Server!", + "UDP быстрее, но ненадежнее", + "EXIT" + ] + + for message in messages: + logging.info(f"📤 Отправка UDP: {message}") + + # Отправляем дейтаграмму + client_socket.sendto(message.encode('utf-8'), (SERVER_HOST, SERVER_PORT)) + + # Получаем ответ (с таймаутом) + client_socket.settimeout(2) + try: + response, server_address = client_socket.recvfrom(1024) + logging.info(f"📥 Ответ: {response.decode('utf-8')}") + except socket.timeout: + logging.warning("⚠️ Таймаут: ответ не получен") + + import time + time.sleep(1) + + +if __name__ == '__main__': + run_udp_client() \ No newline at end of file diff --git a/udp_server.py b/udp_server.py new file mode 100644 index 0000000..f3ff465 --- /dev/null +++ b/udp_server.py @@ -0,0 +1,42 @@ +# udp_server.py +import socket +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') + + +def run_udp_server(): + """UDP сервер без установки соединения""" + + HOST = '127.0.0.1' + PORT = 10001 + + # Создаем UDP сокет + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server_socket: + server_socket.bind((HOST, PORT)) + + logging.info(f"🚀 UDP сервер запущен на {HOST}:{PORT}") + logging.info("UDP не требует установки соединения, просто ждет данные...") + + try: + while True: + # Получаем данные и адрес отправителя + data, client_address = server_socket.recvfrom(1024) + received_text = data.decode('utf-8') + logging.info(f"📥 Получено от {client_address}: {received_text}") + + # Отправляем обратно в верхнем регистре + modified_data = data.upper() + server_socket.sendto(modified_data, client_address) + logging.info(f"📤 Отправлено {client_address}: {modified_data.decode('utf-8')}") + + if modified_data == b'EXIT': + logging.info("Получена команда выхода") + break + + except KeyboardInterrupt: + logging.info("\nСервер остановлен") + + +if __name__ == '__main__': + run_udp_server() \ No newline at end of file