5 способов сделать Python-сервер на Raspberry Pi
#1
Сегодня в большом числе проектов домашней (и не только) автоматизации используется Raspberry Pi. При этом достаточно удобно иметь не только прямой доступ к устройству, но и использовать браузер — это позволяет выполнять необходимые действия и с компьютера, и с мобильного телефона, и даже удаленно из любой точки мира.

[Изображение: 1jpurcucci3snl7fwhjjoaivp7k.png]

Допустим, у нас уже есть супер Python-программа, делающая что-то очень важное, от мигания светодиодом до управления «умным домом» или хотя бы кормушкой для кота. Я покажу разные способы, от простого к сложному, как сделать web-доступ к такому приложению, добавив немного кода.

Примечание: эта статья является своего рода «экспериментом», как-то в комментариях жаловались что на Хабре недостаточно статей для начинающих. Я попытался восполнить пробел, ну а по оценкам будет видно, имеет смысл продолжать в таком формате или нет. Профи вряд ли найдут здесь что-то кардинально новое, ну а новичкам в Linux надеюсь, будет полезно.

Настройка Raspberry Pi

Будем надеятся, что у читателя есть Raspberry Pi, которая подключена к домашней сети через WiFi или Ethernet, и читатель знает что такое IP адрес и как зайти удаленно на Raspberry Pi через SSH при помощи putty. Мы будем рассматривать так называемую headless-конфигурацию — без клавиатуры и монитора. Но перед тем, как делать что-то с Raspberry Pi, пара небольших лайфхаков.

Совет N1. Чтобы что-то удаленно делать с Raspberry Pi, на нем нужно настроить SSH, а по умолчанию он выключен. Можно пойти традиционным способом, и запустить стандартный конфигуратор, но можно сделать проще — после записи образа диска достаточно создать пустой файл ssh (без расширения) в корне SD-карты. Дальше, после загрузки Raspberry Pi, SSH будет сразу активен.

Чтобы зайти удаленно на устройство, нужно узнать IP-адрес Raspberry Pi. Для этого достаточно открыть контрольную панель своего маршрутизатора, найти там список DHCP-клиентов, скопировать оттуда нужный IP-адрес (например, это будет 192.168.1.102), и ввести команду putty.exe pi@192.168.1.102 (для Windows) или ssh pi@192.168.1.102 для Linux или OSX.

Однако, IP-адреса могут меняться, например после перезагрузки маршрутизатора, это не всегда удобно. Из этого следует Совет N2 — настроить статический IP-адрес. Для этого на Raspberry Pi выполняем команду sudo nano /etc/dhcpcd.conf, и вводим следующие настройки:


Код:
interface eth0
static ip_address=192.168.1.152/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8


Если нужен адрес WiFi, то интерфейс будет wlan0, если Ethernet то eth0. IP-адреса разумеется, нужно тоже подставить свои. После перезагрузки убеждаемся что IP-адрес правильный, введя команду ifconfig.

Теперь все готово, можем приступать к Python. Все примеры даны для Python 3.7, т.к 2.7 уже давно устарел, и поддерживать его бесмысленно. Но при небольших изменениях кода все заработает и там, если нужно. Кстати, язык Python является кроссплатформенным — это значит что весь приведенный ниже код можно запустить и на Windows и на OSX, ну и разумеется, на Raspberry Pi. Из этого следует Совет N3 — отлаживать программу можно и на обычном ПК, а уже готовую версию заливать на Raspberry Pi. Возможно, придется лишь сделать функции-обертки для методов GPIO, все остальное будет работать.

Итак, наша задача — обеспечить доступ к приложению через обычный браузер. Ибо это стильно-модно-молодежно, ну и «интернет вещей» это наше все.


Способ 1: командная строка

Самый простой способ, не требующий вообще никакого программирования.

Выбираем нужную папку на Raspberry Pi, и вводим команду:


Код:
python3 -m http.server 5000



Все, на Raspberry Pi работает файловый сервер! Достаточно зайти на страницу http://192.168.1.102:5000 и мы увидим наши файлы в браузере:

[Изображение: bhim__9w5ivrk8tpm44hujrtby0.png]

Это достаточно удобно, если нужно открыть удаленный доступ к каким-либо файлам с минимумом затраченных сил. Можно также ввести команду sudo python3 -m http.server 80 и запустить сервер со стандартным 80-м портом, это позволит не указывать порт в адресной строке браузера.

Кстати, если мы хотим, чтобы сервер работал и после закрытия терминала, можно использовать команду sudo nohup python3 -m http.server 80 & — это запустит процесс в фоне. Убить такую программу можно перезагрузкой, или вводом в командной строке команды sudo killall python3.



Способ 2: SimpleHTTPServer

Мы можем довольно просто интегрировать такой же сервер в нашу программу на Python, для этого достаточно запустить его отдельным потоком при старте программы. Теперь, нам не надо возиться с командной строкой, пока программа запущена, сервер будет работать.



Код:
import http.server
import socketserver
from threading import Thread
import os

def server_thread(port):
    handler = http.server.SimpleHTTPRequestHandler
    with socketserver.TCPServer(("", port), handler) as httpd:
        httpd.serve_forever()

if __name__ == '__main__':

    port = 8000
    print("Starting server at port %d" % port)
    os.chdir("/home/pi/Documents")
    Thread(target=server_thread, args=(port,)).start()


Команда os.chdir является опциональной, если мы хотим предоставить доступ из сервера к какой-то другой папке, кроме текущей.

Способ 3: HTTPServer


Это уже полноценный web-сервер, способный обрабатывать GET и POST-запросы, возвращать разные данные и пр. Но и кода разумеется, понадобится больше.

Рассмотрим минимально работающий вариант сервера:


Код:
from http.server import BaseHTTPRequestHandler, HTTPServer

html = "<html><body>Hello from the Raspberry Pi</body></html>"

class ServerHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(html.encode('utf-8'))
        else:
            self.send_error(404, "Page Not Found {}".format(self.path))

def server_thread(port):
    server_address = ('', port)
    httpd = HTTPServer(server_address, ServerHandler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()

if __name__ == '__main__':
    port = 8000
    print("Starting server at port %d" % port)
    server_thread(port)


Запускаем браузер, и видим в нем нашу HTML-страницу:

[Изображение: ygzg1sjqiiaijewe0_vcx_mu3lo.png]

Данный сервер несложно научить отдавать файлы, например изображения.

Добавим в HTML тег img:



Цитата:html = '<html><body><h3>Hello from the Raspberry Pi</h3><img src="raspberrypi.jpg"/></body></html>'

Исходный файл «raspberrypi.jpg» разумеется, должен лежать в папке с программой. Добавим в функцию do_GET возможность получения файлов:

Код:
def do_GET(self):
        print("GET request, Path:", self.path)
        if self.path == "/":
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(html.encode('utf-8'))
        elif self.path.endswith(".jpg"):
            self.send_response(200)
            self.send_header('Content-type', 'image/jpg')
            self.end_headers()
            with open(os.curdir + os.sep + self.path, 'rb') as file:
                self.wfile.write(file.read())
        else:
            self.send_error(404, "Page Not Found {}".format(self.path))

Запускаем сервер, и видим соответствующую картинку:

[Изображение: 9yurj9o1c452hvs930r3kdlpyng.png]

Вряд ли такой сервер выиграет конкурс веб-дизайна, но он вполне работает. Сервер несложно заставить отдавать более полезные данные, например возвращать информацию о работе программы. Для примера добавим обработчик для новой функции status:

Код:
import psutil
import json

def cpu_temperature():
    return psutil.sensors_temperatures()['cpu-thermal'][0].current

def disk_space():
    st = psutil.disk_usage(".")
    return st.free, st.total

def cpu_load() -> int:
    return int(psutil.cpu_percent())

def ram_usage() -> int:
    return int(psutil.virtual_memory().percent)

def do_GET(self):
        ...
        elif self.path == "/status":
            self.send_response(200)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            health = {'CPUTemp': cpu_temperature(), 'CPULoad': cpu_load(), "DiskFree": disk_space()[0], "DiskTotal": disk_space()[1], "RAMUse": ram_usage()}
            self.wfile.write(json.dumps(health).encode('utf-8'))

Теперь мы можем открыть в браузере ссылку http://192.168.1.102:5000/status и увидеть текущие параметры системы:



[Изображение: lkf5dx14-wkw8xplpe_tskb0wco.png]

Кстати, как можно видеть, мы отдаем данные в формате JSON, что позволит использовать их для каких-то других запросов.

Заключение

Все задуманное в одну часть не влезло. Если продолжение будет аудитории интересно, в следующей части я расскажу об интерактивном сервере с Javascript и об использовании flask.

Важно: меры безопасности

Если для Raspberry Pi будет использоваться внешний IP-адрес, обязательно стоит помнить о мерах безопасности. Может показаться что ваш мини-сервер никому не нужен, однако сейчас не составляет труда пакетно просканировать все диапазоны IP-адресов (как пример, Украина, Австрия) и найти все доступные устройства. Так что обязательно стоит поменять пароль на Raspberry Pi, и не стоит хранить на устройстве какую-либо конфиденциальную информацию (папки Dropbox, имена/пароли захардкоженные в скриптах, фото и пр).


Автор DmitrySpb79
Источник: habr.com
Ответ
Thanks given by: Tim , SX , pafnuty , MariO
#2
чем Питон сервер хорош, кто подскажет?
Ответ
Thanks given by:
#3
Хороший вопрос. То же хотел бы знать.
Вообще удобство этой затеи мне понятно, но надежность самого сервера. Ну и для большого проекта, я лично, так делать бы не стал. Нужно все таки разделять визуализацию и логику на разные кучки, тем более что некоторые вещи можно и нужно писать вообще на других языках
Ответ
Thanks given by: MariO
#4
как в плане потребления ресурсов железа сервер ?
Ответ
Thanks given by:


Перейти к форуму:


Пользователи, просматривающие эту тему: 1 Гость(ей)