2013-01-23 11 views
10

Tôi cần tạo một lớp có thể nhận và lưu trữ tin nhắn SMTP, tức là E-Mail. Để làm như vậy, tôi đang sử dụng asyncore theo một ví dụ được đăng here. Tuy nhiên, asyncore.loop() đang chặn vì vậy tôi không thể làm bất kỳ điều gì khác trong mã.Làm thế nào để xử lý asyncore trong một lớp trong python, mà không chặn bất cứ điều gì?

Vì vậy, tôi đã nghĩ đến việc sử dụng chuỗi. Dưới đây là một ví dụ-mã cho thấy những gì tôi có trong tâm trí:

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() 

Tôi hy vọng bạn sẽ có được hình ảnh. Lớp học MyServer sẽ có thể bắt đầu và dừng nghe cổng 25 theo cách không chặn, có thể truy vấn tin nhắn trong khi nghe (hoặc không). Phương thức start bắt đầu trình nghe asyncore.loop(), trong đó, khi nhận được email, hãy thêm vào danh sách nội bộ. Tương tự, phương pháp stop sẽ có thể dừng máy chủ này, như được đề xuất here.

Mặc dù thực tế mã này không hoạt động như tôi mong đợi để (asyncore có vẻ chạy mãi mãi, thậm chí tôi gọi là stop phương pháp trên. Các error tôi nâng cao được đánh bắt trong stop, nhưng không phải trong target hàm chứa asyncore.loop()), Tôi không chắc liệu cách tiếp cận của tôi đối với vấn đề có hiệu quả hay không. Bất kỳ đề xuất nào để sửa mã trên hoặc đề xuất triển khai vững chắc hơn (mà không cần sử dụng phần mềm của bên thứ ba), được đánh giá cao.

+0

Tôi cảm thấy có sự nhầm lẫn ở đó. Vấn đề với việc chặn 'asyncore.loop()' là gì? Bạn có hiểu tại sao bạn gọi hàm 'loop' và nó làm gì? – mmgp

+0

@mmgp: Vấn đề với 'asyncore.loop()' là nó đang chặn.Tôi muốn có thể sử dụng lớp học bất kỳ lúc nào trong một số mã khác. Ở phía bên kia, tôi không phải là một chuyên gia về 'asyncore.loop()', nhưng AFAIK nó xử lý nội bộ 'select.select', trông giống như ví dụ. cho các tin nhắn SMTP đến trên cổng 25, trong trường hợp này. – Alex

+0

bạn đã sử dụng bộ công cụ GUI chưa? Về cơ bản tất cả chúng đều dựa trên các vòng lặp sự kiện. Bạn phải sắp xếp những thứ sao cho chúng tạo ra các sự kiện để được xử lý bởi "vòng lặp sự kiện". Sự nhầm lẫn mà tôi đã đề cập là bởi vì bạn dường như không biết làm thế nào để sử dụng một vòng lặp sự kiện, là trường hợp? – mmgp

Trả lời

12

Giải pháp được cung cấp có thể không phải là giải pháp phức tạp nhất, nhưng nó hoạt động hợp lý và đã được thử nghiệm.

Trước hết, vấn đề với asyncore.loop() là nó chặn cho đến khi tất cả asyncore kênh bị đóng, vì người dùng Wessie được chỉ ra trong nhận xét trước đó. Đề cập đến số smtp example được đề cập trước đó, nó chỉ ra rằng smtpd.SMTPServer kế thừa từ asyncore.dispatcher (như được mô tả trên smtpd documentation), trả lời câu hỏi về kênh sẽ bị đóng.

Vì vậy, câu hỏi ban đầu có thể được giải đáp như sau ví dụ mã Cập nhật:

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 
    .... 

Vì vậy, cuối cùng, tôi có một và một phương pháp startstop để bắt đầu và dừng lại lắng nghe trên cổng 25 trong một tổ chức phi - môi trường khóa.

+1

Có cách nào để thực hiện tương tự mà không sử dụng chủ đề không? – Vikram

2

Bạn nên cân nhắc sử dụng Twisted. http://twistedmatrix.com/trac/browser/trunk/doc/mail/examples/emailserver.tac minh họa cách thiết lập máy chủ SMTP bằng móc có thể tùy chỉnh được.

+2

xin lỗi, tôi không muốn sử dụng thêm gì đó. Tôi nên rõ ràng hơn. – Alex

+1

Nó không có khả năng sử dụng một cái gì đó "thêm" thực sự sẽ làm tổn thương dự án của bạn. Nó có nhiều khả năng nó sẽ làm cho nó tốt hơn rất nhiều. Bạn đã sử dụng Python, đó cũng là "phụ". Không sao, mọi người có thể cài đặt phần mềm. Bạn thậm chí có thể làm cho các gói siêu trơn để cài đặt nó cho họ. Bằng cách giới hạn bản thân với những gì trong thư viện chuẩn của Python, bạn đang cắt bỏ một lượng lớn chức năng vô cùng hữu ích, Twisted chỉ là một ví dụ nhỏ về nó. Cuối cùng, bạn đang lãng phí thời gian của riêng bạn (và thời gian của những người chọn để trả lời câu hỏi của bạn) và làm tổn thương dự án của bạn. –

4

Xuất phát từ câu hỏi khác asyncore.loop doesn't terminate when there are no more connections

Tôi nghĩ bạn hơi suy nghĩ về luồng. Sử dụng mã từ câu hỏi khác, bạn có thể bắt đầu một chủ đề mới mà chạy asyncore.loop bằng đoạn mã sau:

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() 

này sẽ chạy nó trong một chủ đề mới và sẽ tiếp tục đi cho đến khi tất cả asyncore kênh được đóng lại.

+1

Ok, tôi giả định rằng cách tiếp cận của tôi hơi quá lớn. Nhưng để theo dõi, để kết thúc luồng này, tôi cần phải đóng tất cả các kênh 'asyncore'. Làm thế nào để làm điều đó? Làm thế nào tôi có thể 'ngừng' asyncore? Làm thế nào tôi có thể ngăn chặn chủ đề này? (Là một phần của câu hỏi thực tế của tôi) – Alex

+0

@Alex Cuộc gọi đến 'asyncore.loop' sẽ bỏ chặn khi tất cả các kênh' asyncore' được đóng lại. Các kênh trong ngữ cảnh này đề cập đến bất kỳ lớp con nào của 'asyncore.dispatcher' hoặc' asynchat.async_chat'. Trong trường hợp của bạn, bạn chỉ cần gọi 'server.close()' tại thời điểm bạn muốn thoát. – Wessie

+0

Tôi đã thử rằng trong đoạn mã ví dụ trên và đặt vào phần sau trong phương thức 'stop':' self.close(); self._thread.join() '. Nó không hoạt động. Nó treo ở 'thread.join', ngụ ý rằng có một kênh (?) Khác mở? Cái nào? Làm thế nào để tìm thấy chúng? – Alex