简易电子邮件收信的原理以及实现
上面一篇已经讲到怎样发信了,今天索性来个结尾谈一谈怎样发信!
和前面的流程差点儿相同,我们也手工模拟一次发信流程!
事实上和前面的发信流程差不太多!
一样的,我们以网易的邮箱为例!
我们先要连接到网易的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的代码,加个壳而已!