您的位置:首页 > 其它

简易电子邮件收信的原理以及实现

2016-01-20 09:59 615 查看
上面一篇已经讲到怎样发信了,今天索性来个结尾谈一谈怎样发信!

和前面的流程差点儿相同,我们也手工模拟一次发信流程!

事实上和前面的发信流程差不太多!

一样的,我们以网易的邮箱为例!



我们先要连接到网易的pop邮箱!

命令为: telnet pop.163.com 110

意思非常明显,要求连接到网易的popserver的110号port.





然后就能够登陆了!

输入命令:user xxxxx (你的username,不用加密)

假设没有出错的话,系统通常会返回+OK之类的东西.

然后输入:pass xxxxxx(你邮箱的password,不加密)

一样的,假设没有出错的话,系统通常会返回+OK之类的东西.





如今我们就能够操作了。

尽管能够使用的命令非常多,只是最经常使用的命令仅仅有两个!

第一个是list命令,用来列出邮件的条目!我们看一下。





表示有19封邮件。右边是邮件大小。



另一个命令自然是retr命令了!它用来获取邮件!

看我演示:






retr使用规则是 retr + 你要获取的邮件的编号!



好吧!

既然已经讲到这份上了,我顺便提一句!server发送的大部分内容是用base64加密了的。所以我们看到满屏幕的字母!

那么怎么读取出内容呢?这不是这篇文章的重点,所以我们代码採取的方式是直接将server发送过来的邮件内容写到文件中,存成.eml文件,然后邮件client能够打开这样的文件。推荐採用foxmail来打开这样的文件!


最后。不要忘了quit命令,关闭与server的连接,这里就不再演示!

看代码吧!



pop3.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pop3.h"

CPop3::CPop3()
{
WSADATA wsaData;
WORD version = MAKEWORD(2, 0);
WSAStartup(version, &wsaData);
}

CPop3::~CPop3()
{
WSACleanup();
}

int CPop3::Pop3Recv(char* buf, int len, int flags)
{/*接收数据*/
int rs;
int offset = 0;

do
{
if (offset > len - 2)
return offset;

rs = recv(m_sock, buf + offset, len - offset, flags);
if (rs < 0) return -1;

offset += rs;
buf[offset] = '\0';
}while (strstr(buf, "\r\n.\r\n") == (char*)NULL);

return offset;
}

bool CPop3::Create(
const char* username,	//用户名
const char* userpwd,		//用户password
const char* svraddr,		//server地址
unsigned short port		//服务端口
)
{
strcpy(m_username, username);
strcpy(m_userpwd, userpwd);
strcpy(m_svraddr, svraddr);
m_port = port;

return true;
}

bool CPop3::Connect()
{
//创建套接字
m_sock = socket(AF_INET, SOCK_STREAM, 0);

//IP地址
char ipaddr[16];

struct hostent* p;
if ((p = gethostbyname(m_svraddr)) == NULL) //假设得不到server信息,就说明出错
{
return FALSE;
}

sprintf(
ipaddr,
"%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]
);

//连接popserver
struct sockaddr_in svraddr;
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = inet_addr(ipaddr);
svraddr.sin_port = htons(m_port);
int ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
if (ret == SOCKET_ERROR)
{
return FALSE;
}

//接收pop3server发来的欢迎信息
char buf[128];
int rs = recv(m_sock, buf, sizeof(buf), 0);
buf[rs] = '\0';

printf("%s", buf);
if (rs <= 0 || strncmp(buf, "+OK", 3) != 0)  /*server没有返回OK就出错了*/
{
return FALSE;
}

return TRUE;
}

bool CPop3::Login()
{/*登陆*/

/*发送用户命令*/

char sendbuf[128];
char recvbuf[128];

sprintf(sendbuf, "USER %s\r\n", m_username);
printf("%s", sendbuf);
send(m_sock, sendbuf, strlen(sendbuf), 0); //发送用户名

int rs = recv(m_sock, recvbuf, sizeof(recvbuf), 0);	//接收server发来的信息
recvbuf[rs] = '\0';
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 )  //假设没有"+OK"就说明失败了
{
return FALSE;
}
printf("%s", recvbuf);

/*发送password信息*/
memset(sendbuf, 0, sizeof(sendbuf));
sprintf(sendbuf, "PASS %s\r\n", m_userpwd);
send(m_sock, sendbuf, strlen(sendbuf), 0);
printf("%s", sendbuf);

rs = recv(m_sock,recvbuf, sizeof(recvbuf), 0);
recvbuf[rs] = '\0';
if (rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0)
{
return FALSE;
}
printf("%s", recvbuf);

return TRUE;
}

bool CPop3::List(int& sum)
{
/*发送list命令*/
char sendbuf[128];
char recvbuf[256];

sprintf(sendbuf, "LIST \r\n");
send(m_sock, sendbuf, strlen(sendbuf), 0);
printf("%s", sendbuf);

int rs = recv(m_sock, recvbuf, sizeof(recvbuf), 0);
if (rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0)
{
return FALSE;
}
recvbuf[rs] = '\0';
printf("%s", recvbuf);

sum = GetMailSum(recvbuf); //得到邮件的数目

return TRUE;
}

bool CPop3::FetchEx(int num)
{
int rs;
FILE* fp;
int flag = 0;
unsigned int len;
char filename[256];

char sendbuf[128];
char recvbuf[20480];

/* 发送RETR命令*/
sprintf(sendbuf, "RETR %d\r\n", num);
send(m_sock, sendbuf, strlen(sendbuf), 0);
do
{
rs = Pop3Recv(recvbuf, sizeof(recvbuf), 0); //接收数据
if (rs < 0)
{
return FALSE;
}

recvbuf[rs] = '\0';

printf("Recv RETR Resp: %s", recvbuf); //输出接收的数据

if (flag == 0)
{
itoa(num, filename, 10); //依照序号给文件排名
strcat(filename, ".eml");

flag = 1;
fp = fopen(filename, "wb");//准备写文件
}
len = strlen(recvbuf);
fwrite(recvbuf, 1, len, fp);
fflush(fp); //刷新
}while (strstr(recvbuf, "\r\n.\r\n") == (char*)NULL);

fclose(fp);
return TRUE;
}

bool CPop3::Quit()
{
char sendbuf[128];
char recvbuf[128];

/*发送QUIT命令*/
sprintf(sendbuf, "QUIT\r\n");
send(m_sock,sendbuf, strlen(sendbuf), 0);
int rs = recv(m_sock, recvbuf, sizeof(recvbuf), 0);
if (rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0)
{
return FALSE;
}

closesocket(m_sock);
return TRUE;
}

int CPop3::GetMailSum(const char* buf)
{
int sum = 0;
char* p = strstr(buf, "\r\n");
if (p == NULL)
return sum;

p = strstr(p + 2, "\r\n");
if (p == NULL)
return sum;

while ((p = strstr(p + 2, "\r\n")) != NULL)
{
sum++;
}

return sum;
}
pop3.h

#include <WinSock2.h>
#pragma  comment(lib, "ws2_32.lib")	/*链接ws2_32.lib动态链接库*/

class CPop3 {

public:
CPop3();
~CPop3();

//初始化pop3的属性
bool Create(const char* username, const char* userpwd, const char* svraddr,
unsigned short port = 110);

//连接pop3server
bool Connect();

//登陆的server
bool Login();

//利用list命令得到全部的邮件数目
bool List(int& sum);

//获得序号为num的邮件
bool FetchEx(int num = 1);

//退出命令
bool Quit();

protected:
int GetMailSum(const char* buf);

SOCKET m_sock;
char m_username[32];	/*用户名*/
char m_userpwd[32];		/*password*/
char m_svraddr[32];		/*server域名*/
unsigned short m_port;

private:
int Pop3Recv(char* buf, int len, int flags = 0);

};
然后用一个主程序測试一下:

main.cpp

#include <stdio.h>
#include "pop3.h"

int main()
{
int sum;
CPop3 pop3;
char userName[256] = "it_is_just_a_test@163.com";
char password[256] = "19930714lyh";
char srv[256] = "pop.163.com";
pop3.Create(userName, password, srv, 110);

pop3.Connect(); //连接pop3server

pop3.Login();

pop3.List(sum);

if (sum < 0)
printf("You have no letter in box !");

for (int i = 1; i <= sum; i++)
{
pop3.FetchEx(i);
}

pop3.Quit();

return 0;
}


在VC6以下測试完美通过!然后看看你的project的目录以下,是不是出现了非常多.eml文件?这些文件能够用foxmail打开,这就是接收到的邮件!

赶快尝试一下吧!



文章写到这里,建议的收信,写信基本上都已经说明确了,事实上你略微包装一下。就能够写出一段MFC的邮件client的代码,加个壳而已!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: