First of all, I declare that the content of this article and the technologies involved are only for study and technical research, and it does not involve any intentional infringement of the software copyright of the relevant company.
Since the long vacation, I have been pondering emails every day. However, I have carefully studied and understood the SMTP protocol and instructions over the past few days, and I have some knowledge about all aspects of email sending, in particular, the E-mail sending platform software compiled by myself had to learn deeply and improve its ability to do better.
During the compilation of the mail sending applet, I did several tests and found that the mail sending speed was very slow. On average, it would take 15 to 16 seconds to send an email. In this way, if I had 100 emails to send in a certain period of time, it would take more than half an hour to send text messages on a mobile phone. At first, I thought there was a problem with my code. I did not find any problems after careful check. In the tangle, I used Sina's smtp.sina.com server instead of the SMTP server, at this time, it was found that the email was sent at a speed of about 1 to 2 seconds. The problem was found on the company's email server.
The company's mail server uses Lotus Notes 8.0, a minicomputer, and around four thousand users. Theoretically, it should not be so slow. Contact the email server administrator and ask him to monitor the sending process in the background. After monitoring, he told me that every email sent should be connected to the server once, our server just performed a security baseline some time ago, and added a parameter, "Whether the mail sent by the SMTP session to be verified comes from the Internet address of the verified user, the purpose of this setting is to confirm that the user has not attempted to forge the "recipient" domain ". This may result in a long time to connect to the server for verification. The related information is as follows:
[454808: 00136-03743] 13:32:13 SMTP server: localhost (10. 169. **. **) connected
[454808: 00136-03743] 13:32:13 SMTP server: authentication succeeded for user *******; connecting host 10. 169 .**.**
[454808: 00136-02218] 13:32:13 SMTP server: Message 001e6a8c (messageid:) received
[454808: 00136-03743] 13:32:13 SMTP server: localhost (10. 169. **. **) disconnected. 1 message [s] received
[364650: 00020-04113] 13:32:13 router: Message 001e6a8c delivered *******
[454808: 00136-02218] 13:32:30 SMTP server: localhost (10. 169. **. **) connected
[454808: 00136-02218] 13:32:30 SMTP server: authentication succeeded for user *******; connecting host 10. 169 .**.**
[454808: 00136-03743] 13:32:30 SMTP server: Message 001e710c (messageid:) received
[454808: 00136-02218] 13:32:30 SMTP server: localhost (10. 169. **. **) disconnected. 1 message [s] received
[364650: 00016-03085] 13:32:30 router: Message 001e710c delivered *******
If the problem is that it takes a long time to verify the connection, can I verify whether multiple emails are sent continuously and then close them? I carefully checked jmail. net parameters, jmail. net does not provide this function. It can only connect to the server once each time an email is sent, its own group mail function is not suitable for the different purposes of each mail I want to achieve, and it is not suitable for searching some of the content on the Internet, finally, I decided to modify jmail by myself.. net.
Download to a decompilation source code of jmail. Net (if you need to search for the source code by yourself, we will not provide it here), load vs2005, and start to read the code.
The automatic prompt during code writing shows that the sent code is sent by dimac. jmail. SMTP. send () to directly view dimac. jmail. SMTP. CS code. Locate the main function implementation code:
public void Send(Message message) { if (message == null) { throw new ArgumentNullException("message", "Message cannot be null."); } using (SmtpClient client = new SmtpClient(this.m_logStream)) { client.Connect(this.m_hostName, this.m_port); client.Helo(this.m_domain); client.Auth(this.m_authentication, (this.m_userName == null) ? string.Empty : this.m_userName, (this.m_password == null) ? string.Empty : this.m_password); if (message.From != null) { client.MailFrom(message.From); } foreach (Address address in message.To) { client.RcptTo(address.Email); } foreach (Address address2 in message.Cc) { client.RcptTo(address2.Email); } foreach (Address address3 in message.Bcc) { client.RcptTo(address3.Email); } client.Data(message); client.Quit(); } }
Based on my ideas, I want to divide this code into three different subroutines: open (open the port and verify), send (send mail), close (close the connection ), the existing code needs to be modified for these tasks. The main problem in the middle is that you did not pay attention to it at the beginning, and each subprogram uses a using (client = new smtpclient );, in this way, after the subprogram is executed, the client variable will be released, and the desired effect will not be reached. Later, I searched for and consulted some relevant information in SMTP. add the following code to CS:
private SmtpClient m_Client = null; // Methods public void Open(string hostName, short port, string domain, SmtpAuthentication authentication, string userName, string password) { if (null == m_Client) { m_Client = new SmtpClient(); m_Client.Connect(hostName, port); m_Client.Helo(domain); m_Client.Auth(authentication, (userName == null) ? string.Empty : userName, (password == null) ? string.Empty : password); } } public void Close() { System.Diagnostics.Debug.Assert(null != m_Client); if (null != m_Client) m_Client.Quit(); } public void SendEx(Message message) { System.Diagnostics.Debug.Assert(null != m_Client); // if (null == m_Client) //return false; if (message == null) { throw new ArgumentNullException("message", "Message cannot be null."); } if (message.From != null) { m_Client.MailFrom(message.From); } foreach (Address address in message.To) { m_Client.RcptTo(address.Email); } foreach (Address address2 in message.Cc) { m_Client.RcptTo(address2.Email); } foreach (Address address3 in message.Bcc) { m_Client.RcptTo(address3.Email); } m_Client.Data(message); }
In this way, I divide the mail sending process into three steps: first open the port, then send the mail (multiple circular mails can be sent), and finally close the port, it greatly reduces the sending time of multiple emails, implements one verification and multiple sending functions for mail sending. At the same time, I keep the official sending method to facilitate standard calling and sending an email separately. The following two Methods Compare the results on the company's email server. We can see that the speed has been greatly improved.
After this function is successfully modified, I can't help but start thinking again. I remember that in the previous article, I mentioned the replyto feature in jmail 4.4, which is available in the latest jmail. net, and my mail sending platform also needs this function (a sends an email to B through the platform, b. If you want to reply, you 'd better click the reply button to reply to a's mailbox instead of the system's sending email address.) do whatever you want and analyze the code again.
Based on my understanding of SMTP commands, mail header content, and using jmail. net, replyto should be added to dimac. message of the jmail project. CS, carefully checked the message. the CS Code also involves the header. CS and headercollection. modify the CS file.
The modification process is mainly cat-and-tiger-like. There is nothing to say, but it should be noted that we are used to message in our usual use. replyto, but the "reply-to:" command is used in the mail header instructions. There is a minus sign in the middle and it is case sensitive.
First, modify the message. CS file as follows:
// Add the following code
private Address m_replyto; public Address ReplyTo { get { return this.m_replyto; } set { this.m_replyto = value; } }
// Modify public message
Public message ()
: This (new address (), null, null)
{
}Public message (address from, address to, address replyto, string subject, string bodytext)
{
This. m_charset = encoding. ASCII;
This. m_from = from;
This. m_replyto = replyto;
This. m_to = new Addresslist ();
If (! = NULL)
{
This. m_to.add ();
}
This. m_cc = new Addresslist ();
This. m_bcc = new Addresslist ();
This. m_subject = subject;
This. m_date = datetime. now;
This. m_bodytext = bodytext;
This. m_bodyhtml = NULL;
This. m_attachments = new attachmentcollection ();
This. m_headers = new namevaluecollection ();
}
// Modify the following subprograms in the message. CS file. You can refer to m_from to add the corresponding m_replyto:
//public Entity Assemble()/public object Clone()/public static Message Disassemble(Entity entity)
Modify the headercollection. CS file:
// Add the following code public address replyto {get {object headervalue = This. internalgetheadervalue ("replyto"); If (headervalue = NULL) {headervalue = This. internalset ("replyto", new address ()). value;} return (Address) headervalue;} set {This. internalset ("reply-to", value) ;}// modify the judgment part of the following two subprograms public icollection customkeys {get {arraylist list = new arraylist (); foreach (string STR in this. names) {If ((String. Compare ("Content-Type", STR, true )! = 0) & (string. Compare ("content-disposition", STR, true )! = 0) & (string. Compare ("content-transfer-encoding", STR, true )! = 0) & (string. Compare ("subject", STR, true )! = 0) & (string. Compare ("from", STR, true )! = 0) & (string. Compare ("reply-to", STR, true )! = 0) & (string. Compare ("date", STR, true )! = 0) & (string. Compare ("to", STR, true )! = 0) & (string. Compare ("BCC", STR, true )! = 0) & (string. Compare ("cc", STR, true )! = 0) {list. add (STR) ;}} return list ;}} public icollection customkeys {get {arraylist list = new arraylist (); foreach (string STR in this. names) {If (string. compare ("Content-Type", STR, true )! = 0) & (string. Compare ("content-disposition", STR, true )! = 0) & (string. Compare ("content-transfer-encoding", STR, true )! = 0) & (string. Compare ("subject", STR, true )! = 0) & (string. Compare ("from", STR, true )! = 0) & (string. Compare ("reply-to", STR, true )! = 0) & (string. Compare ("date", STR, true )! = 0) & (string. Compare ("to", STR, true )! = 0) & (string. Compare ("BCC", STR, true )! = 0) & (string. Compare ("cc", STR, true )! = 0) {list. Add (STR) ;}} return list ;}}
Modify the header. CS file:
// Modify the program and add an internal header (headercollection parent, string name, object value) for replyto processing {This. m_parent = parent; this. m_name = Name; If (value! = NULL) & (typeof (string) = value. getType () {string contenttype = (string) value; Switch (this. m_name.tolower () {Case "Content-Type": This. m_value = mediatype. parse (contenttype); return; Case "content-disposition": This. m_value = disposition. parse (parent, contenttype); return; Case "content-transfer-encoding": This. m_value = transferencoding. parse (contenttype); return; Case "date": This. m_value = rfc822date. parse (contenttype); return; Case "from": This. m_value = address. parse (contenttype); return; Case "replyto": This. m_value = address. parse (contenttype); return; Case "to": Case "cc": Case "BCC": This. m_value = Addresslist. parse (contenttype); return;} This. m_value = value;} else {This. m_value = value ;}}
This is jmail.. NET added the replyto function. After the solution is regenerated, copy the two DLL files to the bin directory of the application, you can use it.
In this practice, although not a lot of work is done, it is not worth the attention of the experts, but the most important thing is to consider the shortcomings in the actual design and think of the features you need to enhance, in addition, we achieved our original vision through our own efforts and improved our capabilities.
BTW: In fact, I still feel that the slow sending of mail is not a verification, but it takes too long to initiate a connection, but I have not found the specific reason, the current processing method has also achieved effective results.