起こったこと
Python初心者が、PythonでTCP通信を試してみようとしたときのことです。
↓このサイトを参考にやってみていました。
"PythonソケットによるTCP通信入門"
https://nayutari.com/python-socket#
そしてできたのが以下のコード。
(上記のサイトの"2.5.ブロードキャスト対応"と、一部を除いて、内容は同じです。)
import socket
import threading
IPADDR = "127.0.0.1"
PORT = 49152
sock_sv = socket.socket(socket.AF_INET)
sock_sv.bind((IPADDR, PORT))
sock_sv.listen()
# クライアントのリスト
sock_list = []
def recv_client(sock, addr):
while True:
try:
data = sock.recv(1024)
if data == b"":
break
print("$ say client:{}".format(addr))
# 受信データを全クライアントに送信
for sock in sock_list:
sock[0].send(data)
except ConnectionResetError:
break
# クライアントリストから削除
client_list.remove((sock, addr))
print("- close client:{}".format(addr))
sock.shutdown(socket.SHUT_RDWR)
sock.close()
# クライアント接続待ちループ
while True:
sock_cl, addr = sock_sv.accept()
# クライアントをリストに追加
sock_list.append((sock_cl, addr))
print("+ join client:{}".format(addr))
thread = threading.Thread(target=recv_client, args=(sock_cl, addr))
thread.start()
このコードで動かしてみたところ、問題なく動いていたように思えたのですが、
以下の動きを試したとき、クライアント側からの送信を受け付けなくなってしまいました。
- クライアントを2つ以上接続する。
- 1つ目に接続したクライアントから送信し、その後2つ目に接続したクライアントからも送信。
- もう一度、1つ目に接続したクライアントから送信しようとすると、以下のエラーが出て、受信がされない。
Exception in thread Thread-1 (recv_client):
Traceback (most recent call last):
File "C:\Users\(ユーザ名)\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1009, in _bootstrap_inner
self.run()
File "C:\Users\(ユーザ名)\AppData\Local\Programs\Python\Python310\lib\threading.py", line 946, in run
self._target(*self._args, **self._kwargs)
File "c:\Users\(ユーザ名)\OneDrive\ドキュメント\server.py", line 17, in recv_client
data = sock.recv(1024)
AttributeError: 'tuple' object has no attribute 'recv'
このエラー文は、後からこの記事を書くために、クライアントもpythonにして試したときに出たもので、最初に試したときは出ていませんでした。
(クライアント側を別言語で作っていたからだと思われます)
なので、最初はエラー文も出ず、なぜこうなっているのか原因が分かりませんでした。
ですが、ここで参考サイトの元コードとの違いに気づきました。
# 受信データを全クライアントに送信(元のコード)
for client in client _list:
client[0].send(data)
# 受信データを全クライアントに送信(今のコード)
for sock in sock_list:
sock[0].send(data)
別言語で書いていたクライアント側の変数名と合わせるために、変数名をsockに変えていました。
試しに、これを元コードと同じく、clientに変えてみると、先ほどの動作をしても、動くようになりました。
なぜ?
しかしなぜ、変数名の違いで動作が適切に行われなかったのでしょうか?
色々試してみたところ、以下のようにrecv_client()の引数の名前と、for文内でループで使われる名前が一致した際に、このようなエラーが出ました。
def recv_client(sock, addr):# ←ここの変数名がsock
while True:
try:
data = sock.recv(1024)
if data == b"":
break
print("$ say client:{}".format(addr))
# 受信データを全クライアントに送信
for sock in sock_list:# ←ここの変数名もsock
sock[0].send(data)
except ConnectionResetError:
break
後から試したときに出たエラーコードは、「タプルオブジェクトだから、recvは使えない」といった内容なので、同じsockという変数が使われて、分からなくなったのでしょうか?
ちなみに、最初にsockからclientに戻して動いたとき、
pythonでは、for文内でも同じ変数を使うと動かなくなるのか、と考え、以下のコードを試しましたが、普通に動きました。
users = ['A','B','C']
def configure_user(user):
for user in users:
print(user)
return
user = 'D'
configure_user(user)
A
B
C
おわりに
このことについて「python for」などと、雑に調べてみたのですが、はっきりとした原因は、分からずじまいでした。
とりあえず、ここから以下の学びを得ました、
- 最初からいきなりコードをいじらず、まずはそのまま動かして、試した方が良い。
- for文内とはいえ、同じ変数名はあまり使うべきではない。(それにより動く、動かないに関わらず)
以上です。ありがとうございました。