2010-09-08 64 views
9

Vâng, tôi đang cố gắng xây dựng một sơ đồ con trăn nhỏ với một SocketServer mà là vụ phải gửi tin nhắn nó nhận được cho tất cả các khách hàng kết nối. Tôi bị kẹt, tôi không biết cách lưu trữ khách hàng trên máy chủ và tôi không biết cách gửi cho nhiều khách hàng. Oh, và chương trình của tôi thất bại mọi hơn sau đó 1 client kết nối, và mỗi lần một khách hàng gửi nhiều hơn thì một thông điệp ...Python SocketServer: gửi cho nhiều khách hàng?

Dưới đây là mã của tôi cho đến bây giờ:

 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() 

Rõ ràng là tôi không có ý tưởng những gì tôi m làm, vì vậy bất kỳ trợ giúp sẽ là tuyệt vời.
Cảm ơn bạn trước!

Chỉnh sửa: Tôi đang sử dụng Python 2.7 trên Windows Vista.

+0

Tôi đã có một vấn đề tương tự giải quyết ở đây: [server/client code] (http://stackoverflow.com/questions/41785969/python-tcp-server-accepting-connections -and-Broadcast-commands/41786133 # 41786133) –

Trả lời

15

Bạn muốn xem asyncore tại đây. Các hoạt động ổ cắm mà bạn đang gọi ở phía máy khách đang chặn (không quay trở lại cho đến khi một số dữ liệu được nhận hoặc một thời gian chờ xảy ra) gây khó khăn cho việc nghe tin nhắn được gửi từ máy chủ và để cho các trường hợp khách hàng enqueue dữ liệu gửi cùng lúc. asyncore được cho là trừu tượng vòng lặp bỏ phiếu dựa trên thời gian chờ xa bạn.

Dưới đây là một mã số "mẫu" - cho tôi biết nếu bất cứ điều gì không rõ ràng:

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() 

Những kết quả trong các kết quả sau:

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

Cảm ơn, trông giống như những gì tôi đang tìm kiếm! Thật không may, tôi đã không quản lý để làm cho nó hoạt động bên ngoài __main__: Tôi đã thêm asyncore.loop() vào cuối Host.__ init __(), và đối tượng host của tôi chấp nhận các kết nối máy khách, nhưng nó không phản ứng với các thư đã gửi .. – Alex

+0

@Alex: asyncore.loop() chạy mãi mãi! Hiệu quả bằng cách gọi nó là bạn đang nói, "Tôi đang thực hiện kiểm soát chương trình, bàn giao mọi thứ cho vòng lặp asyncore để nó có thể xử lý gửi/nhận cho phần còn lại của thời gian." Lưu ý cách tôi thiết lập mọi thứ trước khi gọi asyncore.loop(). Bạn đang cố gắng làm gì bằng cách di chuyển nó? – cdleary

+0

Vâng, tôi không muốn chỉ chạy một số kết nối nhất định, nhưng có máy chủ đang chạy và khách hàng có thể kết nối/gửi tin nhắn bất cứ lúc nào. Cảm ơn vì đã dành thời gian cho tôi! – Alex

0

tại sao sử dụng SocketServer? một khách hàng đơn giản không đáp ứng nhu cầu của bạn?

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

sẽ không chặn các khách hàng khác kết nối? – Codler

+0

Điều này sẽ không hoạt động. –

0

Chịu nhiều khách hàng cùng một lúc, bạn sẽ phải thêm SocketServer.ForkingMixIn hoặc ThreadingMixIn.

5

Bạn có thể sử dụng socketserver để phát tin nhắn tới tất cả khách hàng được kết nối. Tuy nhiên, khả năng không được xây dựng trong mã và sẽ cần phải được thực hiện bằng cách mở rộng một số lớp đã được cung cấp. Trong ví dụ sau, điều này được triển khai bằng các lớp học ThreadingTCPServerStreamRequestHandler. Họ cung cấp một nền tảng để xây dựng nhưng vẫn yêu cầu một số sửa đổi để cho phép những gì bạn đang cố gắng thực hiện. Tài liệu hướng dẫn này sẽ giúp giải thích từng chức năng, lớp và phương thức đang cố gắng thực hiện để hoàn thành công việc.

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() 

Tất nhiên, bạn cũng cần một khách hàng có thể giao tiếp với máy chủ của bạn và sử dụng cùng một giao thức máy chủ nói. Vì đây là Python, quyết định được thực hiện để sử dụng mô-đun pickle để tạo điều kiện chuyển dữ liệu giữa máy chủ và máy khách. Các phương thức chuyển dữ liệu khác có thể đã được sử dụng (chẳng hạn như JSON, XML, et cetera), nhưng việc có thể chọn lọc và bỏ chọn dữ liệu đáp ứng nhu cầu của chương trình này cũng đủ. Tài liệu được bao gồm một lần nữa, do đó, nó không phải là quá khó khăn để tìm ra những gì đang xảy ra. Lưu ý rằng các lệnh máy chủ có thể làm gián đoạn nhập dữ liệu người dùng.

Khách hàng

#! /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() 
Các vấn đề liên quan