Python socket ソケット通信

Python学習【365日チャレンジ!】162日目のマスターU(@Udemy11)です。

物欲を押さえきれず、コストパフォーマンスの高いアジング用ロッドとリールを購入してしまいました。

おまけに【あおむしの釣行記4】のアジングエキスパートの矢野さんが紹介されていた感度抜群で、世界観が変わるというライン【シンカーアジング】まで購入してしまったので、当分物欲は押さえておかないと大変なことになりそうです。

それでは今日もPython学習を始めましょう。

昨日の復習

昨日は、socket通信の基本情報について学習しました。

socketは【プログラムとネットワークをつなげるもの】で、リアルな世界にあるものだと、電話や郵便ポストの役割と一緒でした。

要求する側と提供する側をつなげる役割を持ってる通信を接続するためのもので、TCP/IP自体をソケット通信と呼ぶこともありました。

詳細については、昨日の記事をごらんください。

今日は、socket通信を実際に処理してみましょう。

Pycharmの準備

socket通信を行うには、サーバーとクライアントが必要ですが、Pycharmで2つのPythonファイルを作成してsocket通信を行うことができるので、そのためのコードを書いていきます。

Pycharmで2つのファイルを作成して、左右の画面表示にしてから操作していきます。

Python socket ソケット通信

画面を分けるには、ファイルタブを右クリックして表示されるメニューから【Split Vertically】を選択し、不必要なタブを閉じます。

同様に下記の【Terminal】をクリックして、ターミナルウインドウを表示したあと、タブを右クリックして【Split Vertically】で画面を分けることができます。

Python socket ソケット通信

この状態でコードを書いて、ターミナルからPythonを実行します。

サーバー側コード

まずはサーバー側です。

import socket


with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind(('127.0.0.1', 50007))
    s.listen(1)
    while True:
        conn, addr = s.accept()
        with conn:
            data = conn.recv(1024)
            if not data:
                break
            print('data: {}, addr: {}'.format(data, addr))
            conn.sendall(b'Recieved:' + data)

最初にsocketパッケージをインポートします。

4行目でwithステートメントを使ってsocketを開きますが、アドレスファミリーとソケットタイプを指定します。

第1引数のAF_INETは、インターネットからのアドレスで、IPv4インターネットプロトコルを使うときに使います。

第2引数のSOCK_STREAMは、信頼性と順序性を持った双方向のバイトストリームに対応した接続です。

アドレスファミリについては、代表的なものとして、次のようなものがあります。

  • AF_INET:IPv4インターネットプロトコル
  • AF_INET6:IPv6インターネットプロトコル
  • AF_UNIX:ローカル通信用プロトコル
  • AF_PACKET:低レベルのパケットインターフェース

ソケットタイプについての代表的なものはこちら。

  • SOCK_STREAM:信頼性と順序性を持った双方向のバイトストリームに対応した接続(TCP)
  • SOCK_DGRAM:データグラムをサポートする接続(UDP)

データグラム

配送成功・到達時間・到達順序がネットワークサービスによって保証されることがないパケット交換網における基本転送単位

アドレスファミリー、ソケットタイプについての詳細は、Python公式ドキュメントを参考にしてください。

では、コードに戻りましょう。

5行目ではs.bind()でIPアドレスとポートをソケットに紐付けています。

6行目のs.listen()は、socketが接続を待つ状態にしています。
引数が1になっていますが、これはシステムが新しい接続を拒否するまでに許可する未受付の接続の数で、0以上の数字を入れます。

7行目以降はWhileループを使って、ソケットからの接続を待つ状態にしています。

8行目のs.accept()は、接続を受け付けて、接続があったときの返り値をconnaddrに代入しています。

9行目以降が接続があったときの処理で、Withステートメントを使ってdataに、ソケットから受信した結果のデータconnをbytesオブジェクトで代入しています。
引数の1024は、受け取る最大のバッファサイズになります。

11行目から14行目が実際の処理で、受信したdataが空の場合は、Withステートメントを抜けて、取得したdataaddrを表示して、クライアントにdataを返しています。

処理が終了したあとは、4行目のWhileステートメントで接続待ちの状態になります。

クライアント側コード

次にクライアント側のコードを書いてみます。

import socket


with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('127.0.0.1', 50007))
    s.sendall(b'Hello')
    data = s.recv(1024)
    print(repr(data))

サーバー側と同様、最初にsocketパッケージをインポートします。

4行目もサーバー側のコードと同じです。

5行目は、サーバー側で紐付けられているソケットにconnectしています。
6行目のs.sendall()でバイトデータのHelloを送信し、7行目のs.recv(1024)で受け取ったデータをdataに代入したあと、最後にdataの出力可能な文字列を出力しています。

実際の動き

実際にサーバー側とクライアント側でPythonファイルを実行してみた動画を作りました。

それがこちら

非常に短い動画ですが、サーバー側は、実行した時に特に何も反応がなく、接続を待っている状態になります。

その後、クライアント側を実行すると、サーバーが受け取ったデータとアドレスを表示して、クライアントがサーバーから返されたデータを表示します。

一連のやり取りが終了すれば、クライアント側はターミナルに戻りますが、サーバー側は接続を待つ状態になります。

この状態で、再度、クライアント側のPythonファイルを実行すると同じデータのやり取りが行われます。

目に見えていい感じ

プログラミングをしていると、どうしても頭の中でいろいろと組み立てながらコードを書かないといけないので、かなり脳みそを使うのですが、今回のように、サーバー側とクライアント側の処理が目に見えるようにすると非常にわかりやすくて、理解がすすみます。

理解がすすむと楽しくなってくるので、

  • 「こうしたらどうなるんだろう?」
  • 「あれやりたいからこうしてみよう」

という欲求が湧いてきて、知らない間に時間が過ぎてしまいます。

普段何気なく利用しているWebの処理が少しずつ分かっていくので、これまでより具体的なイメージが掴めるようになってきましたが、まだまだ学習することが多いので、しっかりと気を引き締めて学習を継続していきたいと思います。

それでは、明日もGood Python!