beatsbears
beatsbears

Reputation: 211

how to use threading with a 'neverending' process

I'm attempting to add an irc client to a django web application I'm working on. I'd like the server side code for the irc communication to use python and connect through the sockets library. I'll then use gevent-socketio to send and receive data from the irc channel to the client side in the browser. So far I have a very basic gevent-socketio server and client that can be used for realtime broadcasting amongst multiple clients, however, when I start to connect clients to IRC they connect to the IRC server successfully using a nick passed from the client, but then it appears that this is ongoing keepalive/listening process is blocking be from sending any messages from the client.

Python IRC code

import socket

class pycli:
    def __init__(self,user):
        self.nick = user
        self.chan = "#testchannel"
        self.owner = "Bluebot"
        self.sock = socket.socket()
        print "irc conn"

    def send_msg(self,message):
        self.sock.send("PRIVMSG " + self.chan + " : " + message + "\r\n")
    ## misc setup
    def keep_alive(self):
        self.sock.connect(("irc.freenode.net",6667))
        self.sock.send("USER " + self.nick + " 0 * :" + self.owner + "\r\n")
        self.sock.send("NICK " + self.nick + "\r\n")
        while 1:
            ## keep checking for data
            data = self.sock.recv(512)
            datalen = len(data.split(" "))
            sender = ""
            msg_type = ""
            msg_rcpt = ""
            message = ""

        ###----------------------------------------------
        ## reply to keep alive pings
            if data[0:4] == "PING":
                self.sock.send(data.replace("PING", "PONG"))
            if data[0]!=':':
                continue
            if data.split(" ")[1] == "001":
                self.sock.send("MODE " + self.nick + " +B\r\n")
                self.sock.send("JOIN " + self.chan + "\r\n")

        ###-----------------------------------------------
        ##split and assign data parts

        ## parse out the actual sender
            send_data = data.split(" ")[0]
            send_data = send_data[1:]
            sender = send_data.split('!')[0]

        ## mode
            msg_type = data.split(" ")[1]

        ## if it exists get the recipient (room or private)
            if datalen > 2:
                msg_rcpt = data.split(" ")[2]

        ## get the actual message body  
            if datalen > 3:
                message = (" ".join(data.split(" ")[3:])).lower()[1:]
            print data

I know this functionality is super basic, but I can expand on it once I get it working through the client.

The relevant parts of my server code basically looks like:

def on_login(self, nick):
    if self.nick:
        self._broadcast('exit', self.nick)
        self.nick = nick
        self._broadcast('enter', nick)
        self.emit('users',
              [ ns.nick
                for ns in self._registry.values()
                if ns.nick is not None ])
        t = threading.Thread(target=self.make_start_irc(nick),daemon=True)
        t.start()

def on_chat(self, message):
    if self.nick:
        self._broadcast('chat', dict(u=self.nick, m=message)) 
        self._irc_nicks[self.nick].send_msg("this is a test")
    else:
        self.emit('chat', dict(u='SYSTEM', m='You must first login'))

def make_start_irc(self,nick):
    if nick not in self._irc_nicks.values():
        self._irc_nicks[nick] = pycli.pycli(nick)
        print self._irc_nicks
        self._irc_nicks[nick].keep_alive()

def _broadcast(self, event, message):
    for s in self._registry.values():
        s.emit(event, message)

def chat(environ, start_response):
if environ['PATH_INFO'].startswith('/socket.io'):
    return socketio_manage(environ, { '/chat': ChatNamespace })
else:
    return serve_file(environ, start_response)

def serve_file(environ, start_response):
    path = os.path.normpath(
        os.path.join(public, environ['PATH_INFO'].lstrip('/')))
    assert path.startswith(public), path
    if os.path.exists(path):
        start_response('200 OK', [('Content-Type', 'text/html')])
        with open(path) as fp:
            while True:
                chunk = fp.read(4096)
                if not chunk: break
                yield chunk
    else:
        start_response('404 NOT FOUND', [])
        yield 'File not found'


if __name__ == "__main__":
    from gevent import monkey
    monkey.patch_all()
    sio_server = SocketIOServer(
    ('', 8080), chat, 
    policy_server=False)
    t2 = threading.Thread(target=sio_server.serve_forever())
    t2.start()

When I eventually give up and use ctrl-C, I see the following stacktrace which leads me to believe something about the way I'm threading is blocking.

Traceback (most recent call last):
  File "socketio_test.py", line 92, in <module>
    t2 = threading.Thread(target=sio_server.serve_forever())
  File "/Users/andrewscott/Desktop/wham/pycli/wham/lib/python2.7/site-packages/gevent/baseserver.py", line 284, in serve_forever
    self._stop_event.wait()
  File "/Users/andrewscott/Desktop/wham/pycli/wham/lib/python2.7/site-packages/gevent/event.py", line 77, in wait
    result = self.hub.switch()
  File "/Users/andrewscott/Desktop/wham/pycli/wham/lib/python2.7/site-packages/gevent/hub.py", line 338, in switch
    return greenlet.switch(self)
KeyboardInterrupt

If anyone has any idea how I can change the irc process to be non-blocking, or any general suggestions they'd be greatly appreciated.

Upvotes: 1

Views: 121

Answers (1)

Dan D.
Dan D.

Reputation: 74675

You should remove the call:

t2 = threading.Thread(target=sio_server.serve_forever())

And properly pass the method:

t2 = threading.Thread(target=sio_server.serve_forever)

Upvotes: 0

Related Questions