2017-01-22 172 views
0

我一直在使用一些Raspberry Pis,Python和一些按鈕/開關進行遊戲。我的遊戲需要一個向多個客戶端發佈命令的中央服務器。Python TCP服務器接受連接和廣播命令

我不是新來的編程,但是對於Python和較低層次的網絡通信來說並不陌生,並且在過去的2天中我已經在雜草中丟失瞭如何編寫我的服務器代碼。

客戶端程序是一個簡單的socket.connect,然後等待數據發送。那裏沒有問題。

我很難確定如何編寫以及如何使服務器工作。

這裏是我的服務器代碼看起來像此刻:

import socket, time, sys 
import threading 

TCP_IP = '' 
TCP_PORT = 8888 
BUFFER_SIZE = 1024 
CLIENTS = {} 
clientCount = 0 

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() 
     print("new connection from:"+ str(addr)) 
     #print(len(CLIENTS)) 
     global clientCount 
     clientCount = clientCount+1 
     print (clientCount) 
     # register client 
     CLIENTS[conn.fileno()] = conn 


def broadcast(): 
    for client in CLIENTS.values(): 
      client.send('this is a broadcats msg') 

if __name__ == '__main__': 
    listener() 

    while clientCount > 0: 
     broadcast() 
     print(len(CLIENTS)) #print out the number of connected clients every 5s 
     time.sleep(5) 

這裏的所需流量: 1.服務器啓動並等待第一個連接或更多。我相信這個「​​服務器」應該在後臺線程上運行? 2.如果connectionCount > 0啓動主程序循環 3.現在主程序循環應該顯示連接的客戶端的數量並且每5秒向所有客戶端廣播一條消息。

我有約5個版本的這臺服務器。我嘗試了異步,select.select和幾種線程方法,但不能完全確定我尋求的行爲。我應該把服務器放在後臺線程中嗎?如果是的話如何廣播到所有的連接?

我沒有嘗試過的唯一的事情是Twisted,那是因爲我無法將它安裝在Windows中......所以我認爲這是暫時的optino。如果任何人有一個指向哪裏去,我會很感激它!

更新

好吧,基於@Armans建議,以便有一個服務器類,但它仍然執行相同的我已經更新了我的代碼。

class server(): 

    def __init__(self): 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     s.bind((TCP_IP,TCP_PORT)) 
     s.listen(10) 
     while 1: 
      client_socket, addr = s.accept() 
      print ('Connected with ' + addr[0] + ':' + str(addr[1])) 
      global clientCount 
      clientCount = clientCount+1 
      print (clientCount) 
      # register client 
      CLIENTS[client_socket.fileno()] = client_socket 
      threading.Thread(target=self.handler, args=(client_socket, addr)).start() 



    def handler(self, client_socket, addr): 
     while 1: 
      data = client_socket.recv(BUFFER_SIZE) 
      print ('Data : ' + repr(data) + "\n") 
      data = data.decode("UTF-8") 


    def broadcast(self, message): 
     for c in self.CLIENTS: 
      c.send(message.encode("utf-8")) 

if __name__ == '__main__': 
    s = server() #create new server listening for connections 

    while clientCount > 0: 
     s.broadcast('msg here') 
     print(len(CLIENTS)) #print out the number of connected clients every 5s 
     time.sleep(5) 

我可以連接多個客戶端和控制檯顯示以下內容:

Connected with 10.0.0.194:38406 
1 
Connected with 10.0.0.169:36460 
2 

但在「而clientCount」循環的代碼永遠不會運行。這是我被卡住了一段時間的區域,所以如果你有更多的想法,我想在這裏有任何想法!

回答

0

終於搞定了!非常感謝@Arman指導我用正確的方向穿線。我終於覺得我明白了一切都是如何運作的!

這是我完整的服務器&客戶端代碼。希望這可以幫助其他人使用master>客戶端設置。 _broadcast()函數正在工作,因爲您會看到它只是廣播靜態消息,但這應該是一個簡單的更新。

如果任何人有任何關於代碼清理的建議,使用此代碼作爲樣本的Python最佳實踐,我很想聽聽和了解更多。再次感謝SE!

##Client 

import socket 
import sys 
import json 

#vars 
connected = False 

#connect to server 
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
client_socket.connect(('10.0.0.158',8888)) 
connected = True 

while connected == True: 
    #wait for server commands to do things, now we will just display things 
    data = client_socket.recv(1024)  
    cmd = json.loads(data) #we now only expect json  
    if(cmd['type'] == 'bet'): 
     bet = cmd['value'] 
     print('betting is: '+bet) 
    elif (cmd['type'] == 'result'):   
     print('winner is: '+str(cmd['winner'])) 
     print('payout is: '+str(cmd['payout'])) 


##Server 

import socket, time, sys 
import threading 
import pprint 

TCP_IP = '' 
TCP_PORT = 8888 
BUFFER_SIZE = 1024 

clientCount = 0 

class server(): 

    def __init__(self): 
     self.CLIENTS = []   


    def startServer(self): 
     try: 
      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
      s.bind((TCP_IP,TCP_PORT)) 
      s.listen(10) 
      while 1: 
       client_socket, addr = s.accept() 
       print ('Connected with ' + addr[0] + ':' + str(addr[1])) 
       global clientCount 
       clientCount = clientCount+1 
       print (clientCount) 
       # register client 
       self.CLIENTS.append(client_socket) 
       threading.Thread(target=self.playerHandler, args=(client_socket,)).start() 
      s.close() 
     except socket.error as msg: 
      print ('Could Not Start Server Thread. Error Code : ') #+ str(msg[0]) + ' Message ' + msg[1] 
      sys.exit() 


    #client handler :one of these loops is running for each thread/player 
    def playerHandler(self, client_socket): 
     #send welcome msg to new client 
     client_socket.send(bytes('{"type": "bet","value": "1"}', 'UTF-8')) 
     while 1: 
      data = client_socket.recv(BUFFER_SIZE) 
      if not data: 
       break 
      #print ('Data : ' + repr(data) + "\n") 
      #data = data.decode("UTF-8") 
      # broadcast 
      for client in self.CLIENTS.values(): 
       client.send(data) 

     # the connection is closed: unregister 
     self.CLIENTS.remove(client_socket) 
     #client_socket.close() #do we close the socket when the program ends? or for ea client thead? 

    def broadcast(self, message): 

     for c in self.CLIENTS: 
      c.send(message.encode("utf-8")) 

    def _broadcast(self):   
     for sock in self.CLIENTS:   
      try : 
       self._send(sock) 
      except socket.error:     
       sock.close() # closing the socket connection 
       self.CLIENTS.remove(sock) # removing the socket from the active connections list 

    def _send(self, sock):   
     # Packs the message with 4 leading bytes representing the message length 
     #msg = struct.pack('>I', len(msg)) + msg 
     # Sends the packed message 
     sock.send(bytes('{"type": "bet","value": "1"}', 'UTF-8')) 


if __name__ == '__main__': 
    s = server() #create new server listening for connections 
    threading.Thread(target=s.startServer).start() 

    while 1:  
     s._broadcast() 
     pprint.pprint(s.CLIENTS) 
     print(len(s.CLIENTS)) #print out the number of connected clients every 5s 
     time.sleep(5) 
0

我有一個multithread方法在這裏:

s.listen(10) 
    while 1: 
     client_socket, addr = s.accept() 
     print ('Connected with ' + addr[0] + ':' + str(addr[1])) 
     threading.Thread(target=self.handler, args=(client_socket, addr)).start()   


def handler(self, client_socket, addr): 
    while 1: 
     data = client_socket.recv(BUFF) 
     print ('Data : ' + repr(data) + "\n") 
     data = data.decode("UTF-8") 

我強烈建議你們倆寫ServerClient一類,爲每個客戶端創建客戶端object並將其連接到Server,並存儲每個連接的客戶端(它的套接字和一個名字例如)到字典中,然後你想要廣播一條消息,你可以通過Server所有連接的客戶端和你想要的廣播消息:

def broadcast(self, client_socket, message): 
     for c in self.clients: 
      c.send(message.encode("utf-8")) 

更新

因爲你有運行主一thread你需要另一個thread對正在運行的服務器,我建議你寫的服務器start方法,並調用它在thread:現在

def start(self): 
    # all server starts stuff comes here as define socket 
    self.s.listen(10) 
    while 1: 
     client_socket, addr = self.s.accept() 
     print ('Connected with ' + addr[0] + ':' + str(addr[1])) 
     threading.Thread(target=self.handler, args=(client_socket, addr)).start() 

在創建服務器對象後的主要部分或主文件中運行啓動線程:

a = server() 
threading.Thread(target=a.start).start()