PythonでAndroidアプリ作成してみる #13 Update#1 巨大ファイル対策 バージョン認証

こんにちは、Rcatです。
#13からはアップデートとなります。

本シリーズはこちら

作品の配布はこちら


巨大ファイル対処

サーバー側

バックアップ中にサーバーが強制終了するなと思ったら、送付中のファイルサイズが16GBありました…。
現仕様では分割受信データをメモリに保持して最後に書き込みという仕様ですが、最後までメモリに保持するためメモリ使用率が大きくなってしまいます。その結果完全にサーバーのメモリオーバーで落ちてました。
ちなみにこの仕様は、当初wait()を使用していた際にリクエストが遅すぎたのでとにかく投げるだけ投げて後でまとめてwaitするという力業を取っていた時の名残で、大量のリクエストが待機状態となるため分割データが必ずしも順番通りに送信されなかったという背景があります。
少し前にコールバックフラグ方式に変更し、順番通りに受信できるようになりましたので特定サイズ以上は受信と同時にディスクに書き込むように仕様変更します。

旧仕様イメージ
新仕様イメージ

サーバー側のソースコードは以下の通りです。
分割データを受信する際は、以下のようなクラスをインスタンスして分割データの受信ということを行っています。
今回の変更点はセーブメソッドの追加です。
クラスのインスタンス時にファイルのサイズを渡すことでモードが変化します。500MB以上の場合は直接書き込みモードとなり、保存時はtmpファイルから本来のファイルパスへの移動を行います。
それより小さいファイルの場合は引き続きメモリに保持し、書き込みは1回で済ませます。

クライアント側

私のPCはメモリ48Gなので全く気になりませんでしたが、実はクライアントで分割する際も一旦データを全て取り込んでから分割しているので、こちらも読み込みながら分割するように変更します。

クライアント側の送信ループ関数です。分割されたファイルのバイナリをforで回しています。
今回のポイントとしては、forに渡す部分をジェネレーターに変更したところです。

こちらが今回変更したジェネレーターです。
この関数は入力されたファイルを特定のサイズに分割して返します。
前回と違うところは、今までは一括で分割したデータを返していたところをジェネレーターに変更したため、メモリ使用量が分割サイズ分まで減らせるという特徴があります。
単純なスプリットだと結局リストの中に全てのデータが入ってくるので、その分のメモリを消費してしまいます。
しかし、ジェネレーターを使用すればfor文で進むことにファイルの分割を行うことができます。このくらいの処理なら直接書いてもいいのかもしれませんが、せっかくなのでこういう実装にしました。

クライアンドバージョン認証

現在の仕様だとサーバーとクライアントできちんと通信できなければいけないにもかかわらず、クライアントが接続さえすれば通ってしまうような状態なので、きちんとバージョンの区別をつけます。
例えば、今回のようにファイルの分割由来の処理を変えた場合、クライアントとサーバー双方のアップデートが必要なので、こういった場合にバージョン認証で引っ掛けるようにします。

クライアント側

クライアント側のバージョンについては、パッケージングの回で紹介したバージョンの記述をそのまま使います。
具体的には以下の部分ですね。これをユーザー認証の時にユーザー名、パスワードとともにバージョンという名前で送信します。

サーバー側

サーバー側にはバージョンをチェックする関数を追加しました。
具体的にはドットで分割してからそれぞれの数値の大小を比較するだけという簡単な構造です。
もしバージョンの数値が小さい場合は403のエラーコードを返します。

サーバーの自動検索

アプリにはサーバーのIPアドレスを入力する場所がありますが、ここを自動で入力する機能を追加します。
下記記事で紹介しているM-Searchを使用してバックアップサーバーを自動で検索します。
M-Searchとは、ネットワーク上のデバイスを検索するためのもので、ポート開放のためにルーターにアクセスしたり、今回のように任意のサービスを検索するのに使うことができます。

動作はこんな感じです。
ボタンを押すだけでサーバーのIPアドレスが入力されます。

なお、現状ではWindowsでのみ動作しAndroidでは動きません。理由は端末のローカルIPアドレスを取得する方法が今のところ見つからないからです。

試したこと
socketライブラリ→ローカルループ
ipget→piコマンドが無いというエラー
subprocess & ifconfig →空の文字列

サーバー側でやること

本機能はアプリ及びサーバーとは別体なので追加でスクリプトを起動します
上記で紹介しているM-Searchの記事からスクリプトをDLして実行してください。
こちらは汎用M-Searchスクリプトなので、このアプリ用の記述を追加する必要があります。
ServiceListの中に"urn:rcat999:service:backupapp:1"を追記します。
LocationListの中にf"{UPnPServer.MyIP}:25011"を追記します。
この状態でこのスクリプトを起動することでアプリからの自動検索に応答するようになります。

クライアント側

クライアント側では検索ボタンを押したときのイベントが追加されています。
こちらもM-Searchの記事のスクリプトを追加importしてサーバー側でServiceListに追加したサービス名を検索するような動作を入れています。

アプリのファイル構成にひとつファイルが増えます

ソース配布について

ソース配布は第12回で行っています。
画像の中の伏せられている部分や紹介されていない全体像などが知りたい場合は、ぜひ配布を受けてください。
もちろん利用規約に則りご使用いただくこともできます。


情報が役に立ったと思えば、僅かでも投げ銭していただけるとありがたいです。