Python websocket-clientでバイナリデータの送受信

websocket-clientでWebSocket通信してみました。
Python websocket-clientでBottleフレームワークと通信する

バイナリデータの送受信を試してみます。


バイナリデータの送信



サンプルとして画像のバイナリデータを送信。
確認用にサーバー側でファイルに復元。
クライアントには送信されてきたバイナリデータをそのまま返却してみます。

こちらが参考になりました。
Python websocket client: sending binary content


・app.py


  1. import json
  2. import bottle
  3. import gevent
  4. from bottle.ext.websocket import GeventWebSocketServer
  5. from bottle.ext.websocket import websocket
  6. app = bottle.Bottle()
  7. @app.route('/wsbin', apply=[websocket])
  8. def wsbin(ws):
  9.     while True:
  10.         body = ws.receive()
  11.         if not body:
  12.             break
  13.         
  14.         # 確認用に受信したデータをファイルに出力
  15.         with open('debug_app.jpg', 'wb') as f:
  16.             f.write(body)
  17.         # そのまま送り返す
  18.         ws.send(body)
  19.         
  20. app.run(host='localhost', port=8080, reloader=True, debug=True, server=GeventWebSocketServer)




・client.py


  1. import json
  2. import time
  3. import urllib.request
  4. import urllib.parse
  5. import websocket
  6. url = 'ws://localhost:8080/wsbin'
  7. # 画像データ読み込み
  8. with open('images.jpg', 'rb') as f:
  9.     data = f.read()
  10. ws = websocket.create_connection(url)
  11. # sendではなくsend_binaryでデータ送信
  12. ws.send_binary(data)
  13. body = ws.recv()
  14. # 確認用に受信データをファイルに保存
  15. with open('debug_client.jpg', 'wb') as f:
  16.     f.write(body)
  17. ws.close()




ポイントは、sendではなくsend_binaryでデータ送信する点でしょうか。
受信部分はrecvのまま変更しなくてもOKでした。

適当な画像ファイルimages.jpgを用意して実行してみると、同じ内容の
debug_app.jpg
debug_client.jpg
が出力されることが確認できました。


【参考URL】
Python websocket client: sending binary content
関連記事

Python websocket-clientでBottleフレームワークと通信する

BottleフレームワークでWebSocketが使用できるよう構成し、JavaScriptで通信してみました。
BottleフレームワークでWebsocket通信を行う

Pythonからの通信を試してみます。


websocket-client



こちらを使用して接続することにしました。
https://github.com/websocket-client/websocket-client

pipでインストールしておきます。


$ pip3 install websocket-client





http通信のサンプル



jsonデータを送信しサーバー側で解析。
jsonデータの応答を返すサンプルを動かしてみることにします。

まず、http通信でのサンプル。

・app.py


  1. import json
  2. import bottle
  3. import gevent
  4. from bottle.ext.websocket import GeventWebSocketServer
  5. from bottle.ext.websocket import websocket
  6. app = bottle.Bottle()
  7. @app.route('/httpecho', method='POST')
  8. def httpecho():
  9.     body = bottle.request.body
  10.     data = body.read().decode('utf-8')
  11.     json_data = json.loads(data)
  12.     return json.dumps({'status':'ok', 'message': 'hello ' + json_data['name']})
  13. app.run(host='localhost', port=8080, reloader=True, debug=True, server=GeventWebSocketServer)




・client.py


  1. import json
  2. import urllib.request
  3. import urllib.parse
  4. url = 'http://localhost:8080/httpecho'
  5. data = json.dumps({'name': 'symfoware'})
  6. response = urllib.request.urlopen(url, data.encode('utf-8'))
  7. body = response.read().decode('utf-8')
  8. result = json.loads(body)
  9. print(result['message'])




http通信でjson通信できました。


$ python3 client.py
hello symfoware



これをWebSocket版に変更してみます。




WebSocket



WebSocket版のサンプルは以下のようになりました。

・app.py


  1. import json
  2. import bottle
  3. import gevent
  4. from bottle.ext.websocket import GeventWebSocketServer
  5. from bottle.ext.websocket import websocket
  6. app = bottle.Bottle()
  7. @app.route('/wsecho', apply=[websocket])
  8. def wsecho(ws):
  9.     while True:
  10.         body = ws.receive()
  11.         if not body:
  12.             break
  13.         
  14.         json_data = json.loads(body)
  15.         ws.send(json.dumps({'status':'ok', 'message': 'hello ' + json_data['name']}))
  16. app.run(host='localhost', port=8080, reloader=True, debug=True, server=GeventWebSocketServer)




・client.py


  1. import json
  2. import websocket
  3. url = 'ws://localhost:8080/wsecho'
  4. data = json.dumps({'name': 'symfoware'})
  5. ws = websocket.create_connection(url)
  6. ws.send(data)
  7. body = ws.recv()
  8. result = json.loads(body)
  9. print(result['message'])
  10. ws.close()




実行結果に変化はありませんが、WebSocketでの通信を確認できました。


$ python3 client.py
hello symfoware






通信速度の比較



http、WebSocket各々1,000回通信した際の速度を比較してみました。

・app.py


  1. import json
  2. import bottle
  3. import gevent
  4. from bottle.ext.websocket import GeventWebSocketServer
  5. from bottle.ext.websocket import websocket
  6. app = bottle.Bottle()
  7. @app.route('/httpecho', method='POST')
  8. def httpecho():
  9.     body = bottle.request.body
  10.     data = body.read().decode('utf-8')
  11.     json_data = json.loads(data)
  12.     return json.dumps({'status':'ok', 'message': 'hello ' + json_data['name']})
  13. @app.route('/wsecho', apply=[websocket])
  14. def wsecho(ws):
  15.     while True:
  16.         body = ws.receive()
  17.         if not body:
  18.             break
  19.         
  20.         json_data = json.loads(body)
  21.         ws.send(json.dumps({'status':'ok', 'message': 'hello ' + json_data['name']}))
  22. app.run(host='localhost', port=8080, reloader=True, debug=True, server=GeventWebSocketServer)




・client.py


  1. import json
  2. import time
  3. import urllib.request
  4. import urllib.parse
  5. import websocket
  6. def http_call():
  7.     start_time = time.perf_counter()
  8.     url = 'http://localhost:8080/httpecho'
  9.     data = json.dumps({'name': 'symfoware'})
  10.     for i in range(1000):
  11.         response = urllib.request.urlopen(url, data.encode('utf-8'))
  12.         body = response.read().decode('utf-8')
  13.         result = json.loads(body)
  14.     print('http: %.4f sec' % (time.perf_counter() - start_time))
  15.     
  16. def ws_call():
  17.     start_time = time.perf_counter()
  18.     url = 'ws://localhost:8080/wsecho'
  19.     data = json.dumps({'name': 'symfoware'})
  20.     ws = websocket.create_connection(url)
  21.     
  22.     for i in range(1000):
  23.         ws.send(data)
  24.         body = ws.recv()
  25.         result = json.loads(body)
  26.     ws.close()
  27.     print('ws: %.4f sec' % (time.perf_counter() - start_time))
  28. if __name__ == '__main__':
  29.     http_call()
  30.     ws_call()




実行結果


$ python3 client.py
http: 0.4717 sec
ws: 0.1240 sec



WebSocketの方が4倍程度高速になりました。

関連記事

BottleフレームワークでWebsocket通信を行う

Python製軽量Webフレームワーク Bottle
https://bottlepy.org/docs/dev/

単体ではWebsocket通信を行うことはできませんが、プラグインを導入することで対応できるようです。
BottleでWebsocketする
bottle-websocket


ライブラリのインストールとサンプル



bottle-websocketと、依存しているgevent、gevent-websocketをpipでインストール。


$ pip3 install gevent
$ pip3 install gevent-websocket
$ pip3 install bottle-websocket




サンプルを参考にテキストに文字を入力したら「echo:」という文字列を付与した応答を送信するプログラムを作成しました。

・app.py


  1. import bottle
  2. from bottle.ext.websocket import GeventWebSocketServer
  3. from bottle.ext.websocket import websocket
  4. app = bottle.Bottle()
  5. @app.route('/')
  6. def main():
  7.     return bottle.static_file('index.html', root='./')
  8. @app.route('/websocket', apply=[websocket])
  9. def echo(ws):
  10.     while True:
  11.         msg = ws.receive()
  12.         if msg is not None:
  13.             ws.send('echo:' + msg)
  14.         else:
  15.             break
  16. app.run(host='localhost', port=8080, reloader=True, debug=True, server=GeventWebSocketServer)




・index.html


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  5.     <title>WebSocket Sample</title>
  6.     <meta charset="utf-8">
  7.     <meta name="viewport" content="width=device-width, initial-scale=1">
  8.     <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
  9. <script>
  10. $(function() {
  11.     if (!window.WebSocket) {
  12.         if (window.MozWebSocket) {
  13.             window.WebSocket = window.MozWebSocket;
  14.         } else {
  15.             $('#result').append("Your browser doesn't support WebSockets.");
  16.         }
  17.     }
  18.     
  19.     ws = new WebSocket('ws://127.0.0.1:8080/websocket');
  20.     ws.onopen = function(evt) {
  21.         $('#result').append('<li>WebSocket connection opened.</li>');
  22.     }
  23.     ws.onmessage = function(evt) {
  24.         $('#result').append('<li>' + evt.data + '</li>');
  25.     }
  26.     ws.onclose = function(evt) {
  27.         $('#result').append('<li>WebSocket connection closed.</li>');
  28.     }
  29.     $('#send').submit(function() {
  30.         ws.send($('input:first').val());
  31.         $('input:first').val('').focus();
  32.         return false;
  33.     });
  34.     $('#message').on('keyup', function() {
  35.         ws.send($('#message').val());
  36.     });
  37. })
  38. </script>
  39. </head>
  40. <body>
  41.     <h3>WebSocket Sample</h3>
  42.     <div>
  43.         <input type="text" id="message" />
  44.     </div>
  45.     <ul id="result"></ul>
  46. </body>
  47. </html>




狙い通りの動作です。

a15_01.png


関連記事

動画をMotion JPEG(multipart jpeg)に変換し、Bottleフレームワークで配信する

こちらの記事で知ったのですが、
OpenCV – Stream video to web browser/HTML page
Motion JPEGという連続して画像を配信する規格があるそうです。

動画ファイルから画像を切り出して加工。
ブラウザに配信するということが簡単に実現できますね。

参考サイトではwebフレームワークにFlaskが使用されていますが、
Bottleを使用して同様の配信を試してみます。

動画ファイルの読み込みにはOpenCVを利用します。



サンプル



サンプルはこのようになりました。

・app.py


  1. import time
  2. import bottle
  3. import cv2
  4. app = bottle.Bottle()
  5. def gen():
  6.     
  7.     cam = cv2.VideoCapture('travelpockets_iceland_land_of_fire_and_ice.mp4')
  8.     while True:
  9.         # 動画から1フレーム分の画像を読み込み
  10.         ret_val, image = cam.read()
  11.         if not ret_val:
  12.             break
  13.         # jpg形式に変換
  14.         flag, frame = cv2.imencode('.jpg', image)
  15.         yield b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + bytearray(frame) + b'\r\n\r\n'
  16.         # 連続再生されるのでwaitを入れる
  17.         time.sleep(1/60)
  18. @app.route('/')
  19. def main():
  20.     return bottle.static_file('index.html', root='./')
  21. @app.route('/video_feed')
  22. def video_feed():
  23.     bottle.response.content_type = 'multipart/x-mixed-replace;boundary=frame'
  24.     return gen()
  25. app.run(host='localhost', port=8080, reloader=True, debug=True)




・index.html


  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  5.     <title>mjpeg Sample</title>
  6.     <meta charset="utf-8">
  7.     <meta name="viewport" content="width=device-width, initial-scale=1">
  8. </head>
  9. <body>
  10.     <h3>mjpeg Sample</h3>
  11.     <div>
  12.         <img src="./video_feed">
  13.     </div>
  14. </body>
  15. </html>




再生する動画はこちらからお借りしました。
https://mazwai.com/video/iceland-%7C-land-of-fire-and-ice/455108


サーバーを起動


$ python3 app.py



ブラウザでhttp://localhost:8080/にアクセスすると、動画のように連続して画像が表示されました。

a14_01.png


【参考URL】
OpenCV – Stream video to web browser/HTML page
https://github.com/mrxmamun/camera-live-streaming/blob/master/app.py
Python Live Video Streaming Example


関連記事

Ryzen + MSI マザーボードで仮想化サポートの有効化(kvm.koの利用)

この構成でパソコンを作成しました。
Ubuntu 18.04 + Ryzen 9の動作確認構成

Ryzen 9 + MSIのマザーボードの組み合わせです。

KVMをインストールして起動したところこんな警告が表示されました。

KVM を利用できません。これは KVM パッケージがインストールされていない。
または、KVM のカーネルモジュール (kvm.ko) が読み込まれていないことを意味します。



CPUの仮想化サポートが有効になっていないようです。


BIOSでの設定変更



MSI MPG X570 GAMING PRO CARBON WI-FI ATX マザーボード [AMD X570チップセット搭載] MB4780


上記マザーボードでの設定変更例となります。
起動時にF12(だったはず)を押して、BIOSの設定画面に入ります。

「OC(Over Clock)」の項目を選択。

表示される選択肢の中から「CPU Features」の項目を選択。

「SVM Mode」を「Disabled」から「Enabled」に変更して設定を保存。

これでCPU仮想化が有効となり、kvm.koの読み込みエラーがなくなりました。
関連記事

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
Symfoware まとめ

PR




検索フォーム

月別アーカイブ