はじめに
Pythonのsocketを使用してサーバとクライアントのソケット通信をやってみたいと思います。
視覚的にわかり易いと思うので、TkinterでGUIを作ります。動作としてはサーバとクライアントが接続された後、サーバ側からクライアント側に設定した任意の文字列を送信すると、クライアント側からサーバ側に設定した任意の文字列を送り返すというシンプルなシーケンスです。
今回はサーバ側もクライアント側も同じPCで動かすので同一のIPアドレスを想定しています。もちろん変更すれば2台のPCでもソケット通信は可能だと思います。
開発環境
- Windows 10
- Python 3.10.5
サーバ側のプログラム
プログラム(server.py)は以下です。
import socket
import tkinter
from tkinter import *
import tkinter as tk
import threading
# グローバル
flag_start = False
flag_init_done = False
#ボタンを押した時の処理
def ButtonPush20(e):
global flag_start
flag_start = True
#ボタンを押した時の処理
def ButtonPush30(e):
global flag_send_msg
flag_send_msg = True
#ボタンを押した時の処理
def ButtonPush50(e):
global flag_start
flag_start = False
def Operation():
print('Operation')
global flag_start
global flag_init_done
global flag_send_msg
while(1):
flag_send_msg = False
if (flag_start==True):
if (flag_init_done==False):
print('Start')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), 1234)) # IPとポート番号を指定します
s.listen(5)
clientsocket, address = s.accept()
print('accept')
print('address = ', address[0])
label10_01['text'] = address[0]
flag_init_done = True
else:
if (flag_send_msg==True):
clientsocket.send(bytes(entry30_00.get(), 'utf-8'))
print('Send')
msg = clientsocket.recv(1024)
label40_01['text'] = msg.decode("utf-8")
print(msg.decode("utf-8"))
print('passed')
flag_send_msg = False
if __name__ == '__main__':
print('Program start.')
# スレッド
thread1 = threading.Thread(target=Operation)
thread1.start()
ip = socket.gethostbyname(socket.gethostname())
print(ip)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
######################## tkinter #################################################################################################
root = tkinter.Tk()
root.title('Server')
frame_00 = Frame(root, pady=10, padx=10)
frame_10 = Frame(root, pady=10, padx=10)
frame_20 = Frame(root, pady=10, padx=10)
frame_25 = Frame(root, pady=10, padx=10)
frame_28 = Frame(root, pady=10, padx=10)
frame_30 = Frame(root, pady=10, padx=10)
frame_40 = Frame(root, pady=10, padx=10)
# ラベル
label10_00 = tkinter.Label(frame_10, text="クライアントIPアドレス ", font=("MS明朝", "10"))
# ラベル
label10_01 = tkinter.Label(frame_10, text=" ", font=("MS明朝", "10"))
label10_00.pack(side=tk.LEFT)
label10_01.pack(side=tk.LEFT)
frame_10.pack(fill=tk.X)
# ラベル
label20_00 = tkinter.Label(frame_20, text="サーバスタート ", font=("MS明朝", "10"))
#ボタン作成
button20_00 = tkinter.Button(frame_20, text="スタート", fg="white", bg="blue", height="2", width="20", font=("MS明朝", "10"))
label20_01 = tkinter.Label(frame_20, text="", font=("MS明朝", "10"))
label20_00.pack(side=tk.LEFT)
button20_00.pack(side=tk.LEFT)
label20_01.pack(side=tk.LEFT)
frame_20.pack(fill=tk.X)
#ボタン処理の割り当て
button20_00.bind("<Button-1>", ButtonPush20)
# ラベル
label30_00 = tkinter.Label(frame_30, text="メッセージ送信 ", font=("MS明朝", "10"))
#ボタン作成
button30_00 = tkinter.Button(frame_30, text="送信 ", fg="white", bg="blue", height="2", width="20", font=("MS明朝", "10"))
# ラベル
label30_01 = tkinter.Label(frame_30, text=" ", font=("MS明朝", "10"))
#入力ボックス作成
entry30_00 = tkinter.Entry(frame_30, width=30, bg="white", justify='center', font=("MS明朝", "10"))
entry30_00.insert(0, "Sent from server.")
label30_00.pack(side=tk.LEFT)
button30_00.pack(side=tk.LEFT)
label30_01.pack(side=tk.LEFT)
entry30_00.pack(side=tk.LEFT)
frame_30.pack(fill=tk.X)
#ボタン処理の割り当て
button30_00.bind("<Button-1>", ButtonPush30)
# ラベル
label40_00 = tkinter.Label(frame_40, text="受信メッセージ ", font=("MS明朝", "10"))
# ラベル
label40_01 = tkinter.Label(frame_40, text="", font=("MS明朝", "10"))
label40_00.pack(side=tk.LEFT)
label40_01.pack(side=tk.LEFT)
frame_40.pack(fill=tk.X)
root.mainloop()
threadを使用して並行処理を行います。Operationで押されたボタンの状態をモニタしながら相応の処理を行います。
このプログラムを実行すると、まず以下のようなTkinterのウィンドウが表示されます。「スタート」をクリックすると取得したPCのIPアドレスとポート番号1234でクライアントとの接続を待ちます。
クライアント側のプログラム
プログラム(client.py)は以下です。
import socket
import tkinter
from tkinter import *
import tkinter as tk
import threading
# グローバル
flag_start = False
flag_init_done = False
flag_send_msg = False
ip = ''
#ボタンを押した時の処理
def ButtonPush20(e):
global flag_start
flag_start = True
#ボタンを押した時の処理
def ButtonPush30(e):
global flag_send_msg
flag_send_msg = True
#ボタンを押した時の処理
def ButtonPush50(e):
flag_start = False
def Operation():
print('Operation')
global flag_start
global flag_init_done
global flag_send_msg
global ip
while(1):
if (flag_start==False):
flag_start = False
flag_send_msg = False
flag_init_done = False
if (flag_start== True):
if (flag_init_done==False):
print('Client start')
label10_01['text'] = ip
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((socket.gethostname(), 1234)) # IPとポート番号を指定します
flag_init_done = True
else:
while (flag_start== True):
msg = s.recv(1024)
print('Received')
print(msg.decode("utf-8"))
label40_02['text'] = msg.decode("utf-8")
s.send(bytes(entry30_00.get(), 'utf-8'))
if __name__ == '__main__':
print('Program start.')
# スレッド
thread1 = threading.Thread(target=Operation)
thread1.start()
ip = socket.gethostbyname(socket.gethostname())
print(ip)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
######################## tkinter #################################################################################################
root = tkinter.Tk()
root.title('Client')
frame_00 = Frame(root, pady=10, padx=10)
frame_10 = Frame(root, pady=10, padx=10)
frame_20 = Frame(root, pady=10, padx=10)
frame_25 = Frame(root, pady=10, padx=10)
frame_28 = Frame(root, pady=10, padx=10)
frame_30 = Frame(root, pady=10, padx=10)
frame_40 = Frame(root, pady=10, padx=10)
# ラベル
label10_00 = tkinter.Label(frame_10, text="クライアントIPアドレス ", font=("MS明朝", "10"))
# ラベル
label10_01 = tkinter.Label(frame_10, text="", font=("MS明朝", "10"))
label10_00.pack(side=tk.LEFT)
label10_01.pack(side=tk.LEFT)
frame_10.pack(fill=tk.X)
# ラベル
label20_00 = tkinter.Label(frame_20, text="クライアントスタート ", font=("MS明朝", "10"))
#ボタン作成
button20_00 = tkinter.Button(frame_20, text="スタート", fg="white", bg="blue", height="2", width="20", font=("MS明朝", "10"))
label20_01 = tkinter.Label(frame_20, text="", font=("MS明朝", "10"))
label20_00.pack(side=tk.LEFT)
button20_00.pack(side=tk.LEFT)
label20_01.pack(side=tk.LEFT)
frame_20.pack(fill=tk.X)
#ボタン処理の割り当て
button20_00.bind("<Button-1>", ButtonPush20)
# ラベル
label30_00 = tkinter.Label(frame_30, text="送信メッセージ ", font=("MS明朝", "10"))
# ラベル
label30_01 = tkinter.Label(frame_30, text=" ", font=("MS明朝", "10"))
#入力ボックス作成
entry30_00 = tkinter.Entry(frame_30, width=30, bg="white", justify='center', font=("MS明朝", "10"))
entry30_00.insert(0, "This is client.")
label30_00.pack(side=tk.LEFT)
label30_01.pack(side=tk.LEFT)
entry30_00.pack(side=tk.LEFT)
frame_30.pack(fill=tk.X)
# ラベル
label40_00 = tkinter.Label(frame_40, text="受信メッセージ ", font=("MS明朝", "10"))
# ラベル
label40_01 = tkinter.Label(frame_40, text=" ", font=("MS明朝", "10"))
# ラベル
label40_02 = tkinter.Label(frame_40, text="", font=("MS明朝", "10"))
label40_00.pack(side=tk.LEFT)
label40_01.pack(side=tk.LEFT)
label40_02.pack(side=tk.LEFT)
frame_40.pack(fill=tk.X)
root.mainloop()
同様にthreadを使用して並行処理を行います。Operationで押されたボタンの状態をモニタしながら相応の処理を行います。
このプログラムを実行すると、まず以下のようなTkinterのウィンドウが表示されます。
サーバ側とクライアント側のソケット通信
以下の手順でソケット通信を行います。
- server.pyを起動します。
- client.pyを起動します。
- server.pyのGUIで、スタートボタンを押します。
- client.pyのGUIでスタートボタンを押します。サーバと接続が完了するとserver.pyのGUIにクライアント側のIPアドレスが表示されます。
- server.pyのGUIで、送信ボタンを押すと、サーバからクライアントにメッセージが送信され、クライアントからサーバにメッセージが送信されます。