2016-04-29 19 views
2

Von meinem Verständnis Python kann nur 1 Thread zu einem Zeitpunkt laufen, so, wenn ich so etwas wie diesePython - Multithreaded-Steckdosen

import socket, select 
from threading import Thread 
import config 

class Source(Thread): 
    def __init__(self): 
     self._wait = False 
     self._host = (config.HOST, config.PORT + 1) 
     self._socket = socket.socket() 
     self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     self._sock = None 
     self._connections = [] 
     self._mount = "None" 
     self._writers = [] 
     self._createServer() 
     Thread.__init__(self) 

    def _createServer(self): 
     self._socket.bind(self._host) 
     self._socket.listen(2) 
     self._connections.append(self._socket) 
     self._audioPackets=[] 

    def _addPacket(self, packet): 
     self._audioPackets.append(packet) 

    def _removePacket(self, packet): 
     self._audioPackets.remove(packet) 

    def _getPacket(self): 
     if len(self._audioPackets) > 0: 
      return self._audioPackets[0] 
     else: 
      return None 

    def _sendOK(self, sock): 
     sock.send("OK") 

    def _sendDenied(self, sock): 
     sock.send("DENIED") 

    def _sendMount(self, sock): 
     sock.send("mount:{0}".format(self._mount)) 

    def _sendBufPacket(self, sock, packet): 
     packet = "buffer:%s" % packet 
     sock.send(packet) 

    def recv(self, sock, data): 
     data = data.split(":", 1) 
     if data[0] == "WAIT": self._wait = True 
     elif data[0] == "STOP_WAITING": self._wait = False 
     elif data[0] == "LOGIN": 
      if data[1] == config.SOURCE_AUTH: 
       self._source = sock 
       self._sendOK(sock) 
      else: 
       self._sendClose(sock) 
     elif data[0] == "MOUNT": 
      if self._source == sock: 
       self._mount = data[1] 
      else: 
       self._sendClose(sock) 

     elif data[0] == "CLIENT": 
      self._sendMount(sock) 
      self._writers.append(sock) 


    def _sendCloseAll(self): 
     for sock in self._connections: 
      sock.send("CLOSE") 
      sock.close() 

    def _sendClose(self, sock): 
     sock.send("CLOSE") 
     sock.close() 

    def main(self): 
     while True: 
      rl, wl, xl = select.select(self._connections, self._writers, [], 0.2) 
      for sock in rl: 
       if sock == self._socket: 
        con, ip = sock.accept() 
        self._connections.append(con) 
       else: 
        data = sock.recv(config.BUFFER) 
        if data: 
         self.recv(sock, data) 
        else: 
         if sock in self._writers: 
          self._writers.remove(sock) 
         if sock in self._connections: 
          self._connections.remove(sock) 
      for sock in wl: 
       packet = self._getPacket() 
       if packet != None: 
        self._sendBufPacket(sock, packet) 

    def run(self): 
     self.main() 

class writeThread(Thread): 
     def __init__(self): 
      self.running = False 

     def make(self, client): 
      self.client = client 
      self.running = True 

     def run(self): 
      host = (config.HOST, config.PORT+1) 
      sock = socket.socket() 
      sock.connect(host) 
      sock.send("CLIENT") 
      sock.send("MOUNT:mountpoint") 
      while self.running: 
       data = sock.recv(config.BUFFER) 
       if data: 
        data = data.split(":", 1) 
        if data[0] == "buffer": 
        self.client.send(data[1]) 
        elif data[0] == "CLOSE": 
         self.client.close() 
         break 


if __name__=="__main__": 
    source = Source() 
    source.start() 
    webserver = WebServer() 
    webserver.runloop() 

täte, wenn ich den Webserver Teil werde ich aufbauen müssen. Aber ich werde es erklären. Okay, also im Grunde, wenn jemand eine Verbindung zum Webserver unter dem eingestellten Mountpoint herstellt, erhalten sie dort einen eigenen persönlichen Thread, der dann die Daten von Source() ergreift und an sie sendet. Nun sagen Sie, eine andere Person verbindet sich mit dem Mount-Punkt und der letzte Client sowie die Quelle läuft noch. Wäre der neue Client nicht daran gehindert, die Quelldaten zu erhalten, wenn zwei aktive Threads vorhanden sind?

Antwort

2

Ihr Verständnis davon, wie Threads in Python funktionieren, scheint auf der Grundlage der von Ihnen gestellten Frage falsch zu sein. Bei korrekter Verwendung blockieren Threads nicht: Sie können mehrere Threads mit Python instanziieren. Die Einschränkung besteht darin, dass Sie aufgrund der Global Interpreter Lock (GIL) nicht die volle Parallelität erwarten können, die in der Thread-Programmierung erwartet wird (z. B. gleichzeitige Ausführung und somit reduzierte Laufzeit). Was in Ihrem Fall passieren wird, ist, dass die zwei Threads zusammen die gleiche Zeit benötigen, die sie nehmen würden, wenn sie sequentiell ausgeführt würden (obwohl das in der Praxis nicht unbedingt der Fall ist).

0

Okay, ich habe einen Python3-Code kopiert und eingefügt, den ich bereits für ein Projekt geschrieben habe, an dem ich gerade arbeite. Mit der Änderung können Sie diesen Code Ihren Zwecken dienen lassen.

Der Code verwendet Multiprocessing und Multithreading. Für meine Zwecke verwende ich Multiprocessing, so dass Sockets auf einem Prozessor ausgeführt werden und ich ein GUI-Programm auf einem anderen Prozessor ausführen kann. Sie können den Multiprozessor-Teil entfernen, wenn Sie bevorzugen. Der folgende Code führt einen Socket-Nachrichtenserver aus. Der Server hört nacheinander nach Clients. Sobald ein Client eine Verbindung hergestellt hat, wird ein neuer Thread initiiert, um die gesamte Kommunikation zwischen dem Server und jedem Client abzuwickeln. Der Server sucht dann weiterhin nach Clients. Im Moment hört der Server jedoch nur Daten ab, die von jedem Client gesendet werden, und druckt sie dann auf dem Terminal aus. Mit ein wenig Aufwand können Sie meinen Code ändern, um Informationen vom Server an jeden Client einzeln zu senden.

import multiprocessing 
import threading 
from threading import Thread 

class ThreadedServer(object): 

    def __init__(self, host, port): 
    self.host = host 
    self.port = port 
    self.sock = socket(AF_INET, SOCK_STREAM) 
    self.sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 
    self.sock.bind((self.host, self.port)) 

    def listen(self):  
     self.sock.listen(3) #Allow 3 Clients to connect to this server 
     while True: 
      #The program will search for one client at a time 
      print("Searching for Client") 
      client, address = self.sock.accept() 
      print(address, " is connected") 
      #client.settimeout(60) 

      #Once a client has been found, start a individual client thread 
      d = threading.Thread(target = self.listenToClient, args=(client, address)) 
      d.daemon = True 
      d.start() 

def listenToClient(self, client, address): 
    size = 1024 
    while True: 
     try: 
      data = client.recv(size) 
      if not data: 
       break 
      if data: 
       print(data) 
       #client.send(response) 
      else: 
       raise error('Client disconnected') 
     except: 
      client.close() 
      return False 

def dataSharingHost(): 
    #Using Sockets to send information between Processes 
    #This is the server Function 
    #ThreadServer(Host_IP, Port_Number), for LocalHost use '' 
    ThreadedServer('', 8000).listen() 

def Main(): 
    commServer = multiprocessing.Process(target=dataSharingHost, args=()) 
    commServer.daemon = True 
    commServer.start() 

if __name__== '__main__': 
    Main() 

Und um fair zu sein, ist mein Code aus https://www.youtube.com/watch?v=qELZAi4yra8 geändert. Der Client-Code ist in diesen Videos enthalten. Ich denke, das dritte Video deckt die Verbindung mehrerer Clients ab.

+0

'client, adresse = self.sock.accept()' führt dies nicht zu einem Fehler, wenn ein anderer Client eine Verbindung herstellt oder wenn ein Client die Verbindung wieder herstellt? –

+0

Ich hatte kein Problem, 3 Clients gleichzeitig zu verbinden. self.sock.listen (3) konfiguriert den Server so, dass 3 Clients gleichzeitig akzeptiert werden. Es stellt sich heraus, dass die Variable "Adresse" tatsächlich zwei Informationen enthält. Der erste Teil ist die IP-Adresse des Clients, die 127.0.0.1 lauten sollte, wenn Sie diesen Code ausführen. Die zweite Information ist die Variable "Adresse" ist die eindeutige ID für jeden verbundenen Client. Daher erhält jeder Client seine eigene ID. Derzeit ist dieser Code nicht ausgereift genug, um Clients die Verbindung wiederherzustellen. Das muss etwas sein, woran ich an einer späteren Kreuzung arbeite. – jberry