Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Das Versenden von Emails selbst programmieren

Geschrieben von: Christoph Wille
Kategorie: ASP.NET

This printed page brought to you by AlphaSierraPapa

Emailkomponenten gibt es (beinahe) soviele wie Sand am Meer, und viele davon sind sogar gratis. Verwendet hat sie bestimmt schon (beinahe) jeder, sei es jetzt AspMail, CDONTS, SA-SmtpMail oder eine der vielen anderen. Etwas, das aber bestimmt (beinahe) keiner bis jetzt gemacht hat, ist sich eine Emailkomponente selbst zu programmieren.

Im heutigen Artikel befassen wir uns mit den Grundlagen, und werden nur eine sehr einfache Email versenden. Spätere Artikel werden auf dem heute vorgestellten Sourcecode aufbauen, und am Ende werden wir eine voll funktionstüchtige Komponente für HTML Emails und Attachments haben.

Voraussetzung um den Sourcecode dieses Artikels verwenden zu können ist eine Installation des Microsoft .NET Framework SDK's auf einem Webserver. Weiters setze ich voraus, daß der Leser die Programmiersprache C# zu einem gewissen Grad beherrscht - es finden sich etliche Artikel auf diesem Server, um das notwendige Wissen zu erlernen

Eine minimale Email

Als erstes müssen wir abklären, wie das SMTP (Simple Mail Transport Protocol) Protokoll funktioniert - weil dieses die Grundlage für den Versand von Emails darstellt. Das schöne ist, daß alle Befehle und Antworten vom SMTP Server in Klartext verschickt werden (was man allerdings wiederum auch als Sicherheitsproblem auslegen kann...).

Das folgende "Script" zeigt die grundsätzliche Reihenfolge der SMTP Befehle um eine Mail zu verschicken:

HELO local computername
MAIL FROM: sender's address
RCPT TO: recipient's address
DATA
mail header and body text
.

Um nun tatsächlich eine Email zu verschicken, verbindet man sich mittels Telnet mit Port 25 (der SMTP Port, telnet servername 25) am Emailserver und tippt eine Beispielemail ein:

HELO SHAGPAD
MAIL FROM: christophw@alphasierrapapa.com
RCPT TO: christophw@dev.alfasierrapapa.com
DATA
Subject: Hi Chris

This is a part of the body text
.

Die Parameter HELO, MAIL FROM und RCPT TO sind unspektakulär und waren "vorhersehbar". Der DATA Parameter ist hingegen interessant: die Header (Subject) und der Body Text sind durch eine Leerzeile voneinander getrennt. Dieses Beispiel zeigt nur einen Header (Subject), folgende Beispiele werden die fehlenden Header einfügen.

Der Punkt schließt die Email ab, und der Server meldet den Status der Operation. Apropos Statusmeldungen: jedes der gezeigten Kommandos liefert eine Statusmeldung des Servers - diese können sehr nützlich sein!

Implementierung der minimalen Email in C#

Wir werden diesen Prozess jetzt 1:1 in C# umsetzen, und zwar mit Hilfe der Net Klassen des .NET Frameworks. Grundsätzlich dreht sich alles um eine Instanz der TcpClient Klasse und dessen Stream - für die Verbindung zum SMTP Server, und zum Austausch von Befehlen und Statusmeldungen.

Die Datei email-simple.aspx ist im Download des heutigen Artikels enthalten.

<% @Page Language="C#" %>
<% @Import Namespace="System.IO" %>
<% @Import Namespace="System.Net" %>
<% @Import Namespace="System.Net.Sockets" %>
<% @Import Namespace="System.Text" %>
<script language="C#" runat="server">
bool WriteToStream(ref NetworkStream nwstream, string strLine)
{
  string strString2Send = strLine + "\r\n";
  Byte[] arr2Send = Encoding.ASCII.GetBytes(strString2Send.ToCharArray());
  try
  {
    nwstream.Write(arr2Send, 0, arr2Send.Length);
  }
  catch
  {
    return false;
  }
  return true;
}

bool ReadFromStream(ref NetworkStream nwstream, out string strMessage)
{
  byte[] readBuffer = new byte[255];
  int nLength = nwstream.Read(readBuffer, 0, readBuffer.Length);
  strMessage = Encoding.ASCII.GetString(readBuffer, 0, nLength);
  return (3 <= readBuffer[0]); // 2 success, 3 informational
}
</script>
<%
Response.Buffer = false;

string strEmailServer = "fx2.dev.alfasierrapapa.com";
string strSendTo = "christophw@fx2.dev.alfasierrapapa.com";
string strMailFrom = "christophw@alphasierrapapa.com";
string strSubject = "My Email Test";

TcpClient tcpc = new TcpClient();
try
{
  tcpc.Connect(strEmailServer, 25);
}
catch (SocketException socketEx)
{
  Response.Write("Connection Error: " + socketEx.ToString());
  Response.End();
}
string strResponse;

NetworkStream nwstream = tcpc.GetStream();
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "HELO myhost");
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "MAIL FROM: " + strMailFrom);
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "RCPT TO: " + strSendTo);
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "DATA");
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "Subject: " + strSubject);

WriteToStream(ref nwstream, ".");
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");
%>

Wenn wir uns nur auf den unteren Teil des Listings konzentrieren (die Dreier-Codeblöcke), dann sieht dies wirklich nicht anders aus als wenn man händisch per Telnet eine Email erstellen würde. Die Funktionalität für das Senden der Befehle und Abfragen der Statusmeldungen ist in den Funktionen WriteToStream bzw ReadFromStream versteckt.

Die WriteToStream Methode ist für das Versenden der Befehle verantwortlich. Dies geschieht durch Anhängen eines CR/LF Paares an den String, sowie dessen Konvertierung in ein Byte-Array und das darauffolgende Verschicken an den SMTP Server durch die Write Methode des NetworkStream Objektes. Der Rückgabewert ist true/false, abhängig davon ob der Befehl erfolgreich gesendet werden konnte:

bool WriteToStream(ref NetworkStream nwstream, string strLine)
{
  string strString2Send = strLine + "\r\n";
  Byte[] arr2Send = Encoding.ASCII.GetBytes(strString2Send.ToCharArray());
  try
  {
    nwstream.Write(arr2Send, 0, arr2Send.Length);
  }
  catch
  {
    return false;
  }
  return true;
}

Die ReadFromStream Methode funktioniert im Grunde sehr ähnlich, nur daß sie ein Byte-Array ausliest, und dann als String an den Aufrufer zurückliefert. Da jede Statusmeldung des SMTP Servers mit einem dreistelligen Statuscode beginnt, verwende ich diesen, um true/false zu berechnen: 2xx sind Erfolgsmeldungen, 3xx sind Erfolge mit Zusatzinformation:

bool ReadFromStream(ref NetworkStream nwstream, out string strMessage)
{
  byte[] readBuffer = new byte[255];
  int nLength = nwstream.Read(readBuffer, 0, readBuffer.Length);
  strMessage = Encoding.ASCII.GetString(readBuffer, 0, nLength);
  return (3 <= readBuffer[0]); // 2 success, 3 informational
}

Obwohl beide Methoden boolsche Resultate liefern, ignoriere ich diese aus Einfachheitsgründen im heutigen Beispiel. Dafür gebe ich die Statusmeldungen des SMTP Servers aus - etwas, das man in einer Produktionsumgebung nicht machen würde:

Laut Statuscode war der Emailversand erfolgreich - auch Outlook Express ist dieser Meinung:

Allerdings sieht das Ergebnis nicht ansprechend aus, weil Outlook spezielle Header für Absender und Empfänger erwartet - Eudora ist hier nicht so kleinlich, aber eben nicht so weit verbreitet. Daher müssen wir noch eine Kleinigkeit hinzuprogrammieren, um die Outlookianer gewogen zu stimmen.

Optischer Aufputz für Outlook

Diese Kleinigkeit sind die Header To und From. Diese werden einfach im Headerteil mit angeführt (email-better.aspx):

WriteToStream(ref nwstream, "DATA");
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

WriteToStream(ref nwstream, "From: " + strMailFrom);
WriteToStream(ref nwstream, "Subject: " + strSubject);
WriteToStream(ref nwstream, "To: " + strSendTo);
WriteToStream(ref nwstream, "");

WriteToStream(ref nwstream, "Hello Christoph!");

WriteToStream(ref nwstream, "\r\n.");
ReadFromStream(ref nwstream, out strResponse);
Response.Write(strResponse + "<br>");

Dieses Mal ist auch Outlook Express so "freundlich" und zeigt alle gewünschten Informationen an:

Schlußbemerkung

Heute haben wir nur eine simple Email verschickt, allerdings dabei alle wichtigen SMTP Kommandos kennengelernt. Was wir in den nächsten Artikeln einfügen werden sind Kopie- und Blindkopieempfänger, Content-Type, zusätzliche Header, HTML Emails und Attachments. Stay tuned!

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20001009.zip

Verwandte Artikel

ACT MetaMailer - Mails verschicken leicht gemacht
http:/www.aspheute.com/artikel/20021028.htm
Email versenden mit AspMail
http:/www.aspheute.com/artikel/19990809.htm
Emails mit ASP.NET versenden
http:/www.aspheute.com/artikel/20000918.htm
Emails mittels CDOSYS versenden
http:/www.aspheute.com/artikel/20000919.htm
Emails senden mit CDONTS
http:/www.aspheute.com/artikel/20000515.htm

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.