POP3(post office protocol 3)协议是邮局协议版本3的缩写。最初是在1984年发表的RFC918中定义的,1985年的RFC937发表了第二版1988年的RFC1081又发表了第三个版本,简称POP3,当前使用的标准是RFC1939。它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议 。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。
Pop3使用C/S工作模式,在接收邮件的PC中,运行POP3客户机程序,在用户连接的ISP的邮件服务器中运行POP3服务器程序,两者之间按照POP3相互发送信息,POP3客户机发送给POP3服务器的消息成为POP3命令,POP3服务器返回的消息成为POP3响应。
POP3服务的TCP默认端口为110,当客户主机需要从服务器上的邮件时,它向服务器发出建立一条TCP连接的请求。在连接成功后客户与服务器之间使用POP3协议会话的过程分为3个阶段:1 认证阶段:每一个用户只有提供了正确的用户名和口令之后才有权访问自己的邮箱,在这个阶段里,可以使用USER、PASS和QUIT这3个POP3命令。2 邮件操作阶段:用户通过了认证就相当于打开了服务器上的用户邮箱,客户就有权进行检查、下载或者删除邮件等操作了。这是会话过程进入事物状态,此时可以使用的POP3命令有:NOOP、STAT、QUIT、LIST、RETR、TOP、DELE、RSET和UIDL。3 更新阶段:当客户发送了QUIT命令后,系统就进入了更新阶段,POP3服务器释放在操作阶段中取得的资源,并将逻辑删除的邮件进行物理删除,然后发送消息,关闭客户与服务器之间的TCP连接,邮件处理的会话层结束。
POP3 的命令有ASCII字符组成,它们之间用空格分隔。命令一般由3~4个字母组成。一个命令可以带有一些参数,每个参数可长达40个字符,命令应当以回车换行符结束。
POP3 命令如下:
命令 | 含义 |
USER | 登录验证的用户名 |
PASS | 登录验证口令 |
APOP | 转换验证机制 |
QUIT | 服务器物理删除已逻辑删除的文件,然后关闭连接 |
UIDL | 返回邮件的唯一标识 |
STAT | 查询客户邮箱中邮件的总长度和邮件总数 |
LIST | 命令服务器给出各邮件长度 |
RETR | 从邮箱中取出邮件 |
TOP | 取出信头和邮件的前N行 |
DELE | 对指定的邮件做删除标记 |
RSET | 复位POP3会话 |
NOOP | 空操作,返回一个有效应答,不做任何操作 |
以下程序说明POP3的工作过程:
注:编译环境为VC6.0
//给连接登录信息初始化
strcpy(m_username, username);//用户名,即邮箱号
strcpy(m_password, userpwd);//登录密码,即邮箱登录密码
strcpy(m_svraddr, svraddr);//服务器地址,如pop3.163.com
m_port = port;
//准备与服务器连接
// 创建套接字
m_sock.Create(AF_INET, SOCK_STREAM, 0);
// 解析域名
char ipaddr[16];//把域名解析成IP地址
if ( WSocket::DnsParse(m_svraddr, ipaddr) != true )// WSocket::DnsParse下面说明
{
return false;//如果没有解析成功则返回错误
}
//连接服务器
// 发送连接
if ( m_sock.Connect(ipaddr, m_port) != true )
{
return false;
}
// 接收服务器消息
char buf[128];
int rs = m_sock.Recv(buf, sizeof(buf), 0);
if ( rs <= 0 || strncmp(buf, "+OK", 3) != 0 ) //检查是否连接成功
{
return false;
}
//连接成功之后登录
//发送USER命令,向服务器发送用户名
char sendbuf[128];
char recvbuf[128];
sprintf(sendbuf, "USER %s/r/n", m_username);
m_sock.Send(sendbuf, strlen(sendbuf), 0);
int rs = m_sock.Recv(recvbuf, sizeof(recvbuf), 0);
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 )
{
return false;
}
//发送PASS命令,向服务器发送密码
sprintf(sendbuf, "PASS %s/r/n", m_password);
m_sock.Send(sendbuf, strlen(sendbuf), 0);
rs = m_sock.Recv(recvbuf, sizeof(recvbuf), 0);
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 ) {
return false;
}
//登录成功了开始邮箱操作
//查看邮件
///获取邮件数量
//发送LIST命令,获得邮件信息
char sendbuf[128];
char recvbuf[256];
sprintf(sendbuf, "LIST /r/n");
m_sock.Send(sendbuf, strlen(sendbuf), 0);
int rs = Pop3Recv(recvbuf, sizeof(recvbuf), 0);
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 )
{
return false;
}
recvbuf[rs] = '/0';
sum = GetMailSum(recvbuf);//sum即为获取的邮件数量,
// GetMailSum 下面说明
//获取邮件并保存邮件
//获取邮件并保存邮件
int num;//获取第几封邮件
int rs;
FILE* fp;
int flag = 0;
unsigned int len;
char filename[32];
char sendbuf[128];
char recvbuf[10240];
//发送RETR命令,获取邮件内容
sprintf(sendbuf, "RETR %d/r/n", num);
m_sock.Send(sendbuf, strlen(sendbuf), 0);
do {
rs = Pop3Recv(recvbuf, sizeof(recvbuf), 0);
if ( rs < 0 ) {
return false;
}
recvbuf[rs] = '/0';
// 获得邮件主题,并生成保存邮件的文件名
if ( flag == 0 )
{
GetSubject((char *)(LPCTSTR)Subject,recvbuf);
GetSubject(filename,recvbuf);
strcat(filename, ".eml");
flag = 1;
if ( (fp = fopen(filename, "wb")) == NULL )
return false;
}
//获得邮件大小
len = strlen(recvbuf);
//保存邮件
if ( fwrite(recvbuf, 1, len, fp) != len ) {
fclose(fp);
return false;
}
fflush(fp);
} while ( strstr(recvbuf, "/r/n./r/n") == (char*)NULL );
fclose(fp);
return true;
//操作完成,断开与服务器的连接
//与邮件服务器断开
char sendbuf[128];
char recvbuf[128];
//发送QUIT命令,断开与邮件服务器的连接
sprintf(sendbuf, "QUIT/r/n");
m_sock.Send(sendbuf, strlen(sendbuf), 0);
int rs = m_sock.Recv(recvbuf, sizeof(recvbuf), 0);
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 )
{
return false;
}
//关闭套接字
m_sock.Close();
//结束
相关函数说明:
//服务器域名解析
是自定义类Wsocket的一个静态成员函数
bool WSocket::DnsParse(const char* domain, char* ip)
{
struct hostent* p;
if ( (p = gethostbyname(domain)) == NULL )
return false;
//如果是域名转换成IP地址
sprintf(ip,
"%u.%u.%u.%u",
(unsigned char)p->h_addr_list[0][0],
(unsigned char)p->h_addr_list[0][1],
(unsigned char)p->h_addr_list[0][2],
(unsigned char)p->h_addr_list[0][3]);
return true;
}
//获得邮件主题
bool GetSubject(char* subject, const char* buf)
{
char* p = strstr(buf, "Subject: ");
if ( p == NULL )
return false;
p = p + 9;
for (int i = 0; i < 32; i++) {
if ( p[i] == '/r' || p[i] == '/n' ) {
subject[i] = '/0';
break;
}
subject[i] = p[i];
}
return true;
}
参考文献:李媛媛 《Visual C++ 网络通信开发入门与编程实践》电子工业出版社