简体   繁体   中英

python asyncore server send data to only one sock

I have to send data only to a connection, as I can do?

server:

import asyncore, socket, threading

class EchoHandler(asyncore.dispatcher_with_send):
    def __init__(self,sock):
        asyncore.dispatcher.__init__(self,sock=sock);
        self.out_buffer = ''

    def handle_read(self):
        datos = self.recv(1024);
        if datos:
            print(datos);
            self.sock[0].send("signal");

class Server(asyncore.dispatcher):

    def __init__(self,host='',port=6666):
        asyncore.dispatcher.__init__(self);
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM);
        self.set_reuse_addr();
        self.bind((host,port));
        self.listen(1);

    def handle_accept(self):
        self.sock,self.addr = self.accept();
        if self.addr:
            print self.addr[0];
        handler = EchoHandler(self.sock);

    def handle_close(self):
        self.close();     

cliente = Server();
asyncore.loop()

this line is an example fails, but I want to send data to zero sock:

self.sock[0].send("probando");

for example, if I have 5 sockets choose who to send the data

Explanation

You tried to get sock from list and execute its send method. This causes error, because EchoHandler neither has sock attribute nor it's a list of sockets. The right method is to get instance of EchoHandler you want (based on, eg. IP address, or slots assigned by some user-defined protocol) and then use its send method - here (with dispatcher_with_send ) its also better to use special buffer for that than send.

EchoHandler instantion is created on every accept of connection - from then it is an established channel for communication with the given host. Server listens for any non-established connection, while EchoHandler s use socks (given by Server in handle_accept ) for established ones, so there are as many EchoHandler instances as connections.

Solution

You need to make some list of connections ( EchoHandler instantions; we'll use buffer, not socket's send() directly) and give them opportunity to delete their entries on close:

class Server(asyncore.dispatcher):
    def __init__(self, host='', port=6666):
        ...
        self.connections = []

    def handle_accept(self):
        ...
        handler = EchoHandler(self.sock, self);
        self.connections.append(self.sock)

    ...

    def remove_channel(self, sock):
        if sock in self.connections:
            self.connections.remove(sock)

class EchoHandler(asyncore.dispatcher_with_send):
    def __init__(self, sock, server):
        ...
        self.server = server

    def handle_read(self):
        datos = self.recv(1024);
        if datos:
            print(datos);
            self.out_buffer += 'I echo you: ' + datos

    def handle_close(self):
        self.server.remove_channel(self)
        self.close()

EchoHandler is now aware of server instance and can remove its socket from list. This echo example is now fully functional, and with working socket list we can proceed to asynchronous sending.

But, at this point you can use this list as you wanted - cliente.connections[0].out_buffer += 'I am data' will do the work, but probably you'd want some better controlling of this. If yes, go ahead.

'For whom, by me'

In order to send data asynchronously, we need to separate asyncore from our control thread, in which we'll enter what to send and to whom.

class ServerThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.daemon = True # if thread is a daemon, it'll be killed when main program exits
        self.cliente = Server()
        self.start()

    def run(self):
        print 'Starting server thread...'
        asyncore.loop()

thread = ServerThread()

while True:
    msg = raw_input('Enter IP and message divided by semicolon: ')

    if msg == 'exit':
        break

    ip, data = msg.split('; ')
    for sock in thread.cliente.connections:
        if sock.addr[0] == ip:
            sock.out_buffer += data
            break

This will work and wait for destination IP and data. Remember to have client connected.

As I said, you can use anything to indicate which socket is which. It can be a class with fields for eg. IP and username, so you could send data only to peers whose usernames start with 'D'.

But...

This solution is a bit rough and needs better knowledge of asyncore module if you want to send data nicely (here it has some delay due to how select() works) and make good use of this socket wrapper. Here and here are some resources.

Syntax note

Although your code will now work, your code has some not-nice things. Semicolons on instructions ends don't cause errors, but making nearly every variable of class attribute can lead to them. For example here:

def handle_accept(self):
    self.sock,self.addr = self.accept();
    if self.addr:
        print self.addr[0];
    handler = EchoHandler(self.sock);

self.sock and self.addr might be used in that class for something other (eg. socket-related thing; addresses) and overriding them could make trouble. Methods used for requests should never save state of previous actions.

I hope Python will be good enough for you to stay with it!

Edit: sock.addr[0] can be used instead of sock.socket.getpeername()[0] but it requires self.addr not to be modified, so handle_accept() should look like this:

def handle_accept(self):
    sock, addr = self.accept()
    if addr:
        print addr[0]
    handler = EchoHandler(sock, self)
    self.connections.append(handler)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM