Часть 2: Отправка GPS координат из БД SQLite3 на внешний сервер.

В первой части был написан сервис, который получает координаты с 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’у, например, каждую минуту.

Запись опубликована в рубрике gps, linux. Добавьте в закладки постоянную ссылку.

Добавить комментарий