你或许已经有一个Internet的电子邮件帐号,如果这样,你的邮件阅读工具,可能是和WWW 浏览器捆绑在一起的软件,或者是单独的软件如Eudora,要求你输入一个有关SMTP服务器 的信息。它就是你所使用的DNS服务器的域名。这里有一些Internet服务提供商(ISP)供商的SMTP 服务器域名:
你的Internet服务提供商将为它的SMTP服务器使用和上例相似的域名。
..与服务器建立一个TCP/IP会话
一般SMTP服务器在25号端口(port25)监听连接请求。因此,和一个SMTP主机建立一个TCP/IP 连接就是建立一个和25号端口连接的套接字(socket)。下面这段Java程序试图建立一个和 域名为"smtp.tjd.com"的主机的连接:
1 import java.net.*;
2 import java.io.*;
3 :
4 Socket socketSmtpServer = null;
5 DataOutputStream dos = null;
6 DataInputStream dis = null;
7 try {
8 socketSmtpServer = new Socket("smtp.tjd.com", 25);
9 dos = new DataOutputStream(socketSmtpServer.getOutputStream());
10 dis = new DataInputStream (socketSmtpServer.getInputStream());
11 }
12 catch (UnknownHostException e) {throw (e);}
13 catch (IOException e) {throw (e);}
这段代码在建立TCP/IP连接的同时创建一个DataOutputStream对象和一个DataInputStream 对象,我们以后将会使用它们从SMTP服务器发送和接收数据。
..在服务器上登录
和在UNIX系统或者数据库系统上登录不同,你无须在一个SMTP服务器上真正地登录,因为 这里没有确认/授权的过程,根本不需要真正登录。你只是简单地让服务器能识别你,这样才 有资格成发送邮件。这一步其实不真正需要,但是不能忽略它。
当你第一次和服务器连接时,服务器给你发送确认它自己和它的SMTP版本号两行数据。 我们不关心这些数据,我们只是读入它们并且将其忽略。在我们读入这些数据后,服务器将 把我们推到"司机"的座位上然后等待和回答命令。下面是用Java语言实现的登 录过程:
1 String strBuf;
2 String strMyName = "tomdaley";
3 strBuf = dis.readLine();
4 strBuf = dis.readLine();
5 dos.writeBytes("HELO " + strMyName + "\n");
6 strBuf = dis.readLine();
7 dos.writeBytes("RSET\n");
8 strBuf = dis.readLine();
HELO命令让服务器识别你的身份,RSET命令重置SMTP服务器的状态。如果一切顺利,RSET 命令不是必须的。但是因为事情并非总是进行顺利,而RSET是一个发送和执行起来很" 便宜"的命令,因此首先执行这条命令是一个很好的方法。
注意在每条write字节s()语句后的readLine()语句,SMTP服务器为你发送的每条命令 返回一个状态信息。状态信息以一个3字节的数字开始,它被用来判断命令执行成功与否。 RFC821对此有详尽的解释。
.写明收信人的地址
下一步我们准备填写收信人的地址。和所有礼貌的信件相似,我们应该提供给邮件传送 代理和接收者地址同样清楚的回信地址。下面Java的代码实现了这个功能:
1 dos.writeBytes("MAIL FROM:\n");
2 dis.readLine();
3 dos.writeBytes("RCPT TO:\n");
4 dos.readLine();
..撰写邮件的内容
现在我们准备创建邮件最有趣的部分--数据区域。数据区域包括两个子区域:
1.邮件客户程序阅读的头部信息
2.MIME-编码的正文和数据
数据区域的头部信息并不是必须的,但在你使用收信客户程序看信时,它能使你的邮件 看起来更美观。头部信息是邮件内容的一个概括,它使邮件更容易管理。
数据区域头部信息和办公室间的备忘录的开头相似,可以这样发送:
1 dos.writeBytes ("DATA\N");
2 strBuf = dis.readLine();
3 dos.writeBytes ("To: Tom Daley \n");
4 dos.writeBytes ("From: Tom Daley \n");
5 dos.writeBytes ("Subject: Bug Report\n");
注意,我们在发送"DATA"命令后读入且只读入一行。当你发送"DATA" 命令的时候,服务器可能回送下面形式的消息作为应答:"354Entermail,endwith "."onalinebyitself"。这意味着如果SMTP服务器没有看到信件的 结尾,不会通过套接字发送任何数据。
为了能传送序列化编码对象,我们把它封闭到邮件内容的MIME部分。MIME部分最开始是 纯文本,用MIME的语法来描述,就是"Content-Type:text/plain。"。在文本部 分我们将发送若干指令,用来指明一起发送的对象和关于它的简单说明。下面的代码实现 了这种功能:
1 String strBoundary = "SimpleBoundary";
2 String strInstructions = "Save the attached file and read it with BugNews.class.";
2 dos.writeBytes("Mime-Version 1.0\n");
3 dos.writeBytes("Content-Type: multipart/mixed; boundary=\"" + strBoundary + "\"\n");
4 dos.writeBytes("--" + strBoundary + "\n");
5 dos.writeBytes("Content-Type: text/plain; charset=\"us-ascii\"\n\n");
6 dos.writeBytes(strInstructions + "\n");
现在我们该做费了这么多的口舌一直想做的事情,将序列化的对象附在SMTP邮件内容上。 请记住,邮件内容每行不能超出1000个字节。对于有多个部分的MIME则有更多的限制,即每 行不能超出74个字节的二进制编码。这意味着我们必须声明一个string对象,它包含序列 化的,基于BASE64编码对象,然后我们以每次74个字节的方式将这个string对象写入SMTP 套接字。
1 dos.writeBytes("--" + strBoundary + "\n");
2 dos.writeBytes("Content-Type: application/octet-stream; name=\"BugReport.bug\"\n");
3 dos.writeBytes("Content-Transfer-Encoding: base64\n");
4 dos.writeBytes("Content-Disposition: attachment; filename=\"BugReport.bug\"\n");
5 dos.writeBytes("Content-Description: Bug Report from a customer\n\n");
6 int iLines = strObject.length() / 74;
7 for (i = 0; i