imaplib2:imap.gmail.com处理BYE响应:系统错误(imaplib2 : ima

2019-09-14 22:14发布

我修缮python脚本,检查新邮件的IMAP和发送推送通知,如果有一个新的电子邮件。 问题是,每隔几个小时,我得到一个崩溃。 起初,我也不太懂发生了什么事情,但后来我发现约M.debug = 4 ,给了我一个很好的输出,但我仍然无法理解是什么原因造成的问题。 我已为我的剧本,并与正常行为输出的调试,直至死机,希望有人可以用Python更好的理解能告诉我是怎么回事,我怎么能解决这个问题。

编辑:

我已经编辑我的代码,在回答这个建议:

while True:
    try:
      [call function that does all your logic]
    except imaplib2.IMAP4.abort:
      print("Disconnected.  Trying again.")

或这个:

    while True:
        try:
          [call function that does all your logic]
except (imaplib2.IMAP4.abort, imaplib2.IMAP4.error) as e: 
      print("Disconnected.  Trying again.")

但我仍然得到崩溃的时间未定时间后,异常将永远不会被抓到。 print("Disconnected. Trying again.")将永远不会执行。

编码:

    #!/usr/local/bin/python2.7
    print "Content-type: text/html\r\n\r\n";

    import socket, ssl, json, struct, re
    import imaplib2, time
    from threading import *

    # enter gmail login details here
    USER="username@gmail.com"
    PASSWORD="password"
    # enter device token here
    deviceToken = 'my device token x x x x x'
    deviceToken = deviceToken.replace(' ','').decode('hex')
    currentBadgeNum = -1

    def getUnseen():
        (resp, data) = M.status("INBOX", '(UNSEEN)')
        print data
        return int(re.findall("UNSEEN (\d)*\)", data[0])[0])    

    def sendPushNotification(badgeNum):
        global currentBadgeNum, deviceToken
        if badgeNum != currentBadgeNum:
            currentBadgeNum = badgeNum
            thePayLoad = {
                 'aps': {
                      'alert':'Hello world!',
                      'sound':'',
                      'badge': badgeNum,
                      },
                 'test_data': { 'foo': 'bar' },
                 }
            theCertfile = 'certif.pem'
            theHost = ('gateway.push.apple.com', 2195)

            data = json.dumps(thePayLoad)
            theFormat = '!BH32sH%ds' % len(data)
            theNotification = struct.pack(theFormat, 0, 32, deviceToken, len(data), data)

            ssl_sock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), certfile=theCertfile)
            ssl_sock.connect(theHost)
            ssl_sock.write(theNotification)
            ssl_sock.close()
            print "Sent Push alert."

    # This is the threading object that does all the waiting on 
    # the event
    class Idler(object):
        def __init__(self, conn):
            self.thread = Thread(target=self.idle)
            self.M = conn
            self.event = Event()

        def start(self):
            self.thread.start()

        def stop(self):
            # This is a neat trick to make thread end. Took me a 
            # while to figure that one out!
            self.event.set()

        def join(self):
            self.thread.join()

        def idle(self):
            # Starting an unending loop here
            while True:
                # This is part of the trick to make the loop stop 
                # when the stop() command is given
                if self.event.isSet():
                    return
                self.needsync = False
                # A callback method that gets called when a new 
                # email arrives. Very basic, but that's good.
                def callback(args):
                    if not self.event.isSet():
                        self.needsync = True
                        self.event.set()
                # Do the actual idle call. This returns immediately, 
                # since it's asynchronous.
                self.M.idle(callback=callback)
                # This waits until the event is set. The event is 
                # set by the callback, when the server 'answers' 
                # the idle call and the callback function gets 
                # called.
                self.event.wait()
                # Because the function sets the needsync variable,
                # this helps escape the loop without doing 
                # anything if the stop() is called. Kinda neat 
                # solution.
                if self.needsync:
                    self.event.clear()
                    self.dosync()

        # The method that gets called when a new email arrives. 
        # Replace it with something better.
        def dosync(self):
            print "Got an event!"
            numUnseen = getUnseen()
            sendPushNotification(numUnseen)

    # Had to do this stuff in a try-finally, since some testing 
    # went a little wrong.....
while True:
    try:
        # Set the following two lines to your creds and server
        M = imaplib2.IMAP4_SSL("imap.gmail.com")
        M.login(USER, PASSWORD)
        M.debug = 4
        # We need to get out of the AUTH state, so we just select 
        # the INBOX.
        M.select("INBOX")
        numUnseen = getUnseen()
        sendPushNotification(numUnseen)

        typ, data = M.fetch(1, '(RFC822)')
        raw_email = data[0][1]

        import email
        email_message = email.message_from_string(raw_email)
        print email_message['Subject']

        #print M.status("INBOX", '(UNSEEN)')
        # Start the Idler thread
        idler = Idler(M)
        idler.start()


        # Sleep forever, one minute at a time
        while True:
            time.sleep(60)
    except imaplib2.IMAP4.abort:
      print("Disconnected.  Trying again.")   
    finally:
        # Clean up.
        #idler.stop() #Commented out to see the real error
        #idler.join() #Commented out to see the real error
        #M.close()    #Commented out to see the real error
        # This is important!
        M.logout()

... ... ...

  43:54.43 imap.gmail.com handler _request_pop(continuation, (True, 'idling')) = CHPJ127
  43:54.43 imap.gmail.com handler None:CHPJ127.ready.set
  43:54.43 Thread-4 continuation => True, idling
  43:54.43 Thread-4 server IDLE started, timeout in 1740.00 secs
  43:54.43 Thread-4 state_change_pending.release
  57:13.60 imap.gmail.com reader < * BYE System error\r\n
  57:13.63 imap.gmail.com handler server IDLE finished
  57:13.63 imap.gmail.com handler BYE response: System error
  57:13.63 imap.gmail.com writer > DONE\r\n
  **57:13.63 imap.gmail.com handler terminating: 'connection terminated'**
  57:13.63 imap.gmail.com writer finished
  57:13.63 imap.gmail.com handler last 20 log messages:
  51:49.77 Thread-4 [sync] IDLE ()
  20:50.18 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  20:50.51 Thread-4 [sync] IDLE ()
  49:50.79 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  49:51.02 Thread-4 [sync] IDLE ()
  18:51.33 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  18:51.49 Thread-4 [sync] IDLE ()
  47:51.80 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  47:51.96 Thread-4 [sync] IDLE ()
  16:52.26 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  16:52.63 Thread-4 [sync] IDLE ()
  45:53.08 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  45:53.24 Thread-4 [sync] IDLE ()
  14:53.54 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  14:53.69 Thread-4 [sync] IDLE ()
  43:53.96 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  43:54.17 Thread-4 [sync] IDLE ()
  57:13.63 imap.gmail.com handler BYE response: System error
  57:13.63 imap.gmail.com handler terminating: 'connection terminated'
  57:13.63 imap.gmail.com writer finished
Got an event!
  57:13.63 imap.gmail.com handler state_change_free.set
  57:13.63 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  57:13.63 imap.gmail.com handler finished
  57:13.63 Thread-4 state_change_pending.acquire
  57:13.63 Thread-4 state_change_pending.release
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/usr/local/lib/python2.7/threading.py", line 504, in run
    self.__target(*self.__args, **self.__kwargs)
  File "shaserver.py", line 111, in idle
    self.dosync()
  File "shaserver.py", line 117, in dosync
    numUnseen = getUnseen()
  File "shaserver.py", line 35, in getUnseen
    (resp, data) = M.status("INBOX", '(UNSEEN)')
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1121, in status
    return self._simple_command(name, mailbox, names, **kw)
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1607, in _simple_command
    return self._command_complete(self._command(name, *args), kw)
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1295, in _command
    self._check_bye()
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1229, in _check_bye
    raise self.abort(bye[-1])
abort: System error

  57:13.70 imap.gmail.com reader finished

... ... ...

有时候,我得到这样的:

03:09.29 Thread-4 [sync] IDLE ()
  05:53.25 imap.gmail.com reader socket error: <type 'exceptions.IOError'> - Error Hang up
  05:53.25 imap.gmail.com reader finished
  05:53.26 imap.gmail.com handler terminating: "socket error: <type 'exceptions.IOError'> - Error Hang up"
  05:53.26 imap.gmail.com handler last 20 log messages:
  07:07.66 Thread-4 [sync] IDLE ()
  36:07.78 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  36:07.83 Thread-4 [async] SEARCH ('ALL',)
  36:07.88 Thread-4 [async] FETCH ('1', '(RFC822.HEADER)')
  36:08.09 Thread-4 [sync] IDLE ()
  05:08.19 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  05:08.25 Thread-4 [async] SEARCH ('ALL',)
  05:08.42 Thread-4 [async] FETCH ('1', '(RFC822.HEADER)')
  05:08.48 Thread-4 [sync] IDLE ()
  34:08.58 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  34:08.68 Thread-4 [async] SEARCH ('ALL',)
  34:08.79 Thread-4 [async] FETCH ('1', '(RFC822.HEADER)')
  34:08.94 Thread-4 [sync] IDLE ()
  03:09.05 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  03:09.16 Thread-4 [async] SEARCH ('ALL',)
  03:09.21 Thread-4 [async] FETCH ('1', '(RFC822.HEADER)')
  03:09.29 Thread-4 [sync] IDLE ()
  05:53.25 imap.gmail.com reader socket error: <type 'exceptions.IOError'> - Error Hang up
  05:53.25 imap.gmail.com reader finished
  05:53.26 imap.gmail.com handler terminating: "socket error: <type 'exceptions.IOError'> - Error Hang up"
  05:53.26 imap.gmail.com writer finished
Got an event!
  05:53.26 Thread-4 [async] STATUS ('INBOX', '(UNSEEN)')
  05:53.26 Thread-4 state_change_pending.acquire
  05:53.26 Thread-4 server IDLE finished
  05:53.26 Thread-4 state_change_pending.release
  05:53.26 Thread-4 _get_untagged_response(READ-ONLY) => ['']
  05:53.26 imap.gmail.com handler state_change_free.set
  05:53.26 imap.gmail.com handler finished
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/threading.py", line 551, in __bootstrap_inner
    self.run()
  File "/usr/local/lib/python2.7/threading.py", line 504, in run
    self.__target(*self.__args, **self.__kwargs)
  File "shaserver.py", line 229, in idle
    self.dosync()
  File "shaserver.py", line 235, in dosync
    numUnseen = getUnseen()
  File "shaserver.py", line 150, in getUnseen
    (resp, data) = M.status("INBOX", '(UNSEEN)')
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1121, in status
    return self._simple_command(name, mailbox, names, **kw)
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1607, in _simple_command
    return self._command_complete(self._command(name, *args), kw)
  File "/home/boombe/lib/python2.7/imaplib2/imaplib2.py", line 1305, in _command
    raise self.abort('connection closed')
abort: connection closed

Answer 1:

这是因为异常在另一个线程,并在不被守卫另一个地方抛出except块。 试想一下,在回溯-你会看到,它开始内threading.py (而且也没有异常处理在那里)。 通过继续dosyncgetUnseen方法-再次,没有异常处理。

所以这解释了为什么是你try / except块无效-这是因为你已经明确要求在另一个线程来执行代码。 还有待解释的是为什么你得到的第一个地方例外。 其中一个原因可能是你打它通常与相关的超时IDLE命令-见RFC 2177的细节(这是一个短读取),它会告诉你,你应该打破你的每29分钟空转。 另一种可能性是,Gmail的只是不喜欢你 - 也许他们不喜欢维持其连接太久的客户。 我没有要备份的数据,投机,虽然。

但最重要的问题是 - 你为什么要在所有使用线程? 正如有人谁已实施和IMAP客户端从头(顺便说一句,它在Python很多年前开始),我完全确信线程不需要有效谈IMAP。 在您的特定用途的情况下,你似乎是运行一个线程只为IDLE命令而支出的主线程的时间在无尽wait 。 这本身是我不明白,但也许你有一些重要的原因-也许你真的需要毕竟卸载一些IMAP处理到另一个线程。 但是,请考虑将任何 IMAP相关的活动,以该线程在这种情况下-因为现在,你的线程中的一个执行初始SELECT和一些数据读取,并且只知道它trasnfers的IMAP连接到另一个线程后IDLE 。 这仅仅是邪恶的。

为了线程的基本需要 - 它运作良好,对我来说是利用的IMAP协议的异步性的架构。 我不知道它如何会与玩imaplib2库,也许它不具备真正的异步处理IMAP的支持。 在这种情况下,我只是用在BORIN IMAP,阻止同步的方式,其中的每一个操作真正等待命令完成认真考虑。 是的,他们中的一些将运行很长一段时间-如果你绝对需要执行一些活动,而你如等待的IDLE 29分钟后结束, 然后它可能是到IMAP处理推迟到另一个是个好主意线。 但是,你应该很肯定的是,这是你真正需要你走那条路了。



Answer 2:

我一直从头开始构建相同的应用程序,并收到完全相同的错误(异常与BYE响应)。

如何将渔获线程4个例外

这是通过包装解决

self.M.idle(callback=callback)

try except ,然后保存异常的地方(例如在属性Idler )。

这种保存异常在主线程不断扫描(即睡眠和唤醒每一秒左右),如果存在,在主线程中提出要正确处理(也就是IMAP连接被重新创建,如imaplib2指示图书馆)。

如何避免BYE从服务器响应

为什么出现这种情况摆在首位? 我的猜测是,当一个连接被关闭不清洁(如用kill信号,使得没有清理工作完成后),该服务器没有收到NOOP从连接信号之前,并决定关闭通道。 但现在是接受“关闭通道”信号的新方案。

为什么我猜测呢? 有一次,我已经实现了BYE imaplib2.abort异常捕获,并且也会杀死捕(信号SIGTERM等)的imaplib2.abort例外完全停止。


注意:您也可以找到自己的github两种解决方案的具体实现: https://www.github.com/Elijas/email-notifier



Answer 3:

从你的痕迹,它的外观远程端发送您未经请求的BYE命令,因为它要关闭出于某种原因的连接。

你可能会不得不使你的脚本一点更强大的处理连接故障和BYE命令。

例如,你应该改变你的顶层进入一个循环:

  while True:
    try:
      [call function that does all your logic]
    except imaplib2.IMAP4.abort:
      print("Disconnected.  Trying again.")

此外,您将需要更新您的回调()来看看它的参数。 如果IDLE返回一个错误,因为你用它异步,它会报告错误回调()。 您将需要处理异常那里。 (注意:它不会在你的回调引发错误,只是在参数中返回一个错误。

从imaplib2s文档:

If 'callback' is provided then the command is asynchronous, so after
the command is queued for transmission, the call returns immediately
with the tuple (None, None).
The result will be posted by invoking "callback" with one arg, a tuple:
callback((result, cb_arg, None))
or, if there was a problem:
callback((None, cb_arg, (exception class, reason)))

这意味着你的回调需要看它的参数:

            def callback(args):
                result, arg, exc = args
                if result is None:
                    print("There was an error during IDLE:", str(exc))
                    self.error = exc
                    self.event.set()
                else:
                    self.needsync = True
                    self.event.set()

现在,您可以检查是否有您的惰线程错误。 然后,在你坐下,并在同一时间永远60秒睡你的主线程,你可以设置某种标志的指示,如果你已经断开。



文章来源: imaplib2 : imap.gmail.com handler BYE response: System error