2016-10-12 43 views
0

我一直在試圖通過我的局域網上的TCP python服務器,但我一直遇到這個項目的問題。我的問題是:是否可以從一臺服務器向多個客戶端發送消息(通過TCP)? (即,客戶端-1發送消息「Hello world」,並且它在所有其他客戶端[客戶端-2,客戶端-3]上顯示消息)。我的繼承人服務器到目前爲止的代碼:Python的TCP服務器發送消息到所有的客戶端

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

「是否可以通過TCP發送消息給多個客戶端?」 - 是的。你面臨的問題是什麼? – Prabhu

+0

@Prabhu *嘆*如何? –

回答

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對象)。

請注意,可以使用set而不是dict。然而,你最終會需要一個字典,因爲在某些時候你會想發送消息給特定的客戶端(由某個ID標識)。

+0

我知道我的代碼很奇怪,當涉及到這些事情時,我不是很「有組織」。感謝您的幫助,現在就全力以赴! –

+0

什麼是cpu泄漏? – Sam

+0

@Sam和內存泄漏一樣:你失去了一個資源。在cpu的情況下,這意味着它做了毫無意義的工作,因此你失去了可能做有意義的週期。 – freakish

相關問題