Python websocket client〜WebSocket送信処理を作る〜

イントロダクション(Introduction)

今まで悪戦苦闘してきましたが、ついに糸口を見つけました。(i was struggling to websocket)

人に聞いたりしてわかったこと(i found out by ask to my friends)

  1. Python2.Xは終わるらしいのでPython3.X以上を使う方が良い
  2. レンタルサーバーでやるならJS + PHPの方が早いかも?
  3. 人間諦めも必要だ

というわけで、サーバー、表示するクライアント画面はPHPで実装(i decided to use PHP)

Microbitでのシリアルデータの受信〜Webサーバーへの送信はPython3でやることにしました。

Python3.XならWebSocketクライアントを作れるので以下を参考に作成

https://websockets.readthedocs.io/en/stable/

Python WebSocket!

自分の環境では以下のモジュールが必要でした。ので下のコマンドでインストールします。(i had to install follow ing modules)

  1. asyncio
  2. websockets

ただ、上記のサイトには

pip install websockets

の様にコマンドを実行する様に記載してあったけど(i had used following command)

pip3 install websockets

の様にPython3を指定してやる必要がありました。

いざ!実装(Implementation)

<websocketSender.py>

import asyncio
import websockets
import json

async def hello():
    async with websockets.connect("ws://zenryokuservice.com:9000/demo/server.php") as websocket:
        mes = {
            'message': 'MicrobitData',
            'name': 'WebSocket.py',
            'color': '#0B4C5F'}
        await websocket.send(mes)
        print(mes)

asyncio.get_event_loop().run_until_complete(hello())

実行結果(Results)

アクセスするURLは

http://zenryokuservice.com/project/index.php

データやキャッシュを保存していないので一度画面を閉じると何も残りません。

そして、現状ではメッセージが全部「null」になっています。

修正しなくては。。。原因は送信するJSONデータがちゃんとJSONしていない様に睨んでいます。


関連ページ一覧

お手製リクエストを飛ばす〜HTTPメッセージに関して〜

イントロダクション

Socketでリクエストを飛ばす方法を探していて見つけたのでメモ

HTTPリクエストの中身

要点1:HTTPリクエストとは?

「HTTPリクエストはクライアントが送信してサーバーにアクションを起こさせるリクエストと、サーバーの回答であるレスポンスの、2 種類のメッセージがあります。」

要点2:HTTPリクエストのデータ形式

「HTTP メッセージは ASCII でエンコードされたテキスト情報で構成されており、複数の行にまたがります。」

<HTTPメッセージ>

POST / HTTP/1.1
Host: localhost:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
Sec-Metadata: destination=image, site=same-origin
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://localhost:8000/
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.9,en;q=0.8

<メソッド>

・POST / HTTP/1.1
・GET /background.png HTTP/1.0

・HEAD /test.html?query=alibaba HTTP/1.1
・OPTIONS /anypage.html HTTP/1.0

ちょっと試してみる

通常HTTPヘッダなどを直接指定することはない(だいたいアプリケーション(フレームワークなど)側でやっている)ので今まで触らなかったがヘッダ情報にオリジナルのヘッダを加えてみた。

オリジナルヘッダ追加リクエスト

>>>> Testing <<<<<
127.0.0.1 - - [13/Dec/2018 14:54:35] "GET / HTTP/1.1" 200 -
Created Header!
File CLosed!
*** POST ***
Content-Length: 16
Content-Type: text/plain
Message: Hello

「Hello」は引数で与えたもので、実際に渡す値はなんでも良い(テキストであれば)



<オリジナルヘッダを入れた場合>

*** POST ***
Content-Length: 16
Content-Type: text/plain
Message: Hello

*** BODY ***
Hello

<オリジナルヘッダを入れない場合>

*** POST ***
Content-Length: 0
Content-Type: text/plain

*** BODY ***
None

 

Python HTTPServer 〜Soket送信時のエラー対処〜

イントロダクション

PythonのSocketからHTTPServerへのデータ送信後にレスポンスを返そうとするとエラーになる、ちなみにURLにブラウザでアクセスしたときは問題なく表示する。

<エラーメッセージ>

Exception happened during processing of request from ('127.0.0.1', 50519)
  ・
  ・
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

<HTTPServer>

class MyMinimumServerHandler(BaseHTTPRequestHandler):
    def do_PSOT(self):
        print(self.headers)
        file_path = os.getcwd() + "/first.html"
        print("*******" + file_path + "********")
        print("*******" + self.path + "********")
        try:
            self.path = os.getcwd()
            print("File Open: " + file_path)
            #file = open(file_path)
            self.send_response(200)
            self.send_header('Content-type','text-html')
            self.end_headers()
            print("Created Header!")
            self.wfile.write("Hello")
            #self.wfile.write(file.read())
            #file.close()
            print("File CLosed!")
            return
        except  IOError:
                self.send_error(400, 'File Nout Found...')

<送信側:Socket>

def sendServer(msg):
    HOST = "localhost"
    PORT = 8000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    s.send("GET / HTTP/1.1\r\n\r\n")
    s.send(msg)
    data = s.recv(1024)
    print(repr(data))
    s.close()

このエラーは、上の赤い字の部分に原因がありエンコードを指定しないといけないようでした。

解決1

self.wfile.write("Hello") → self.wfile.write(message.encode('utf-8')

※「message」は変数です。出力する文字をエンコード処理すると言うことです。

エラー2

一難去ってまた一難。。。

code 501, message Unsupported method ('POST')

上のようなエラーメッセージが出ました。

解決2

メソッドの定義ミスでした。サーバー「do_PSOT」→「do_POST」が正しい

エラー3

error: [Errno 32] Broken pipe

これはこのサイトを参照しました。が解決には至らず。。。

これは、ブラウザに返信の処理を行う場合はエラーが出ず、Socket送信した時に出るエラーなのですが、よく考えてみれば表示(出力)先がないならエラーになるよなぁ。。。

結果

レスポンスを返却する時に受けるもの(ブラウザなど)がないとエラーになる!当たり前か。。。


関連ページ一覧

Python Socket Server〜Python低水準ソケット通信〜

イントロダクション

タイトルにある「低水準」を見て「しょっぱそうな。。。」と感じた方、大外れでございます。

最近の言い方だと「ローレベルAPI」に属するAPI(クラス)で低水準(ローレベル)=細かい設定がいらないと言うことです。

その代わり、高レベルAPI(クラス)と違い、便利な機能が付いていないため自分で実装する必要があります。今回使用する「socket」に関しても同様なことが言えます。

いざSocket通信

もしかしたら「pip install socket」とコマンドを打つ必要があるかもしれません。今までに色々とインストールしたもので。。。

今回は、簡単なソケット通信を行い(実装し)ます。ソケット通信は古い技術です、細かいことはPyhonドキュメントを参照されたし。

概要

ソケットサーバーとクライアントを作成して以下のことを確認します。

  1. クライアントからデータを飛ばす
  2. サーバーで受け取ったものをコンソールに表示
  3. クライアントでレスポンスを受ける

実際に実行するときは、pythonを2つ起動するのでコンソール(ターミナル)も2つ立ち上げた状態になります。※背景にワードプレスくさいのがありますが、気にしないでください。。。

<実行結果:クライアント>

<実行結果:ソケットサーバー>


これは、Pythonだけで完結することができますが今までの経緯もあり、Microbitからの入力を受け付けてソケットサーバーへ通信しました。

  1. MicrobitでHttpリクエストを飛ばすためのメモ
  2. Microbitで遊ぶ〜ボタンを押す〜
  3. Microbitで遊ぶ〜シリアル通信をする〜
  4. Microbit Python 〜シリアル通信データを受け取る〜

<クライアントサイドのPythonコード>

import serial
import time
import socket

import socketTest

def main():
    # マイクロビットからシリアル通信を受け取る処理
    ser = serial.Serial('/dev/tty.usbmodemFA132', 115200,  timeout=3)
    ser.flushInput()
    ser.flushOutput()
    while True:
        try:
            print("Send data from Micro Bit with in 3 seconds!")
            data_raw = ser.read(10)
            if (data_raw != ""):
                print(data_raw)
                # シリアル通信で受け取ったデータをソケットサーバーへ
                sendServer(data_raw)
            else:
                print("It is Time out try one more!")

        except:
            print("Error")
        else:
            print("End Program")
            ser.close()
            break

def sendServer(msg):
    HOST = "localhost"
    PORT = 8000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    s.send(msg)
    # サーバーからの返信を受け取る
    data = s.recv(1024)
    print(repr(data))
    s.close()

if __name__ == '__main__':
    # 「__name__」の値がメインの時※「__name__」にはいろんな値が入ってくる
    main()

<サーバーサイドのPythonコード>

import socket
import time

def main():
    print("Exe MySocket.py")
    server = initSocket()
    #s.connect( ( "localhost", 8000 ) )
    # ソケットサーバーの受信開始処理
    con, addr = server.accept()
    print("Got connection: ")
    print(addr)
    while True:
        #クライアントからのデータ受信
        data = con.recv(1024)
        print(data)
        if not data:
            print("there is no data...")
            break
        con.send("getMessage!: " + data)

    print("Good bye!")
    con.close()

def initSocket():
    # ソケットの初期処理
    port = 8000
    sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    sock.bind(('', port))
    sock.listen(5)
    print("start listen...")
    return sock

if __name__ == '__main__':
    main()

あとは改造して、何をやろうかな?といった感じです。

関連ページ一覧

Python Http リクエスト メモ

イントロダクション

pythonでhttpサーバを起動出来るようだ。早速試してみるがimportに失敗する。

打開策

仕様してるpythonのバージョンにより以下の2種類のクラスを使う

  1. http.server
  2. SimpleHttpServer

2.の方は直接クラスをインポートするのに対し1.はパッケージをインポートする。

作成するサーバのルートディレクトリ(フォルダ)は作成したpyファイルのあるディレクトリになるようだ。

pythonでhttpサーバを作る方法はインターネット上に結構あるので問題なさそうだ(日本語のページがある)

あとは、画面のデザインを考える必要がある…一番の難敵だな…

インポートしたもの(Python)

pip install http.server

pip install SimpleHttpServer

python socket programming

リクエストの送信方法

上記の実装を行なったが、web socketのような処理は実現出来なかった。

リクエストに対して、ハンドルを行う「do_GET」メソッドに返信用のソケットを使用するつもりだったが同じURLに複数のソケットはバインドできない。(当然だな…)ので他の方法を考える