MicroPythonのTCP通信をパケットキャプチャする

Ubuntu上に構築したWiFiアクセスポイント(参考記事1, 参考記事2)にMicroPythonを接続し、
UbuntuとMicroPythonの間でTCP通信を行う。
更にUbuntu上でtcpdumpを動かして通信のパケットキャプチャも試みる。

スポンサーリンク

Python3でサーバとクライアントを準備

いきなりUbuntuとMicroPython間で通信させるのではなく、まずは練習がてらUbuntuのホストでサーバとクライアントを両方動かして実験する。コードはPython公式ドキュメントのサンプルプログラムをそのまま利用させて頂いた。

サーバのソースは以下。socketserverを使った非常にシンプルな物。クライアントから送信されたアルファベット文字列を全て大文字にして返す。

tcpserver.py(Python3)

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    サーバのリクエストハンドラクラス

    サーバへの接続があるたびにインスタンスが生成される。
    クライアントとの通信のためにhandle()メソッドがオーバーライドされなくてはならない。
    """

    def handle(self):
        # self.requestはクライアントに接続したTCPソケット
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # 単に大文字にして返すだけ
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "192.168.11.1", 9999

    # localhostのポート9999にバインドしてサーバ開始
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Ctrl-Cで止めるまでサーバとして動作
        server.serve_forever()

そしてクライアント。コマンドライン引数に与えた文字列を結合してサーバに送る。

tcpclient.py(Python3)

import socket
import sys

HOST, PORT = "192.168.11.1", 9999
data = " ".join(sys.argv[1:])

# ソケットの作成 (SOCK_STREAM はTCPソケット)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # サーバへ接続してデータを送る
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))

    # サーバからのデータを受信して終了
    received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))

サーバを以下で起動する。Ctrl-Cで止めるまで動作する。

$ python3 tcpserver.py 

クライアントを実行。全て大文字に変換されて応答が返ってきた。

$ python3 tcpclient.py Hello Python Server!
Sent:     Hello Python Server!
Received: HELLO PYTHON SERVER!

クライアントをMicroPythonに移植

上記tcpclient.pyソースコードから以下の変更を行う

  • socketをusocketに変更
  • connectメソッドの書式を変更(getaddrinfoメソッド経由でのIPアドレス指定)
  • 中括弧を使ったテキスト整形を使わないようにする

さて、修正したコードをMicroPythonで実行してみると…エラー。エラーメッセージの内容は「socketオブジェクトに__exit__属性が無い」と言っている。どうやらusocketに__exit__メソッドが実装されていなくてwith構文が使えないようだ。

というわけで、上記変更点に加えてwith構文を使わずに記述したコードがこちら。

tcpclient.py(MicroPython)

import usocket

HOST, PORT = "192.168.11.1", 9999
data = "Hello Python Server!"

# ソケットの作成 (SOCK_STREAM はTCPソケット)
sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM);
saddr = usocket.getaddrinfo(HOST,PORT)[0][-1]
# サーバへ接続してデータを送る
sock.connect(saddr)
sock.sendall(bytes(data + "\n", "utf-8"))

# サーバからのデータを受信して終了
received = str(sock.recv(1024), "utf-8")
sock.close()

print("Sent:     " + data)
print("Received: " + received)

ctrl-Eで貼付けモードにして、上記コードをコピペする。
コピペ後ctrl-Dを押すとすぐにプログラムが実行される。

>>> 
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== import usocket
=== 
=== HOST, PORT = "192.168.11.1", 9999
:(略)
=== print("Received: " + received)
=== 
Sent:     Hello Python Server!
Received: HELLO PYTHON SERVER!
>>> 

Python3版と同じ結果が得られた。

tcpdumpでパケットキャプチャしWiresharkで確認する

Ubuntu上に構築したiFiアクセスポイントのインタフェースwlp2s0に対してtcpdumpを実行し、続けてMicroPythonクライアントを実行させた後ctrl-Cでtcpdumpを止める。

$ sudo tcpdump -i wlp2s0 -s 0 -w pytcp.pcap
tcpdump: listening on wlp2s0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C10 packets captured
10 packets received by filter
0 packets dropped by kernel

10パケット取得出来たようだ。Wiresharkで確認。

IPアドレスはMicroPythonが192.168.11.101、サーバが192.168.11.1。
Frame 4でMicroPythonから文字列’Hello Python Server!’のパケットがサーバへ行き、
Frame 6でサーバからMicroPythonへ’HELLO PYTHON SERVER!’のパケットが戻されているのが確認出来た。

コメント