15

SMTP メッセージ、つまり電子メールを受信して​​保存できるクラスを作成する必要があります。そのために、ここasyncoreに投稿された例に従って使用しています。ただし、ブロックしているため、コードで他に何もできません。asyncore.loop()

そこで、スレッドを使用することを考えました。これは、私が考えていることを示すコード例です。

class MyServer(smtpd.SMTPServer):
    # derive from the python server class

    def process_message(..):
        # overwrite a smtpd.SMTPServer method to be able to handle the received messages
        ...
        self.list_emails.append(this_email)

    def get_number_received_emails(self):
        """Return the current number of stored emails"""
        return len(self.list_emails)


    def start_receiving(self):
        """Start the actual server to listen on port 25"""

        self.thread =   threading.Thread(target=asyncore.loop)
        self.thread.start()     

    def stop(self):
        """Stop listening now to port 25"""
        # close the SMTPserver from itself
        self.close()
        self.thread.join()

私はあなたが絵を手に入れることを願っています。このクラスMyServerは、ポート 25 のリッスンをブロックしない方法で開始および停止できる必要があり、リッスン中 (またはリッスンしていない場合) にメッセージを照会できる必要があります。このstartメソッドはasyncore.loop()、電子メールの受信が発生すると、内部リストに追加するリスナーを開始します。同様に、ここでstop提案されているように、メソッドはこのサーバーを停止できる必要があります。

このコードが期待どおりに機能しないという事実にもかかわらず (asyncore は、上記のstopメソッドを呼び出しても永遠に実行されるようです。errorI raise は 内でキャッチされますが、を含む関数内stopではキャッチされません)、問題は理にかなっています。上記のコードを修正するための提案や、より堅実な実装 (サードパーティのソフトウェアを使用しない) の提案を歓迎します。targetasyncore.loop()

4

5 に答える 5

17

提供されているソリューションは、最も洗練されたソリューションではないかもしれませんが、適切に機能し、テスト済みです。

まず第一に、ユーザーWessieが以前のコメントで指摘したように、すべてのチャネルが閉じられるasyncore.loop()までブロックされるという問題があります。前述のsmtp の例を参照すると、 ( smtpd のドキュメントで説明されているように)から継承されていることがわかり、どのチャネルを閉じるかという質問に答えます。asyncoresmtpd.SMTPServerasyncore.dispatcher

したがって、元の質問には、次の更新されたコード例で答えることができます。

class CustomSMTPServer(smtpd.SMTPServer):
    # store the emails in any form inside the custom SMTP server
    emails = []
    # overwrite the method that is used to process the received 
    # emails, putting them into self.emails for example
    def process_message(self, peer, mailfrom, rcpttos, data):
        # email processing


class MyReceiver(object):
    def start(self):
        """Start the listening service"""
        # here I create an instance of the SMTP server, derived from  asyncore.dispatcher
        self.smtp = CustomSMTPServer(('0.0.0.0', 25), None)
        # and here I also start the asyncore loop, listening for SMTP connection, within a thread
        # timeout parameter is important, otherwise code will block 30 seconds after the smtp channel has been closed
        self.thread =  threading.Thread(target=asyncore.loop,kwargs = {'timeout':1} )
        self.thread.start()     

    def stop(self):
        """Stop listening now to port 25"""
        # close the SMTPserver to ensure no channels connect to asyncore
        self.smtp.close()
        # now it is save to wait for the thread to finish, i.e. for asyncore.loop() to exit
        self.thread.join()

    # now it finally it is possible to use an instance of this class to check for emails or whatever in a non-blocking way
    def count(self):
        """Return the number of emails received"""
        return len(self.smtp.emails)        
    def get(self):
        """Return all emails received so far"""
        return self.smtp.emails
    ....

したがって、最終的には、ノンブロッキング環境でポート 25 のリッスンを開始および停止startする方法があります。stop

于 2013-01-24T08:24:54.940 に答える
4

他の質問から来るasyncore.loop は、接続がなくなったときに終了しません

スレッド化について少し考えすぎていると思います。asyncore.loop他の質問のコードを使用して、次のコード スニペットを実行する新しいスレッドを開始できます。

import threading

loop_thread = threading.Thread(target=asyncore.loop, name="Asyncore Loop")
# If you want to make the thread a daemon
# loop_thread.daemon = True
loop_thread.start()

asyncoreこれにより、新しいスレッドで実行され、すべてのチャネルが閉じられるまで続行されます。

于 2013-01-23T17:40:49.457 に答える
3

代わりに、Twistedの使用を検討する必要があります。 http://twistedmatrix.com/trac/browser/trunk/doc/mail/examples/emailserver.tacは、カスタマイズ可能な配信時フックを使用してSMTPサーバーをセットアップする方法を示しています。

于 2013-01-23T16:32:11.920 に答える