2016-10-12 21 views
0

私はLAN上でTCP pythonサーバを作成しようとしていましたが、私はこのプロジェクトで常に問題を抱えています。私の質問です:1つのサーバーから複数のクライアントに(TCP経由で)メッセージを送信することは可能ですか? (つまり、client-1は "Hello world"というメッセージを送信し、他のすべてのクライアント[clients-2、clients-3]にメッセージを表示します)。相続人は、これまでのサーバーのための私のコード:Python TCP Serverがすべてのクライアントにメッセージを送信しています

import socket, time, sys 
import threading 

TCP_IP = input("Host IP: ") 
TCP_PORT = int(input("Host Port: ")) 
BUFFER_SIZE = 1024 

def createNewThread(function): 
    threading.Thread(target=function).start() 

def Listening(): 
    try: 
     while True: 
      s.listen(1) 

      conn,addr = s.accept() 
      threading.Thread(target=Listening).start() 
      print("User joined with IP %s" % (addr[0])) 
      while 1: 
       data = conn.recv(BUFFER_SIZE) 
       if not data: break 
       conn.send(addr[0].encode("utf-8") + b': ' + data) 
      conn.close() 
    except ConnectionResetError as e: 
     print("Connection was closed: ", e) 

try: 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.bind((TCP_IP,TCP_PORT)) 
    print("-----Server started-----") 
    Listening() 
except socket.error as e: 
    print("Socket error occured. More info: ", e) 

そして相続人のクライアントのための私のコード:任意の答えを事前に

import socket, sys, time 

TCP_IP = input("Connect to Local IP: ") 
TCP_PORT = int(input("Connect to Local Port: ")) 
BUFFER_SIZE = 1024 
running = True 

while running == True: 
    try: 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     print("Connecting...") 
     s.connect((TCP_IP,TCP_PORT)) 
     print("Connected!") 
     while True: 
      MESSAGE = input("Message: ") 
      if MESSAGE == "exit": 
       s.close() 
       raise SystemExit 
      s.send(MESSAGE.encode('ascii')) 
      data = s.recv(BUFFER_SIZE) 
      print(data.decode("utf-8")) 

     running = False 
     time.sleep(20) 
    except: 
     print(sys.exc_info()[0]) 
     time.sleep(1) 

ありがとう!

編集: 私は出力に含まはこのような何かを見てみたい:

User3's IP: Message they sent 
User1's IP: Message they sent 
Message: What do you want to send? 
+0

"1台のサーバーから複数のクライアントに(TCP経由で)メッセージを送信することはできますか?" - はい。あなたが直面している問題は何ですか? – Prabhu

+0

@Prabhu * sigh *どうやって? –

答えて

1

あなたのコードは...非常に奇妙です。最初に、acceptに新しいスレッドを作成しますが、クライアントではなくそのスレッドにリスナーを送ります。したがって、スレッドは決して死ぬことはなく、メモリとCPUのリークがあります。さらに悪いことに、あなたのコードでは、スレッドの数はの数に等しくなりますがサーバーに接続されているすべてのクライアントです。これは悪いです。

サーバー側でこれを試してみてください:

def client(conn): 
    while True: 
     data = conn.recv(BUFFER_SIZE) 
     if not data: 
      break 
     conn.send(data) # simple ping 

def listener(): 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.bind((TCP_IP, TCP_PORT)) 
    s.listen(5) 
    while True: 
     conn, addr = s.accept() 
     threading.Thread(target=client, args=(conn,)).start() 

if __name__ == '__main__': 
    listener() 

コードは、短く簡単で、何のメモリ/ CPUの漏出はありません。

すべてのクライアントにデータを送信します。あなたはそれらを追跡しなければなりません。あなたがリスナーに今

CLIENTS = {} 

:あなたは、クライアントのグローバル辞書を保つことによって、これを達成することができます

def listener(): 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.bind((TCP_IP,TCP_PORT)) 
    s.listen(5) 
    while True: 
     conn, addr = s.accept() 
     # register client 
     CLIENTS[conn.fileno()] = conn 
     threading.Thread(target=client, args=(conn,)).start() 

とクライアントで:

def client(conn): 
    while True: 
     data = conn.recv(BUFFER_SIZE) 
     if not data: 
      break 
     # broadcast 
     for client in CLIENTS.values(): 
      client.send(data) 

    # the connection is closed: unregister 
    del CLIENTS[conn.fileno()] 

そのコードを持つ一つの小さな問題があります(実際には、エラー処理のように、実際にはいくつかあります)。 CLIENTS辞書をループしている間に一部のクライアントが登録を解除するとどうなりますか? Pythonは例外をスローします。簡単な解決策は、挿入、削除、反復で辞書をロックすることです。

他のソケットが以前のfileno()を再利用する場合、競合状態が発生します。その場合は、ソケットのIDを手動で生成することができます(好ましくは、カスタムクラスを使用してsocketオブジェクトをラップします)。

dictの代わりにsetを使用できます。しかし、ある時点で特定のクライアント(idによって識別される)にmsgを送信したいので、最終的にはdictが必要になります。

+0

私は自分のコードが変わっていることを知っています。私はこの種のものに関してはあまり組織化されていません。助けてくれてありがとう、ちょうどそれをすべて試してみよう! –

+0

CPUリークとは何ですか? – Sam

+0

@Samメモリリークと同じです:あなたはリソースを失います。cpuの場合、これは無意味な作業なので、何か意味のあることをする可能性のあるサイクルを失うことを意味します。 – freakish

関連する問題