В первой части был написан сервис, который получает координаты с GPS приёмника и сохраняет их в БД SQLite3 в директории /var/gpsdata.
В этой части я опишу скрипт, который отправляет сохранённые координаты на внешний сервер.
Данные отправляются строкой «имяклиента;зашифрованнаяСтрокаИзБазы». Для шифрования строки из базы используется пакет cryptography для python3.
apt install python3-cryptography
Имя клиента нужно для того, чтобы сервер знал какой пароль использовать.
Собственно сам скрипт (client.py):
import base64 import socket import pathlib import sqlite3 import os from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from conf import config as cfg maxLines = cfg.maxLines # Зашифровать отправляемые данные def encryptData(rawData): cipher = Fernet(key) encdata = cipher.encrypt(bytes(rawData, encoding='utf8')) # возвращаем данные вида: клиент;зашифрованные-данные return b''.join([bytes(cfg.myname, encoding='utf8'),b';',encdata]) #Расшифровать полученные данные def decryptData(encdata): cipher = Fernet(key) return cipher.decrypt(bytes(encdata, encoding='utf8')) # Отправка данных на сервер def sendData2Server(rawdata): global client client.send(encryptData(rawdata)) servAnswer = decryptData(str(client.recv(4096),encoding='utf8')) return servAnswer # Пометить в БД точку как отправленную def updateTrackData(dbname, pid): conn = sqlite3.connect(dbname) cur = conn.cursor() cur.execute("UPDATE " + cfg.tablename + " SET upload=1 WHERE id=" + str(pid) ) conn.commit() conn.close() # Выбор данных для отправки на сервер def sendTrackData(track): global maxLines res = '' conn = sqlite3.connect(track) cur = conn.cursor() cur.execute("SELECT " + str(os.path.basename(track)).replace('.db','') + ", id, longitude, latitude, altitude, speed, data, vremya, upload FROM " + cfg.tablename + " WHERE upload = 0 AND speed > 0 ORDER BY id LIMIT " + str(cfg.maxLines) + ";") oldcur = cur.fetchall() conn.close() for row in oldcur: for retr in range(cfg.retry): res = bytes(sendData2Server(str(row))).decode('utf8') if res == "OK": updateTrackData(track, row[1]) maxLines = maxLines - 1 break if res == "ERR": break return '' + res ### START try: client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((cfg.server_address, cfg.server_port)) kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=bytes(cfg.salt, encoding='utf8'), iterations=390000, ) key = base64.urlsafe_b64encode(kdf.derive(bytes(cfg.mykey,encoding='utf8'))) # Логика работы такая: в директории из параметра gpsdata получаем списко файлов *.db # Каждый файл построчно (только те строки, у которых upload=0) отправляется в функции sendTrackData. # После каждой удачной отправки строки переменная maxLines уменьшается на 1. # Когда maxLines < 1 отправка данных прекращается до следующей итерации. for trackfile in sorted(pathlib.Path(cfg.gpsdata).glob('*.db'), reverse=True): upload = sendTrackData(trackfile) if upload == "OK": print('Данные ' + str(trackfile) + ' успешно переданы') elif upload == "ERR": print('Ошибка передачи данных' + str(trackfile)) break if maxLines < 1: break client.send(bytes('bye', encoding='utf8')) client.close() except socket.error: print('Не удалось подключиться к серверу ' + str(cfg.server_address))
Разместим его в директории /root/gpsTracksUploader/
В поддиректории /root/gpsTracksUploader/conf/ разместим файл конфигурации config.py:
myname = "client" # Имя клиента, который будет отправлять данные mykey = "58_btTe_ZJMm41gFP_XL6HgX7kT7vo9ywQzY_xRg6ZybrAfd" # пароль клиента salt = "Ld0GH14L1pv5itZb" # соль — это просто случайные данные, которые вы используете в качестве дополнения в вашем хеше, с целью усложнения расшифровки пароля. Одинаковая для всех клиентов сервера. server_address = "127.0.0.1" # Внешний сервер, который принимает данные server_port = 3333 # Порт на внешнем сервере maxLines = 200 # Максимальное кол-во строк, отправляемых за раз retry = 3 # Кол-во попыток отправки gpsdata = "/var/gpsdata" # Путь, где лежат sqlite3 базы сервиса gpsLog. tablename = "track" # Таблица в базе, где содержатся gps данные
Для генерации пароля (mykey) можно использовать следующий скрипт generateKey.py:
import base64 import os key = base64.urlsafe_b64encode(os.urandom(36)) print(str(key, encoding = 'utf8'))
Для генерации salt можно использовать следующий скрипт generateSalt.py:
import secrets import string def generate_alphanum_crypt_string(length): letters_and_digits = string.ascii_letters + string.digits crypt_rand_string = ''.join(secrets.choice( letters_and_digits) for i in range(length)) print(crypt_rand_string) generate_alphanum_crypt_string(16)
Скрипты generateKey.py и generateSalt.py можно сохранить на сервере и генерировать mykey и salt там.
В config.py необходимо также прописать свои myname, server_address и server_port.
Для запуска этого скрипта я набросал ещё один скрипт, который перед запуском client.py проверяет завершил ли работу ранее запущенный скрипт. Скрипт /root/gpsTracksUploader.sh:
#!/bin/bash PID=`/usr/bin/ps aux | /usr/bin/grep client.py | /usr/bin/grep -v grep | /usr/bin/awk '{print $2}'` if [ "$PID" = "" ]; then { /usr/bin/python3 /root/gpsTracksUploader/client.py }; fi
Этот скрипт запускаем по cron’у, например, каждую минуту.