【Python】ネットワークプログラムの基礎


今回はpythonによるネットワークプログラミングに触れていきたいと思います


ネットワークプログラミングのまえに

ネットワークプログラミング

pythonを使って簡単なネットワークプログラムを作成していきます。
ここではクライアント機能とサーバー機能を一つにしたプログラムを作成します。
python2とpython3で一部記述を買えなければいけない部分があるため、
その部分はコメントとして記載しました。

from socket import *
from sys import *
import threading

## data send and receive for server
def serverLoop(soc, addr):
    while True:
        # python3
        res = soc.recv(4096).decode()
        # python2
        #res = soc.recv(4096)
        if res == "exit":
            soc.close()
            print("[*] Connection Closed {}:{}".format(addr[0], addr[1]))
            return
        #python3
        soc.send(">Server echo : {}".format(res).encode())
        #python2
        #soc.send(">Server echo : {}".format(res))

## connection request from client
def acceptConnect(soc):
    while True:
        acceptSoc, addr = soc.accept()
        print("[*] Connection from {}:{}".format(addr[0], addr[1]))
        th = threading.Thread(target=serverLoop, args=(acceptSoc, addr))
        th.start()

## data send and receive for client
def dataSendRecv(soc):
    while True:
        #python3
        line = input(">")
        #python2
        #line = raw_input(">")
        if line == "exit":
            #python3
            soc.send(line.encode())
            #python2
            #soc.send(line)

            soc.close()
            return
        #python3
        soc.send(line.encode())
        #python2
        #soc.send(line)

        # python3
        print(soc.recv(4096).decode())
        # python2
        #print(soc.recv(4096))

## make server socket
def makeServerSocket(ip, port):
    soc = socket(AF_INET, SOCK_STREAM)
    soc.bind((ip, port))
    soc.listen(SOMAXCONN)
    print("[*] Listen on {}:{}".format(ip, port))
    return soc

## make client socket
def makeClientSocket(ip, port):
    soc = socket(AF_INET, SOCK_STREAM)
    soc.connect((ip, port))
    print("[*] Connect to {}:{}".format(ip, port))
    return soc

def main():
    if len(argv) < 4:
        print("[*] Enter [IP Address] [Port Number] [mode]")
        exit(0)

    if int(argv[3]) == 0:
        soc = makeClientSocket(argv[1], int(argv[2]))
        dataSendRecv(soc)
    elif int(argv[3]) == 1:
        soc = makeServerSocket(argv[1], int(argv[2]))
        acceptConnect(soc)

if __name__ == '__main__':
    main()

プログラムを実行するときの引数によってクライアントで起動するかサーバーとして起動するか選択できます。
このように実行します。

クライアントとして起動
python ExClientServer.py 192.168.11.11 9999 0 
サーバーとして起動
python ExClientServer.py 192.168.11.11 9999 1 

第一引数がIPアドレスです。
クライアントとして起動する場合、通信先のIPアドレスを選択します。
サーバーとして起動する場合、通信を待ち受けるIPアドレスを選択します。
マシンのIPアドレスは
linuxとmacはifconfig
windowsはipconfig
で確認できます。

第二引数がポート番号です。
クライアントとして起動する場合接続するポート番号、
サーバーとして起動する場合接続を待ち受けるポート番号を起動します。

動作を確認してみる

動作の確認をしてみます。
環境はlinuxです。
ターミナルを2つ立ち上げます。
まずプログラムをサーバーとして起動します。
クライアントから接続要求が来るまで待ちます

$ python ExClientServer.py 192.168.11.11 9999 1
[*] Listen on 192.168.11.11:9999

もうひとつのターミナルでプログラムをクライアントとして起動します。
接続が完了したことを知らせるメッセージと、
入力を受け付けるプロンプトが表示されます。
これからクライアントは192.168.11.11というIPアドレスの9999というポートに向けてメッセージを送ることになります。

$ python ExClientServer.py 192.168.11.11 9999 0
[*] Connect to 192.168.11.11:9999
>

サーバーのターミナルに戻ってみると、
クライアントから接続が来たことを知らせるメッセージが表示されます。
これからサーバーはIPアドレスの60262というポートに向けてメッセージを送ることになります。

$ python ExClientServer.py 192.168.11.11 9999 1
[*] Listen on 192.168.11.11:9999
[*] Connection from 192.168.11.11:60262

クライアントのターミナルで何か文字を入力しEnterを押すと、
サーバーから
「Server echo : {入力した文字列}」
という形式でメッセージが届きます

解説

makeClientSocket関数とmakeServerSocket関数が先に述べたソケットを作っています。
soc = socket(AF_INET, SOCK_STREAM)
の部分ですね。
第一引数はネットワークアドレスの種類です。
AF_INETはIPv4ネットワークアドレスを使用することを表します。
第二引数はソケットの方を指定します。
tcp通信を行いたい場合、SOCK_STREAMを指定します。
udp通信を行いたい場合、SOCK_DGRAMを指定します。
これらの引数については他にもいろいろあるので興味があれば調べる感じでいいかと思います。

そして次の行からクライアントとサーバーとで異なってきます。
ますはクライアントから。
soc.connect((ip, port))
これは指定したIPアドレスとポートに対して接続する処理です。
この一行でサーバーとコネクションが接続されました。

そのころサーバーはというとこんなことをしていました。
soc.bind((ip, port))
soc.listen(SOMAXCONN)
bind関数にbindしたいIPアドレスとポート番号を指定します。
そしてlisten関数で接続待ちのキューを指定します。
引数のSOMAXXCONNはシステムに設定されている接続待ちのキューを表してます。
linuxだと128だそうです。
そしてacceptConnect関数のなかの、
acceptSoc, addr = soc.accept()
で接続を待ち受けます。
これは接続を受け付けるまで処理をブロック(その後の処理を行わない)します。
接続を受け付けるのはいつかというと、
今回でいうとクライアント側でsoc.connect((ip, port))が実行されたあとになります。
この関数が戻り値がふたつありacceptSocにクライアントとの通信用のソケットを格納され、
addrには接続してきたクライアントの情報がリスト型で格納されます。

あとはメッセージをやり取りしているだけです。
メッセージを送りたい場合はsend()関数、
受け取りたい場合はrecv()関数を使います。
ただpython3の場合、send()でデータを送る際、それをエンコードしなければいけないそうでなので、その部分の記述がpython2と異なっています。

本記事ではTCPソケットを使いました。
もう少し体系的に学びたい場合以下の書籍がいいかもしれません。
TCPだけでなくUDPやWeb系のネットワークプログラムの作り方を学ぶことができます。

最後に

pythonでネットワークプログラムのサンプルを作りました。
pythonなので割とシンプルに出来たかなと思います。
ITってインターネットが軸になっているところもあると思うので、ネットワークについて学んでおくのは得策かなと思います。
以前にARPポイズニングの記事について書きましたが、
ネットワークの知識って仕組みを理解することは簡単だがそこそこ役に立つことが多いんですよね
もちろん僕の知識はその道のプロに比べたら知識の量は大きく劣りますが。

今回は簡単なプログラムを作りましたがこれを少し発展させると色々作れます。

ネットワーク関連の記事