by March 28, 2001 0 comments



In this article, we’ll show you how to write a simple e-mail client using C# (C Sharp). However, to understand this piece, you need to understand the SMTP protocol (For this, see ‘How E-mail Works’, page 76 in this issue). 

Coding Csmail

Before we get onto the code of Csmail, the SMTP client that we’ll code, make sure that you have the .Net Framework SDK Beta installed on the development system.

You’ll find the required code in the file csmail.cs in the Developer section of this month’s CD. To run the compiled csmail.exe, you need to have the .Net Framework Beta 1 installed. Just like Java code requires the JVM (Java Virtual Machine), this requires the NGWS runtime. Open csmail.cs in any text editor. 

The main method of the class CSMail instantiates a CSMail object and calls its GetInputs method. This method takes the required inputs from the user, namely the SMTP server’s name or IP address, the sender’s and recipient’s e-mail addresses, the subject of the message, and the message itself. To keep things simple, instead of typing a full message, we limit ourselves to one line of text that’ll be sent as the mail message, since the point is to get to know how to implement the SMTP client.

After this, the SendMail method is called, which tries to send the e-mail. This method forms the crux of our SMTP client. So let’s dissect it in detail.

In the SendMail method, we start by first instantiating an object, conSMTP, of the TCPClient class. This class is available under the Systems.Net.Sockets namespace, and thus, we have a ‘using’ statement for the same at the start of the code. The TCPClient class provides a high-level abstraction over the underlying Sockets class and encapsulates the generic functionality required to code any kind of TCP client, like our SMTP client. 

Next we call its Connect method. The Connect method is overloaded and we use the overload, which takes in a string containing the name or the IP address of the host to connect to, along with an integer port number. This overloaded version of the Connect method is prototyped as:

public int Connect(string hostname, int port);

The method tries to connect to the host and returns 0 (zero) if the connection is successfully made. If the connection is successfully made with the SMTP server, we move on and declare a few variables. Two interesting variables are the string arrays containing the SMTP commands (string [ ]SMTPCommand) and their corresponding replies from the server (string [ ]SMTPReplyCode). There is a one-to-one mapping between the SMTP command and the reply code. So, upon sending a HELO, the server should reply with a 250, sending a MAIL FROM: should have the server sending a 250 and so on. One peculiar command contained in the SMTPCommand array is the ‘\r\n.’. This isn’t actually a SMTP command. Recall that when we send the mail text after the DATA command’s 354 reply, we have to indicate the termination of the data by sending a CRLF.CRLF, that is, a carriage return-linefeed, followed by a period and then a carriage return-linefeed again. The ‘\r\n.’ is the first portion of this syntax, that is, it’s the carriage return-linefeed and period portion. The second carriage return-linefeed is common to all the SMTP commands and hence, is added later in the code as:

client_command=String.Concat(client_command,”\r\n”);

We next proceed to acquire the network stream using which we’ll be able to send and receive data to or from the server. You can picture the network stream as a tunnel in which traffic can go both ways. We acquire the network stream using the GetStream method of the TCPClient class, which returns a NetworkStream class object, ns. Next, we call the Read method on ns to read the message sent by the server. This is so because upon connecting with the SMTP server, the SMTP server sends a welcome message to the client, containing the 220 success code. We read this message from the stream using the Read method, which is prototyped in the NetworkStream class as:

public override int Read(byte [] buffer,
int offset, int count);

The override modifier indicates that this function is an override of a base class function, which happens to be the Read method of the Stream class in the System.IO namespace. The method expects an array of bytes as the first parameter into which the data from the stream shall be read into. The second parameter, though not documented in the NGWS SDK accompanying the .Net Framework Beta 1, is the offset in the array starting from which the data shall be copied into the array. The third parameter, also undocumented, is the length of the byte array passed as the first parameter. If the read was successful, the number of bytes read is returned. Otherwise, 0 (zero) is returned if the server closed the connection.

Next we convert the array of bytes read into the buffer to a string, using the overload of Encoding.ASCII.GetString, which takes an array of bytes and returns an equivalent ASCII string. It’s for this purpose that we’ve put a reference to the System.Text namespace at the start of the code. We next check if the string contains the 220 success code sent by the server. If it doesn’t, we abort and return false from SendMail. 

However, if the server sent the 220 success code, we proceed to loop through the SMTP commands, sending one at a time, checking the server’s reply code against each one of them. If the reply code doesn’t match what was expected, we abort and return false from SendMail. If we are to send the mail message (this will be in reply to the 354 reply code from the server and the variable index shall be 4 then), we set the string ‘client_command’ to the mail message text, along with the e-mail headers used to identify the mail sender, recipient, subject, type and more. Else, it is set to be a zero length string.

Next, we proceed to concatenate the SMTP command to be sent to the SMTP server to the string client_command. At this point, client_command shall be ‘MAIL FROM: the e-mail address of the sender’. This is followed by appending the sender’s e-mail address. Finally, we terminate the command formation by appending a \r\n (carriage return- linefeed).
The command formed in the client_command string is then converted to an array of bytes using the Encoding.ASCII.GetBytes method, which takes in a string and returns an array of bytes. This byte array is then sent to the SMTP server using the Write method of the NetworkStream object, ns. It is prototyped quite similarly to the Read method as shown below:

public override int Write(byte [] buffer, int offset, int count);

The override modifier as before indicates that this function is an override of a base class function, which happens to be the Write method of the Stream class in the System.IO namespace. The method expects an array of bytes as the first parameter from which the data shall be written to the stream and sent to the server. The second parameter is the offset in the array starting from which the data shall be read and sent to the server. The third parameter is the length of the byte array passed as the first parameter. If the read was successful, the number of bytes written to the stream is returned. Otherwise, 0 (zero) is returned if the connection was closed by the server.

Next, we read in the reply sent by the SMTP server. If everything is fine, the reply sent should contain the success code for the SMTP command that was sent to the server. We check if the reply received contains the expected success code. If yes, we continue to loop until we are done with all the commands, upon which we return true from
SendMail.

To compile the code, enter the following command line

csc /reference:system.net.dll csmail.cs

where csc is your C# compiler and csmail.cs is the code available in this month’s CD. We hope that you’ve enjoyed this journey through SMTP land as much as we’ve enjoyed explaining it. Until next time then, au
revoir.

Kumar Gaurav Khanna 
runs www.wintools.f2s.com

No Comments so far

Jump into a conversation

No Comments Yet!

You can be the one to start a conversation.

Your data will be safe!Your e-mail address will not be published. Also other data will not be shared with third person.