Reputation: 2597
On the basis of the SMTPD component I wrote my own SMTP server with authorization and etc... The last thing I need to do is add message parsing in to new thread when it comes.
So my component consists of two classes: SMTPServer
and SMTPChannel
. It works perfectly in one thread like this:
def handle_accept(self):
pair = self.accept()
if pair is not None:
conn, addr = pair
logging.info('Incoming connection from ' + str(addr))
channel = SMTPChannel(config, conn, addr)
Log look like this:
kiril@kiril-X501A1 ~/www/py_email $ python SMTPServer.py
[2015-05-27 11:15:57] [MainThread] INFO: Server started at 192.168.0.103:1026
[2015-05-27 11:16:46] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52423)
[2015-05-27 11:16:46] [MainThread] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:16:46] [MainThread] DEBUG: --> EHLO : '*******'
[2015-05-27 11:16:46] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello *******\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:16:47] [MainThread] INFO: ('192.168.0.46', 52423) successfully authenticated
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '235 Authentication successful.'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> MAIL : 'FROM:<reply@*******>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> RCPT : 'TO:<destination@*******>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> DATA
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '354 End data with <CR><LF>.<CR><LF>'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> QUIT
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '221 Bye'
[2015-05-27 11:16:47] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52422)
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:16:47] [MainThread] DEBUG: --> EHLO : '*******'
[2015-05-27 11:16:47] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello *******\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:16:48] [MainThread] INFO: ('192.168.0.46', 52422) successfully authenticated
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '235 Authentication successful.'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> MAIL : 'FROM:<reply@*******>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> RCPT : 'TO:<destination@*******>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> DATA
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '354 End data with <CR><LF>.<CR><LF>'
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '250 Ok'
[2015-05-27 11:16:48] [MainThread] DEBUG: --> QUIT
[2015-05-27 11:16:48] [MainThread] DEBUG: <-- '221 Bye'
But when I add threads like this:
def handle_accept(self):
pair = self.accept()
if pair is not None:
conn, addr = pair
logging.info('Incoming connection from ' + str(addr))
new_thread = threading.Thread(target=SMTPChannel, args=(config, conn, addr,))
new_thread.start()
The log looks like this:
kiril@kiril-X501A1 ~/www/py_email $ python SMTPServer.py
[2015-05-27 11:23:29] [MainThread] INFO: Server started at 192.168.0.103:1026
[2015-05-27 11:23:49] [MainThread] INFO: Incoming connection from ('192.168.0.46', 52414)
[2015-05-27 11:23:49] [Thread-1 ] DEBUG: <-- '220 SMTP Server 0.1 ESMTP'
[2015-05-27 11:23:49] [MainThread] DEBUG: --> EHLO : '*****'
[2015-05-27 11:23:49] [MainThread] DEBUG: <-- '250-SMTP Server 0.1 ESMTP Hello ********\n250-SIZE 20480000\n250 AUTH PLAIN LOGIN CRAM-MD5'
[2015-05-27 11:23:49] [MainThread] DEBUG: --> AUTH : 'PLAIN AGFkbWluADEyMzQ='
[2015-05-27 11:23:49] [MainThread] INFO: ('192.168.0.46', 52414) successfully authenticated
As you can see Thread-1
fires only once, then all messages comes back to main thread... May be it has something to deal with class extending as class MerlinSMTPServer(asyncore.dispatcher):
and class SMTPChannel(asynchat.async_chat):
Upvotes: 0
Views: 71
Reputation: 365707
You can't mix asyncore
and threads like this. The whole point of an asyncore
/asynchat
application is that there's an event loop around select
that handles all of your sockets, and makes calls to the various handlers. That all happens on a single thread. See the intro to the asyncore
documentation, which explains this.
Starting a new asynchat
channel on another thread doesn't do any good. That just means that it registers itself with the event loop from another thread. (Which isn't actually thread-safe, so you're just getting lucky if it works.) After that, the event loop, and the dispatching to the channel's handlers, all happens on the main thread.
If you want to use multi-threading, don't use asyncore
. If you want to use asyncore
, don't use multi-threading.
Upvotes: 1