[初心者向け]Pythonでソケット通信をする
こんにちは!
普段趣味でプログラミングをぼちぼちやってます。
その中で勉強した事を素人なりにnoteにまとめています。
これからプログラミングを始めてみよう!と思っている人や、プログラミング初心者の人など、色んな人の役に立てば良いなと思っています。
今回はPythonでソケット通信をする方法を少し勉強したので自分なりにまとめてみました。
ソケット通信とは
特定の通信相手(IPネットワーク上の場合はIPアドレスとポートの組み合わせ)と紐付いた通信端点をプログラム上に生成し、これを通じてコネクションの確立やデータの送受信、切断などの処理を行う。具体的な通信方式や通信相手の指定方式が複数用意されており、同じコンピュータ上の他のプロセスとも、TCP/IPなどを利用して他のコンピュータ上のプログラムとも通信できる。
ソフトウェア開発者にとっては、ソケットの仕組みに則ってプログラムを記述すれば、具体的な通信手段や手順の詳細を知らなくてもよく、通信相手の種類や仕様を調べて相手に合わせて通信を行うコードを記述する必要もない。
IT用語辞典 e-Wordsより引用
という事らしいです。
簡単に言うと『ソケットを作成して自分のPC内の他のプログラムと通信を行なったり、他のPCと通信したりする事が出来ますよ』という事ですね。
ソケット通信は『サーバー』と『クライアント』という2つの役があります。
サーバーはサービスを提供する側で、クライアントからの接続要求を常に監視しておく必要があります。
クライアントはサービスを受ける側で、相手サーバーが稼働しているのならいつでも接続をして通信をする事が出来ます。
このソケットでの通信はOSI参照モデルやTCP/IPという概念のもと、各層のプロトコル(決まり事)に則って行われます。
OSI参照モデルでは、更に細かく分類されるみたいですが、あまり馴染みのない方はTCP/IPの方が分かりやすいかな?と思います。
データを送信する際はアプリケーション層から物理層に向かって処理されていき、受信する際は物理層からアプリケーション層に向かって処理されていきます。
OSI参照モデルと言われるものやTCP/IPに関する情報は沢山のサイトで解説されているので気になる方は調べてみてください。
今回はPythonでTCPの通信についてやっていきます。
まず通信の基本的な流れを見てから実際にコードを見ていきましょう。
野球のキャッチボールに例えて簡単に説明します。
A君(クライアント)
B君(サーバー)
ある晴れた日の放課後、A君とB君が公園で遊んでいました。
A君『ねえねえB君、野球のキャッチボールしない?』
B君『いいよー!』
2人ともグローブをはめる
キャッチボールが始まる
A君『終わりにしようか』
B君『おっけー!』
A君はB君に対してキャッチボールをしないか?と尋ねているのに対してB君は承諾しています、これがTCPでいうコネクションの確立になります。
この時サーバーの役目であるB君はA君からのリクエストを常時待ち受けている必要があります。
もしB君が耳を塞いでいたらA君の声は聞こえないので『キャッチボールしない?』というA君のリクエストはB君には届かず、やりとりが成立しませんよね。
野球というのがアプリケーション層の役割ですね。
これがサッカーになるとサッカーボールを蹴ってやり取りすることになりますし、卓球だとラケットでボールを打ち合うことになりますよね。
Webページを問い合わせる場合はHTTPという決まり事、メールのやりとりをしたい場合はSMTPやPOP3などのように各アプリケーションごとに決まり事(プロトコル)があります。
そしてA君はB君に向かってボールを投げ、B君はボールを受ける。B君はA君に向かってボールを投げ、A君はボールを受ける。
最後の『終わりにしようか』でコネクションを切断しています。
ソケット通信の流れはこんな感じです。
今回は初心者の人向けという事で、クライアントからサーバーにメッセージを送信して、メッセージを受信したサーバーがそのメッセージをクライアントに返す。という単純なプログラムを作ってみます。
まずはサーバー側の処理をPythonでプログラミングしていきましょう。
ソケット通信(サーバー側)
先に全体コードを見てそこから詳細を見ていきましょう。
ファイル名はなんでも良いですが今回はserver.pyでいきます。
[server.py]
import socket
ip = '127.0.0.1'
port = 8000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((ip, port))
s.listen()
connection, address = s.accept()
print("client : {}".format(address))
while True:
receive = connection.recv(4096).decode()
if receive == "quit":
break
print("received -> message : {}".format(receive))
send_message = "あなたが入力したのは「{}」ですね。".format(receive)
connection.send(send_message.encode("utf-8"))
connection.close()
s.close()
print("Communication disconnected")
------------------------------
import socket
始めにPythonの標準ライブラリのsocketをインポートしています。pipを使ってインストールなどもしなくて大丈夫です。
------------------------------
ip = '127.0.0.1'
port = 8000
次にサーバーとして稼働する自分のPCのIPアドレスとポート番号を変数に割り当てています。
IPアドレスは文字列で"127.0.0.1"(localhostも同義)もしくはサーバーとして動かすPCのIPアドレスにします。
"127.0.0.1"は"localhost"と同じ意味で扱われ、自分のPC内のみの通信となります。
ここはホスト名で指定することも出来るみたいです。
ポート番号はウェルノウンポートと呼ばれる0〜1023の範囲以外の番号を使いましょう。8000辺りの番号であれば問題ないです。
------------------------------
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
その次にソケットを作成して変数sに割り当てています。
AF_INETはアドレスファミリの1つで、IPv4の事です。
よく目にするドット区切りの4byteの192.168.0.10とかのやつですね。
socketのAddressFamilyクラスに定数として定義されています。
SOCK_STREAMはソケットの種類の1つで、ストリームソケットという事です。
ストリームソケットはTCPを使用して通信を行えるようにしてくれます。
socketのSocketKindクラスに定数として定義されています。
------------------------------
s.bind((ip, port))
次にbindでIPアドレスとポート番号をタプルで指定して、ソケットにIPアドレスとポート番号を紐付けています。
------------------------------
s.listen()
そしてlistenでサーバーを有効にして接続を受け付けます。
listenの引数に数値を渡す事ができ、システムが新しい接続を拒否するまでに許可する未受付の接続の数を指定します。指定しない場合、デフォルトの妥当な値が選択されます。
------------------------------
connection, address = s.accept()
print("client : {}".format(address))
listenで待機している状態なので新しく接続の要求があった場合にs.accept()で接続を受け付けています。
このacceptメソッドの戻り値は、新たなソケットオブジェクトと接続先でbindしているアドレスになります。
その2つの戻り値を変数のconnectionとaddressに割り当てています。
接続された相手のアドレスを printで出力しています。
以後connectionを使って通信をしていきます。
------------------------------
while True:
receive = connection.recv(4096).decode()
if receive == "quit":
break
print("received -> message : {}".format(receive))
send_message = "あなたが入力したのは「{}」ですね。".format(receive)
connection.send(send_message.encode("utf-8"))
このwhile文で相手との接続を維持します。
connection.recv(4096).decode()で相手から送信されるデータを待ち受けてbytesオブジェクトとして受信するので、そのデータをデコード(復号化)してから変数receiveに割り当てています。
この4096はバッファと言われるもので1度に受信するデータ量を指定します。任意のbyte量を指定できますが少なすぎると途中からデータを受け取れなくなるので気をつけないといけません。
次のif文で、もしquitを受信したらこのwhile文を抜けるようにしています。
その後は受信したメッセージを出力→送信するデータを作成→エンコード(符号化)した文字列をconnection.send()を使ってクライアントに送信しています。
------------------------------
connection.close()
s.close()
print("Communication disconnected")
while文を抜けたらconnectionをclose()で切断して、sもclose()で切断しています。
最後に『接続が切れたよ』と出力しています。
長くなりましたがここまでがサーバー側の説明になります。
次はクライアント側を見ていきましょう。
ソケット通信(クライアント側)
クライアント側も先に全体コードを見てから詳細を見ていきましょう。
ファイル名はなんでも良いですが、今回はclient.pyでやります。
[client.py]
import socket
ip = "127.0.0.1"
port = 8000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
print("Connected!!!!!")
while True:
print("<メッセージを入力してください>")
message = input('>>>')
if not message:
s.send("quit".encode("utf-8"))
break
s.send(message.encode("utf-8"))
receive = s.recv(4096).decode()
print(receive + "\n")
s.close()
print("END")
------------------------------
import socket
始めにPythonの標準ライブラリのsocketをインポートしています。pipを使ってインストールなどもしなくて大丈夫です。
------------------------------
ip = "127.0.0.1"
port = 8000
次に接続するサーバーのIPアドレスとポート番号を変数に割り当てています。
------------------------------
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
サーバー側の時と同じくソケットを作成して変数sに割り当てています。
------------------------------
s.connect((ip, port))
print("Connected!!!!!")
次に、connectでIPアドレスとポート番号をタプルで指定して、サーバーのソケットに接続しています。
接続が確立されたらConnected!!!!!と出力されるようにprintしています。
------------------------------
while True:
print("<メッセージを入力してください>")
message = input('>>>')
if not message:
s.send("quit".encode("utf-8"))
break
s.send(message.encode("utf-8"))
receive = s.recv(4096).decode()
print(receive + "\n")
このwhile文で相手との接続を維持します。
まずinput関数でユーザーに入力された文字列を変数messageにわりあてています。
if文でmessageが空だった場合に、エンコード(符号化)したquitという文字列をsendでサーバーに送信した後でwhileを抜けています。
変数messageが空文字列じゃなかったら、変数messageをエンコード(符号化)してからsendでサーバーに送信しています。
その後はrecvを使って相手から送信されるデータを待ち受けてbytesオブジェクトとして受信するので、そのデータをデコード(復号化)してから変数receiveに割り当てています。
そして変数receiveを出力しています。
ループなのでまたinput関数で入力して…………と続きます。
------------------------------
s.close()
print("END")
whileを抜けたらsをcloseで切断しています。
最後に『終わったよ』と出力しています。
サーバー側プログラムとクライアント側プログラムを実行してみる
流れを確認しておくと、サーバー側のプログラムを実行してサーバーを待機状態にします。その後クライアント側のプログラムを実行してサーバー側のソケットと接続。
そして接続が確立されたらクライアント側のinput関数の部分で入力を要求されるので何か文字列を入力します。(以降は空文字列を送信しない限りこのやりとりが続きます)
ではプログラムを実行しましょう。
コマンドプロンプトやターミナルを2つ起動してプログラムを置いている階層まで移動します。
移動したら先にserver.pyを実行してからもう一つのウィンドウでclient.pyを実行します。
接続が確立されたら以下のような画面になると思います。
[server.py]
接続したクライアントのIPアドレスとポート番号が表示されました。
[client.py]
入力してくださいと表示されて入力を待機しています。
------------------------------
次にクライアント側で文字列を入力してエンターを押してサーバーに送信します。『Hello Python!!』を送信してみました。
[server.py]
受信した文字列を出力しています。
[client.py]
文字列を入力して送信したら、サーバー側から『あなたが送信したのは〇〇ですね。』というデータが返ってくるのでそれを出力しています。
その後また入力待ちになります。
------------------------------
最後にクライアント側から空文字列を送信するとお互いに接続を切断します。
[server.py]
[client.py]
お互いに接続が切断されたのでwhile文を抜けて通信が終了し、『終わったよ』と出力しています。
まとめ的なやつ
今回はPythonのsocketを使って通信する方法を見ました。
プログラムの流れは単純なので慣れればコツが分かってくると思います。
ソケット通信が出来ると自分でHTTPクライアントを作成してWebページを取得したりする事も可能です。(あまりやらないと思いますが興味があれば遊んでみてください)
その他様々な機器とも通信を行なったりする事も出来るのでプログラミングの幅が広がると思います。
今回やったのは本当に基本的な所ではありますが、通信する時はここからの応用になると思うので是非色々試してみてください!
最後まで読んでいただきありがとうございました!
では、またお会いしましょう!