associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Revisão 1e 2004-08-16 02:20:14

Excluir mensagem

MonitorandoSocketsComPyGtk

Receita: Monitorando Sockets com PyGTK

Acredito que quando se cria aplicações de rede, a forma mais eficiente de lidar com conexões simultâneas seja assincronamente. No caso de Python existem basicamente duas opções para isso: os módulos asynchat/asyncore, presentes na biblioteca padrão e o Twisted (http://www.twistedmatrix.com).

No entanto, quando escrevemos aplicações cliente, com interface gráfica há uma outra opção. O princípio de funcionamento desses módulos assíncronos é exatamente o mesmo que um ambiente gráfico utiliza: eventos, dentro de uma única linha de tempo. Nada impede que nós usemos o mesmo loop da interface gráfica para manipular as conexões de rede.

Muitos desconhecem que o GTK (e outros) têm embutida uma interface de alto nível para o select() do sistema operacional, facilitando em muito o trabalho. São os métodos input_add() e input_remove(). O input_add recebe três argumentos: o socket ou arquivo a ser monitorado, uma constante indicando que condição deve ser monitorada e um método a ser chamado quando a condição for satisfeita. O método input_remove remove um socket ou arquivo que esteja sendo monitorado.

Código

# -*- coding:UTF-8 -*-

import socket
import gtk
import GDK

class MainWindow(gtk.GtkWindow):
    def __init__(self):
        gtk.GtkWindow.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.connect("destroy", self.quit)
        self.connect("delete_event", self.quit)
        self.show()
    
        self.set_usize(500, 210)
        self.main_box = gtk.GtkVBox(False, 1)
        self.main_box.set_border_width(1)
        self.add(self.main_box)
        self.body()
        self.server()
        self.main_box.show()

    def body(self):
        textbox = self.textbox = gtk.GtkText()
        textbox.set_editable(True)
        self.main_box.pack_start(textbox, True, True, 0)
        textbox.show()

        entry = self.entry = gtk.GtkEntry(1024)
        self.main_box.pack_start(entry, True, True, 0)
        entry.show()

        send = gtk.GtkButton('Enviar')
        send.connect('clicked', self.send)
        self.main_box.pack_start(send, True, True, 0)
        send.show()

    def send(self, data=None, widget=None):
        text = self.entry.get_text()
        self.do_send(text)
        self.entry.set_text('')

        # nada de novo até aqui

    def do_send(self, data):

        # envia ''data'' para todos os clientes conectados

        for addr, (conn, tag) in self.clients.iteritems():
            conn.send(data)

    def server(self):

        # inicializa o servidor

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.bind(('localhost', 8000))
        self.sock.listen(1)

        # a chamada para input_read para o socket do servidor é um pouco 
        # diferente, tendo como callback o método self.accept

        self.server_tag = gtk.input_add(self.sock, GDK.INPUT_READ, \
                                          self.accept)

        # mantemos uma lista dos clientes conectados

        self.clients = {}

    def accept(self, source, condition):

        # método chamado quando o servidor tem um cliente 
        # esperando para ser aceito

        conn, addr = source.accept()
        self.insert("%s:%s conectado\n" % addr)

        # insere o cliente na lista e registra o método self.write
        # como  callback para quando existirem dados esperando
        # para serem lidos

        self.clients[addr] = (conn, gtk.input_add(conn, \
                               GDK.INPUT_READ, self.write))

    def write(self, source, condition):

        # método chamado quando um cliente envia dados 

        data = source.recv(1024)
        if data.strip() == 'bye' or not len(data):

            # se o cliente enviar um ''bye'', desconecte-o :)

            source.close()
            for addr, (conn, tag) in self.clients.iteritems():
                if source is conn:
                    gtk.input_remove(tag)
                    self.insert('%s:%s desconectado\n' % addr)
                    del self.clients[addr]
                    break
        else:
            for (addr, port), (conn, tag) in self.clients.iteritems():
                if source is conn:
                    self.insert('%s:%s >>> %s\n'%(addr, port, \
                                data.strip()))
                    break

    def insert(self, data):
        self.textbox.insert_defaults(data)

    def quit(self, *args):
        gtk.input_remove(self.server_tag)
        for addr, (conn, tag) in self.clients.iteritems():
            gtk.input_remove(tag)
            conn.close()
        self.sock.close()

        self.hide()
        self.destroy()
        gtk.mainquit()

if __name__ == '__main__':
    MainWindow()
    gtk.mainloop()


PedroWerneck