テキストチャット
簡単な Chat プログラム
さっきは、一方向でデータ通信を1回するだけでしたが、今度は、双方向に通信を繰り返してみましょう。
作成するのは、簡単なチャットです。
- chat_server.pyが指定ポートで待っている
- chat_client.pyが、ホスト名とポート番号を指定して接続を行う
- 接続完了後は、クライアント、サーバが交互に一行ずつメッセージを交換する。
- どちらかが exit という入力を入れたら終了
動作イメージは、こんな感じ
server 側 client 側
[起動]
[起動]
(Client) Input:
clientMsg0 [入力]
Client says: clientMsg0 [受信]
(Server) Input:
serverMsg0 [入力]
Server says: serverMsg0 [受信]
(Client) Input:
client3 [入力]
Client says: client3 [受信]
(Server) Input:
exit [入力]
Server msg: exit [受信]
Program finished with exit code 0
Program finished with exit code 0
まずは、動かしてみて、イメージを掴んでください。
実行の際に、ターミナルを二つ同時に見れないと困りますよね。
- PyCharm の場合は、実行タブが二つあるので、一方を右か下の領域に drag すると、画面分割できるようになります。
- vscode の場合は、複数のターミナルを実行している場合は、ターミナル画面右に虫アイコンが複数現れるかと思います。虫アイコンを drag してターミナル以外のところなどに持っていくと、画面が分かれたような気がします。
接続
connection を張るまでは、先ほどの例と差はありません。 違うのは、今回は双方向通信なので、Server, Client 両側で、Input/OutputStream を両方開いている点ですね。
サーバ側
def send_server():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as serverSock:
serverSock.bind((HOST, PORT))
serverSock.listen()
sock, client = serverSock.accept()
recv_and_send(sock)
- 接続開始〜確立は同じ
- sock を用いて
recv_and_send(sock)
を実行
クライアント側
def chat_client():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((SERVER, PORT))
send_and_recv(sock)
- 接続開始〜確立は同じ
- sock を用いて
send_and_recv(sock)
を実行
交互に送受信
サーバ(受信・送信を交互に)
def recv_and_send(sock: socket):
while True:
data = sock.recv(BUFSIZE)
line = data.decode('utf-8')
print('Client says: ', line)
if line in ['exit', 'quit']:
break
print('(Server) Input: ')
user_msg = input()
sock.sendall(user_msg.encode('utf-8'))
if user_msg in ['exit', 'quit']:
break
主なI/O
input()
: ユーザ入力の取り込み, 文字列sock.recv()
: クライアントからの受信用- byte 列を文字列にするために
decode('utf-8')
する
- byte 列を文字列にするために
sock.sendall()
: クライアントへの送信用- 文字列をbyte 列にするために
encode('utf-8')
する
- 文字列をbyte 列にするために
プログラム見てもらえば、交互に受信&送信しているだけですね。
クライアント(受信・送信を交互に)
受信と送信の順序が逆になっている以外は、処理の流れはまったく同じです。
def send_and_recv(sock: socket):
while True:
print('(Client) Input: ')
user_msg = input()
sock.sendall(user_msg.encode('utf-8'))
if user_msg in ['exit', 'quit']:
break
data = sock.recv(BUFSIZE)
line = data.decode('utf-8')
print(data, line)
print('Server says: ', line)
if line in ['exit', 'quit']:
break
まとめ
交互に通信
実は、今回のプログラム互い違いに通信してました。逆にいうと、双方のユーザが好きなタイミングでメッセージを送ることができていません。 それは、プログラムが、「標準入力を待つか」「ネット入力を待つか」のどちらかしかできないからです。一度待ち始めると、途中で止めづらい。 で、このあたりはマルチスレッドをつかうと簡単に解消できるのですが、それはまた後日。
TCP 接続
今回あつかったのは TCP 接続です。 2点間の通信を担当し、パケット落ちなどがあると再送手続きなどしてくれます。 一方で、ネットワークの勉強をした人なら、 UDP 接続というのも聞いたことがあるかと思います。 パケットを直接おくるためのもので、マルチキャストやブロードキャストも可能ですが、パケット落ちなどがあっても、利用者側で対応しなくてはいけません。
今回は、再送制御などは TCP にお任せしています。