2010-09-08 16 views
9

Cóż, próbuję zbudować małego programa prgram z SocketServer, który ma wysyłać wiadomości, które otrzymuje do wszystkich podłączonych klientów. Utknąłem, nie wiem, jak przechowywać klientów na serwerze, i nie wiem, jak wysłać do wielu klientów. No i mój program nie spędzaj więcej niż 1 klient łączy, i za każdym razem klient wysyła więcej niż jednej wiadomości ...Python SocketServer: wysyłanie do wielu klientów?

Oto mój kod do tej pory:

 print str(self.client_address[0])+' connected.' 
    def handle(self): 
     new=1 
     for client in clients: 
      if client==self.request: 
       new=0 
     if new==1: 
      clients.append(self.request) 
     for client in clients: 
      data=self.request.recv(1024) 
      client.send(data) 

class Host: 
    def __init__(self): 
     self.address = ('localhost', 0) 
     self.server = SocketServer.TCPServer(self.address, EchoRequestHandler) 
     ip, port = self.server.server_address 
     self.t = threading.Thread(target=self.server.serve_forever) 
     self.t.setDaemon(True) 
     self.t.start() 
     print '' 
     print 'Hosted with IP: '+ip+' and port: '+str(port)+'. Clients can now connect.' 
     print '' 
    def close(self): 
     self.server.socket.close() 

class Client: 
    name='' 
    ip='' 
    port=0 
    def __init__(self,ip,port,name): 
     self.name=name 
     self.hostIp=ip 
     self.hostPort=port 
     self.s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.s.connect((self.hostIp, self.hostPort)) 
    def reco(self): 
     self.s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.s.connect((self.hostIp, self.hostPort)) 
    def nick(self,newName): 
     self.name=newName 
    def send(self,message): 
     message=self.name+' : '+message 
     len_sent=self.s.send(message) 
     response=self.s.recv(len_sent) 
     print response 
     self.reco() 
    def close(self): 
     self.s.close() 

Oczywiście nie mam pojęcia, co ja” Robię, więc każda pomoc byłaby świetna.
Z góry dziękuję!

Edytuj: Używam Pythona 2.7 w systemie Windows Vista.

+0

miałem podobny problem rozwiązany tutaj: [server/client code] (http://stackoverflow.com/questions/41785969/python-tcp-server-accepting-connections -i-rozkazy-nadawcze/41786133 # 41786133) –

Odpowiedz

15

Chcesz obejrzeć tutaj asyncore. Operacje na gniazdach, które wywoływane są po stronie klienta, są blokowane (nie zwracaj, dopóki nie zostaną odebrane niektóre dane lub wystąpi limit czasu), co utrudnia wysłuchanie wiadomości wysłanych z hosta i pozwala klientowi instanować kolejkowanie danych do wysłania o godzinie o tym samym czasie. asyncore ma na celu streszczenie pętli odpytywania opartej na timeoutie.

Oto kod "próbka" - daj mi znać, jeśli coś jest niejasne:

from __future__ import print_function 

import asyncore 
import collections 
import logging 
import socket 


MAX_MESSAGE_LENGTH = 1024 


class RemoteClient(asyncore.dispatcher): 

    """Wraps a remote client socket.""" 

    def __init__(self, host, socket, address): 
     asyncore.dispatcher.__init__(self, socket) 
     self.host = host 
     self.outbox = collections.deque() 

    def say(self, message): 
     self.outbox.append(message) 

    def handle_read(self): 
     client_message = self.recv(MAX_MESSAGE_LENGTH) 
     self.host.broadcast(client_message) 

    def handle_write(self): 
     if not self.outbox: 
      return 
     message = self.outbox.popleft() 
     if len(message) > MAX_MESSAGE_LENGTH: 
      raise ValueError('Message too long') 
     self.send(message) 


class Host(asyncore.dispatcher): 

    log = logging.getLogger('Host') 

    def __init__(self, address=('localhost', 0)): 
     asyncore.dispatcher.__init__(self) 
     self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.bind(address) 
     self.listen(1) 
     self.remote_clients = [] 

    def handle_accept(self): 
     socket, addr = self.accept() # For the remote client. 
     self.log.info('Accepted client at %s', addr) 
     self.remote_clients.append(RemoteClient(self, socket, addr)) 

    def handle_read(self): 
     self.log.info('Received message: %s', self.read()) 

    def broadcast(self, message): 
     self.log.info('Broadcasting message: %s', message) 
     for remote_client in self.remote_clients: 
      remote_client.say(message) 


class Client(asyncore.dispatcher): 

    def __init__(self, host_address, name): 
     asyncore.dispatcher.__init__(self) 
     self.log = logging.getLogger('Client (%7s)' % name) 
     self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.name = name 
     self.log.info('Connecting to host at %s', host_address) 
     self.connect(host_address) 
     self.outbox = collections.deque() 

    def say(self, message): 
     self.outbox.append(message) 
     self.log.info('Enqueued message: %s', message) 

    def handle_write(self): 
     if not self.outbox: 
      return 
     message = self.outbox.popleft() 
     if len(message) > MAX_MESSAGE_LENGTH: 
      raise ValueError('Message too long') 
     self.send(message) 

    def handle_read(self): 
     message = self.recv(MAX_MESSAGE_LENGTH) 
     self.log.info('Received message: %s', message) 


if __name__ == '__main__': 
    logging.basicConfig(level=logging.INFO) 
    logging.info('Creating host') 
    host = Host() 
    logging.info('Creating clients') 
    alice = Client(host.getsockname(), 'Alice') 
    bob = Client(host.getsockname(), 'Bob') 
    alice.say('Hello, everybody!') 
    logging.info('Looping') 
    asyncore.loop() 

co skutkuje w następujący wynik:

INFO:root:Creating host 
INFO:root:Creating clients 
INFO:Client ( Alice):Connecting to host at ('127.0.0.1', 51117) 
INFO:Client ( Bob):Connecting to host at ('127.0.0.1', 51117) 
INFO:Client ( Alice):Enqueued message: Hello, everybody! 
INFO:root:Looping 
INFO:Host:Accepted client at ('127.0.0.1', 55628) 
INFO:Host:Accepted client at ('127.0.0.1', 55629) 
INFO:Host:Broadcasting message: Hello, everybody! 
INFO:Client ( Alice):Received message: Hello, everybody! 
INFO:Client ( Bob):Received message: Hello, everybody! 
+0

Dzięki, wygląda jak to, czego szukałem! Niestety, nie udało mi się sprawić, żeby działało poza __main__: dodałem asyncore.loop() na końcu Hosta .__ init __(), a mój obiekt hosta akceptuje połączenia z klientami, ale nie reaguje na wysłane wiadomości. – Alex

+0

@Alex: asyncore.loop() działa wiecznie! Efektywnie, nazywając to, mówisz: "Skończyłem kontrolować program, przekazuję rzeczy do pętli asyncore, aby mogła obsłużyć wysyłanie/odbieranie przez resztę czasu." Zauważ, jak wszystko ustawiłem przed wywołaniem asyncore.loop(). Co próbujesz zrobić, przenosząc go? – cdleary

+0

No cóż, nie chcę uruchamiać tylko niektórych określonych połączeń, ale host powinien działać, a klienci mogą łączyć się/wysyłać wiadomości w dowolnym momencie. Dziękuję za Twój czas! – Alex

0

dlaczego używać SocketServer? prosty klient nie spełnia twoich potrzeb?

import socket 

HOST = '' 
PORT = 8000 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.bind((HOST, PORT)) 
sock.listen(5) 
while True: 
    conn, addr = sock.accept() 
    print 'connecting to', addr 
    while True: 
     data = conn.recv(1024) 
     if not data: 
      break 
     conn.send(data) 
+6

czy to nie blokuje połączenia innych klientów? – Codler

+0

To przyniesie pracę. –

0

Aby wziąć wielu klientów jednocześnie, trzeba będzie dodać SocketServer.ForkingMixIn lub ThreadingMixIn.

5

Możesz użyć socketserver do rozsyłania wiadomości do wszystkich podłączonych klientów. Jednak zdolność ta nie jest wbudowana w kod i będzie wymagać implementacji poprzez rozszerzenie niektórych klas już dostarczonych. W poniższym przykładzie jest to realizowane za pomocą klas ThreadingTCPServer i StreamRequestHandler. Stanowią one podstawę, na której można budować, ale nadal wymagają pewnych modyfikacji, aby umożliwić to, co próbujesz osiągnąć. Dokumentacja powinna pomóc wyjaśnić, co każda funkcja, klasa i metoda próbują wykonać, aby wykonać zadanie.

Server

#! /usr/bin/env python3 
import argparse 
import pickle 
import queue 
import select 
import socket 
import socketserver 


def main(): 
    """Start a chat server and serve clients forever.""" 
    parser = argparse.ArgumentParser(description='Execute a chat server demo.') 
    parser.add_argument('port', type=int, help='location where server listens') 
    arguments = parser.parse_args() 
    server_address = socket.gethostbyname(socket.gethostname()), arguments.port 
    server = CustomServer(server_address, CustomHandler) 
    server.serve_forever() 


class CustomServer(socketserver.ThreadingTCPServer): 

    """Provide server support for the management of connected clients.""" 

    def __init__(self, server_address, request_handler_class): 
     """Initialize the server and keep a set of registered clients.""" 
     super().__init__(server_address, request_handler_class, True) 
     self.clients = set() 

    def add_client(self, client): 
     """Register a client with the internal store of clients.""" 
     self.clients.add(client) 

    def broadcast(self, source, data): 
     """Resend data to all clients except for the data's source.""" 
     for client in tuple(self.clients): 
      if client is not source: 
       client.schedule((source.name, data)) 

    def remove_client(self, client): 
     """Take a client off the register to disable broadcasts to it.""" 
     self.clients.remove(client) 


class CustomHandler(socketserver.StreamRequestHandler): 

    """Allow forwarding of data to all other registered clients.""" 

    def __init__(self, request, client_address, server): 
     """Initialize the handler with a store for future date streams.""" 
     self.buffer = queue.Queue() 
     super().__init__(request, client_address, server) 

    def setup(self): 
     """Register self with the clients the server has available.""" 
     super().setup() 
     self.server.add_client(self) 

    def handle(self): 
     """Run a continuous message pump to broadcast all client data.""" 
     try: 
      while True: 
       self.empty_buffers() 
     except (ConnectionResetError, EOFError): 
      pass 

    def empty_buffers(self): 
     """Transfer data to other clients and write out all waiting data.""" 
     if self.readable: 
      self.server.broadcast(self, pickle.load(self.rfile)) 
     while not self.buffer.empty(): 
      pickle.dump(self.buffer.get_nowait(), self.wfile) 

    @property 
    def readable(self): 
     """Check if the client's connection can be read without blocking.""" 
     return self.connection in select.select(
      (self.connection,),(),(), 0.1)[0] 

    @property 
    def name(self): 
     """Get the client's address to which the server is connected.""" 
     return self.connection.getpeername() 

    def schedule(self, data): 
     """Arrange for a data packet to be transmitted to the client.""" 
     self.buffer.put_nowait(data) 

    def finish(self): 
     """Remove the client's registration from the server before closing.""" 
     self.server.remove_client(self) 
     super().finish() 


if __name__ == '__main__': 
    main() 

Oczywiście, trzeba również klienta, który może komunikować się z serwerem i używać tego samego protokołu serwer mówi. Ponieważ jest to Python, podjęto decyzję o wykorzystaniu modułu pickle w celu ułatwienia transferu danych między serwerami i klientami. Można by zastosować inne metody przesyłania danych (takie jak JSON, XML itd.), Ale umiejętność marszczenia i rozpakowywania danych dobrze spełnia potrzeby tego programu. Dokumentacja jest dołączana ponownie, więc nie powinno być zbyt trudne do zrozumienia, co się dzieje. Zauważ, że polecenia serwera mogą przerywać wprowadzanie danych użytkownika.

Client

#! /usr/bin/env python3 
import argparse 
import cmd 
import pickle 
import socket 
import threading 


def main(): 
    """Connect a chat client to a server and process incoming commands.""" 
    parser = argparse.ArgumentParser(description='Execute a chat client demo.') 
    parser.add_argument('host', type=str, help='name of server on the network') 
    parser.add_argument('port', type=int, help='location where server listens') 
    arguments = parser.parse_args() 
    client = User(socket.create_connection((arguments.host, arguments.port))) 
    client.start() 


class User(cmd.Cmd, threading.Thread): 

    """Provide a command interface for internal and external instructions.""" 

    prompt = '>>> ' 

    def __init__(self, connection): 
     """Initialize the user interface for communicating with the server.""" 
     cmd.Cmd.__init__(self) 
     threading.Thread.__init__(self) 
     self.connection = connection 
     self.reader = connection.makefile('rb', -1) 
     self.writer = connection.makefile('wb', 0) 
     self.handlers = dict(print=print, ping=self.ping) 

    def start(self): 
     """Begin execution of processor thread and user command loop.""" 
     super().start() 
     super().cmdloop() 
     self.cleanup() 

    def cleanup(self): 
     """Close the connection and wait for the thread to terminate.""" 
     self.writer.flush() 
     self.connection.shutdown(socket.SHUT_RDWR) 
     self.connection.close() 
     self.join() 

    def run(self): 
     """Execute an automated message pump for client communications.""" 
     try: 
      while True: 
       self.handle_server_command() 
     except (BrokenPipeError, ConnectionResetError): 
      pass 

    def handle_server_command(self): 
     """Get an instruction from the server and execute it.""" 
     source, (function, args, kwargs) = pickle.load(self.reader) 
     print('Host: {} Port: {}'.format(*source)) 
     self.handlers[function](*args, **kwargs) 

    def preloop(self): 
     """Announce to other clients that we are connecting.""" 
     self.call('print', socket.gethostname(), 'just entered.') 

    def call(self, function, *args, **kwargs): 
     """Arrange for a handler to be executed on all other clients.""" 
     assert function in self.handlers, 'You must create a handler first!' 
     pickle.dump((function, args, kwargs), self.writer) 

    def do_say(self, arg): 
     """Causes a message to appear to all other clients.""" 
     self.call('print', arg) 

    def do_ping(self, arg): 
     """Ask all clients to report their presence here.""" 
     self.call('ping') 

    def ping(self): 
     """Broadcast to all other clients that we are present.""" 
     self.call('print', socket.gethostname(), 'is here.') 

    def do_exit(self, arg): 
     """Disconnect from the server and close the client.""" 
     return True 

    def postloop(self): 
     """Make an announcement to other clients that we are leaving.""" 
     self.call('print', socket.gethostname(), 'just exited.') 


if __name__ == '__main__': 
    main() 
Powiązane problemy