socket通信でHTTPプロトコルを再現してみた話。フレームワークの便利さを実感したのと、フレームワークの下でどんな動作が行われているかを知る良い機会となりました。
ローカルサーバ(127.0.0.1:8080)を立てたのち、ブラウザでそのサーバにアクセスしてHTML表示したあとに以下プログラムを走らせると、body部分やHTTPヘッダーの情報をGETしてファイルに出力されます。ブラウザでアクセス前に以下プログラムを走らせてもHTML関連のレスポンスデータは得られないので注意!もちろん、サーバ側はクライアントからのGET要求に対しデータを返すという前提がある。
pythonのsocketドキュメントはこちら→ https://docs.python.org/ja/3/library/socket.html
client.py
import socket
import os.path
import datetime
#接続先サーバの指定
server =('127.0.0.1',8080)
#socketの設置
soc =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#serverにコネクション接続!!
soc.connect(server)
#サーバ(localhost:8080/test)から取得した情報を整理して表示
msg ='GET /test HTTP/1.1\r\n'
msg +='Host: localhost:8080\r\n'
msg +='\r\n'
#HTMLはutf-8しか対応していないのでエンコードしてサーバに送信する必要がある
soc.send(msg.encode('utf-8'))
#1回の通信で受信するデータ量を指定(大体2048の倍数)したのち、
#サーバから受信したメッセージをデコード
data =soc.recv(4096).decode('utf-8')
#ファイルを開いて、取得した情報を"response.txt"として出力
f =open('response.txt',mode='a') #mode = w:上書き / a:追記
#出力ファイルの場所と名前指定
filepath ='./response.txt'
#今回は追記するので前回分との境目が分かるようにラインを引いた
lines ="\r\n#############################\r\n"
#タイムスタンプ関連の表示設定
p =os.stat(os.path.abspath(filepath))
dt =datetime.datetime.fromtimestamp(p.st_mtime)
#複数の引数を指定したい場合↓は「.writelines」を用いる
f.writelines((data,"\r\nSave Date:",dt.strftime("%Y-%m-%d %H:%M:%S"),lines))
#ファイル閉じる
f.close()
print('Completed writing response to response.txt in the current dir.\r\n' + str(data))
#socketを閉じる
soc.close()
print("Client Stopped...")
client側にはconnectionという概念がないので、socketを開通したり、閉じたりだけでOK。
HTTPプロトコルが1回1回の通信でセッションを切るのに対し、socket通信は指定するまで通信をどこで切るか(どこまでが1回の通信なのか不明)判断してくれないので、こちらで事前に指定する必要がある。