質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1542閲覧

マルチプロセスにおけるリストの共有

takkun0710_graf

総合スコア1

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/05 20:57

編集2021/07/06 16:51

マルチプロセス及び、ソケット通信におけるソケットリストの共有

pythonにてクイズアプリを作っています。
サーバー側のボタン入力により、クライアント側に問題文を出力するといった機能を実装中に以下のエラーメッセージ、および事象が発生しました。

プロセス間のソケットリストの共有において有効な手段を教えていただきたいです。

問題① ボタンを押してもクライアント側に問題文が表示されない。
問題② ボタンを2回押すとサーバーのプログラムが強制終了する

TypeError: 'Queue' object is not iterable

quiz_server.py

python

1import socket 2import select 3 4import tkinter as tk 5from multiprocessing import Process,Queue 6from collections import deque 7 8 9def broadcast_quiz(sock_list_q, msg): 10 11 socklist = list(deque(sock_list_q)) 12 13 for sock in socklist: 14 #send_to でソケットに対し、メッセージを送信 15 if not send_to(sock,msg): 16 sock_list_q.deque(sock) 17 18def questions(sock_list_q,i): 19 def inner(): 20 21 sock_list_q.get() 22 23 quiz_list = ["問題1", 24 "問題2"] 25 broadcast_quiz(sock_list_q, quiz_list[i]) 26 return inner 27 28 29def tkinter(sock_list_q): 30 31 32 bt = tk.Button(frame, text="問題(" + str(i+1) + ")",height = 3,bg="skyblue",command = questions(sock_list_q,i))#ボタンを押すとクライアントに問題を出力 33 buttons.append(bt) 34 bt.pack(fill=tk.X) 35 36 root.mainloop() 37 38 39def socket_communication(sock_list_q): 40 41 host = 'localhost' 42 port = 50001 43 backlog = 10 44 bufsize = 4096 45 46 server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 47 print("socket is created") 48 49 try: 50 server_sock.bind((host, port)) 51 print("socket bind") 52 server_sock.listen(backlog) 53 print("socket listen") 54 55 sock_list = [server_sock] 56 57 #クライアントのソケットをポートで管理するために、辞書型でソケットを保存する 58 client_sock_table = {} 59 60 while True: 61 r_ready_sockets, w_ready_sockets, e_ready_sockets = select.select(sock_list, [], []) 62 for sock in r_ready_sockets: 63 if sock == server_sock: 64 conn,address = sock.accept() 65 sock_list.append(conn) 66 #ポートをキーとしてソケットを保存する 67 client_sock_table[address[1]] = conn 68 sock_list.remove(server_sock) 69 broadcast(sock_list,"ポート" + str(address[1]) + "番のユーザーが接続しました") 70 sock_list.append(server_sock) 71 72 sock_list_q.put(sock_list) 73 74 75 else: 76 try: 77 b_msg = sock.recv(bufsize) 78 msg = b_msg.decode('utf-8') 79 if len(msg) == 0: 80 sock.close() 81 sock_list.remove(sock) 82 else: 83 sender_port = None 84 for key, val in client_sock_table.items(): 85 if val == sock: 86 sender_port = key 87 break 88 if sender_port is not None: 89 sock_list.remove(server_sock) 90 sock_list.append(server_sock) 91 except: 92 sock.close() 93 sock_list.remove(sock) 94 sock_list.remove(server_sock) 95 sock_list.append(server_sock) 96 97 except Exception as e: 98 print("Exception!") 99 print(e) 100 server_sock.close() 101 102def main(): 103 104 sock_list_q = Queue() 105 106 process1 = Process(target = tkinter, args=[sock_list_q]) 107 process2 = Process(target = socket_communication, args=[sock_list_q]) 108 process1.start() 109 process2.start() 110 111if __name__ == '__main__': 112 main()

補足情報

現在、チャット機能を拡張しながら実装を行っているため、チャット機能+サーバーのボタンによる問題のチャット欄への出力を目的としています。
アドバイスいただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

プロセス間のリストの共有は、multiprocessing.Manager の list で出来ますが、
不必要なリソースの共有は、可能ならば避けたほうが良いです。

ボタンを押した時に呼ばれる broadcast_quiz 関数では、
GUIプロセスから Serverプロセスで生成したソケットを操作しようとしてます。
一応可能な操作ですが、スレッド間でのオブジェクトの受け渡しは、
シリアライズされたデータを元に再構築しているので、頻繁に発生する場合はコストの掛かる操作。

また、スレッド・プロセスを跨ぐ同時操作は、大抵の場合、安全な操作ではないので、
同期・排他制御といった課題が新たに出てきます。

解決策としては、
broadcast は、Server プロセス側で行うような設計にしましょう。

Server 側でリストを管理していれば、
GUI プロセス -> メッセージを送る -> Server プロセスから送る で済むはずです。

  • A: Server プロセス側で別途キュー読み出しのスレッドを設けて、

 GUI プロセスとは キュー を通じてやりとりをする。

  • B: マルチプロセスにする必要性は薄いので、マルチスレッドにする。

リストを共有したい場面は、例えば、GUI に接続情報の一覧を表示したい時、等がありますが
その場合でも、リストデータの共有は避ける事ができて、接続・切断のメッセージを
GUI <=> Server 間でやり取りして、双方でリストの同期を取ることが出来ます。


改善可能な点

  • select と broadcast の為に、サーバーソケットを毎回 remove/append している箇所

 は、集合set表記を使うと記述が楽に出来ます。

python

1sock_list = {server_socket, conn1, conn2} # => select 対象のソケット 2peer_list = sock_list - {server_socket} # => broadcast 対象の {conn1, conn2}

ですが、用途が異なる為、ここは broadcast 対象のリストを別に管理でも良いと思います。


他の問題点

  • キューはシーケンス型のデータではないので、

 get/put 以外でデータを取り出そうとするのは、誤用です。

  • GUI での queue.get は、ウィンドウが応答なしになるリスクに繋がります。
  • socket.send を使う場合は、戻り値を要確認。

 ローカル環境でのテストではあまり問題になりませんが、
外部ネットワークが対象だと、データが完全に送られない可能性があります。
詳しくは、Python Socket FAQ を参照。

  • broadcast 関数。繰り返し内での remove は、意図通りの繰り返しになりません。

 コピーを繰り返すようにしてください。

python

1 2# Bad 3xs = [1,2,3] 4for x in xs: 5 xs.remove(x) 6print(xs) # => [2] 7 8# Ok 9xs = [1,2,3] 10for x in xs.copy(): 11 xs.remove(x) 12print(xs) # => [] 13

投稿2021/07/06 12:45

teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問