您的当前位置:首页正文

POP3客户端

2023-12-01 来源:意榕旅游网


一. 实验目的

1. 操作系统,windows xp home edition版本

2. Vc++ 6.0编译器

3. 接入互联网

二. 实验目的

理解POP3协议原理,对邮件接收信息内容进行分析,熟悉SOCKET编程,来实现POP3客户端。

三. 实验内容

实现一个简单的POP3客户端。

1. 连接POP3服务器.

2. 实现命令:USER,PASS,STAT,LIST,RETR,DELE,QUIT。

3.接收邮箱所有邮件

4.邮件进行分析,解析出:主题,发件人,发件时间,发件内容。

5.对个部分内容进行传输编码的解码,根据传输编码,进行base64解码或

Quoted-Printable的解码

6.进行编码转换,将邮件原编码UTF8,GBK或7bit等转换为GBK,可以正确在本机上显示

四. 程序运行

在实验的时候,对pop.sina.com和pop.163.com进行了测试,以下是对pop.163.com服务器,用户pop3client,密码123456的演示(通过选取下拉框中的主题切换邮件)

登录界面:

显示界面:

点击删除按钮:

五. 程序实现

5.1 程序结构

base64_dequoted.h :存放对base64和quoted的解码函数

1.int DecodeQuoted(const char* pSrc, unsigned char* pDst, int nSrcLen)//对quoted传输编码进行解码

2.void Decode(const char *szCoded, BYTE *pOut) //对base64传输编码进行解码

StructInfo.h: 根据邮件的格式,设计的一个结构体

struct MailInfo

{

CString m_Subject; //邮件的主题

CString s_Time; //邮件发送时间

CString s_Person; //邮件的发送人

CString m_Contex; //邮件的内容

CString m_Code; //邮件的编码

};

PopClient.cpp: 存放对pop3的命令操作:USER,PASS,STAT,LIST,RETR,DELE,QUIT

bool ConToServer(const char* sName) pop3服务器名字或者ip;

//连接pop3服务器,sName为

bool LoginToServer(const char *uName,const char *uPass) 器,用户名为uName,用户密码为uName;

//登陆POP3服务

bool RecvFromServer(vector &m_Info) //从POP3服务器获取邮件信息,将信息存储在容器m_Info中;

bool DeleteFromServer(int index) //从POP3服务器删除某个邮件,index指定删除邮件的标示号

bool QuitFromServer() //从pop3服务器退出

int RecvData(void *pData, int nLen) //接收nLen长度的流,在接收大数据量邮件的时候,需要循环读取流

void StringToStruct(const char * msgBuf,MailInfo &mailInfo) //将获取的邮件字符串流,转换到对应的结构体mailInfo中,便于以后显示和操作

int GetContent(char *destStr,CString &dStr,CString code) //获取邮件正文进行传输编码解码,并将编码转换为GBK,存储在dStr中,code为正文的编码

int GetGBKSubject(char *destStr,CString &dStr) //将destStr进行qp或base64解码后,转换为GBK,存储于dStr中

int GetAdress(char *destStr,CString &dStr) //找出邮箱的地址,为<>之间的内容,或者直接为“From:”后面的值,存储于dStr中

LoginDlg.cpp: 用于登录对话框,接收用户输入的pop3服务器地址,用户名和密码,调用PopClient.cpp中的函数连接服务器,登录服务器

PClientDlg.cpp:用于显示邮件的主题,时间,发件人,内容等信息的对话框,还可以进行删除操作和退出操作。

5.2 连接服务器

采用流方式tcp创建socket,可以根据服务器名(如pop.sina.com)或者服务器IP(如sina的pop3的ip: 202.108.3.242),服务器端口号110; 服务器返回"+OK …"表示成功;

/*采用tcp传输*/

pClient = socket(AF_INET,SOCK_STREAM,0);

/*设置POP3服务器的IP以及地址信息*/

SOCKADDR_IN pServerAddr; //存储pop3服务器的地址和端口

pServerAddr.sin_family = AF_INET;

pServerAddr.sin_port = htons(110); //服务器端口为110

pServerAddr.sin_addr.s_addr = inet_addr(sName);

if (pServerAddr.sin_addr.s_addr == INADDR_NONE)

{

HOSTENT *hostInfo; //存储服务器地址信息

hostInfo = gethostbyname(sName);

pServerAddr.sin_addr.s_addr = *((unsigned long *)hostInfo->h_addr); //服务器IP地址

}

int ret; //记录函数执行返回值

/*连接POP3服务器*/

ret = connect(pClient,(SOCKADDR *)&pServerAddr,sizeof(SOCKADDR));

if (ret == SOCKET_ERROR)

return false;

char msgBuf[501]; //存储服务器返回信息

int msgLen; //存储服务器返回信息长度

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

msgBuf[msgLen] = '\\0';

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示连接失败

return false;

return true;

5.3 登录服务器

根据用户输入的用户名和密码,分别向服务器发送"USER xxx\\r\\n", xxx\\r\\n",服务器返回"+OK …"表示成功

char msgBuf[501]; //用于存储发送和接受字符串

int ret; //记录函数执行返回值

int msgLen; //发送或接受字符串长度

//发送用户名给服务器

msgBuf[0] = '\\0';

strcat(msgBuf,\"USER \");

PASS "

strcat(msgBuf,uName);

strcat(msgBuf,\"\\r\\n\");

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

msgBuf[msgLen] = '\\0';

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示用户名发送成功

return false;

//发送用户密码给服务器

msgBuf[0] = '\\0';

strcat(msgBuf,\"PASS \");

strcat(msgBuf,uPass);

strcat(msgBuf,\"\\r\\n\");

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

msgBuf[msgLen] = '\\0';

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示用户登录失败

return false;

return true;

5.4 接收邮件

首先向服务器发送STAT命令,获取邮件的个数mCount,然后根据mCount的个数循环从pop3服务器获取邮件;首先用LIST x命令获取x邮件的大小,然后根据大小接收此邮件,并将其存储于mailInfo结构体

char msgBuf[501]; //用于存储发送和接受字符串

int ret; //记录函数执行返回值

int msgLen; //发送或接受字符串长度

m_Info.clear();

//向服务器发送请求邮箱信息

strcpy(msgBuf,\"STAT\\r\\n\");

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回的邮箱信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示用户请求失败

return false;

msgBuf[msgLen] = '\\0';

int mCount; //记录邮件的个数

int sMail; //记录邮件的长度

sscanf(msgBuf,\"+OK %d %d\

for (int i = 1; i <= mCount; i++)

{

//向服务器发送邮件i信息的请求

sprintf(msgBuf,\"LIST %d\\r\\n\

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回的信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

if (strncmp(msgBuf,\"+OK\服务器返回非+OK表示用户请求失败

return false;

msgBuf[msgLen] = '\\0';

int iFlag; //获取邮箱标示

sscanf(msgBuf,\"+OK %d %d\

//请求接收邮箱i的内容

sprintf(msgBuf,\"RETR %d\\r\\n\

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回的信息

msgLen = recv(pClient,msgBuf,6,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

if (strncmp(msgBuf,\"+OK\服务器返回非+OK表示用户请求失败

return false;

char * mailBuff = new char [sMail+15]; //用于接收邮件内容

msgLen = RecvData(mailBuff,sMail+14);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

mailBuff[msgLen] = '\\0';

MailInfo mailInfo;

StringToStruct(mailBuff,mailInfo);

m_Info.push_back(mailInfo);

delete mailBuff;

}

return true;

5.5 删除邮件

向服务器发送DELE x命令,删除邮件x,但是要在执行QUIT命令后才真正的删除邮件

char msgBuf[501]; //用于存储删除字符串和接收字符串

int ret; //记录函数执行返回值

int msgLen; //发送或接受字符串长度

//向服务器发送请求邮箱信息

sprintf(msgBuf,\"DELE %d\\r\\n\

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回的邮箱信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示用户请求失败

return false;

msgBuf[msgLen] = '\\0';

return true;

5.6 退出登录的pop3服务器

向服务器发送QUIT命令

char msgBuf[501]; //用于存储退出字符串和接收字符串

int ret; //记录函数执行返回值

int msgLen; //发送或接受字符串长度

//向服务器发送请求邮箱信息

sprintf(msgBuf,\"QUIT\\r\\n\");

ret = send(pClient,msgBuf,strlen(msgBuf),0);

if (ret == SOCKET_ERROR || ret <= 0)

return false;

//接收服务器返回的邮箱信息

msgLen = recv(pClient,msgBuf,500,0);

if (msgLen == SOCKET_ERROR || msgLen <= 0)

return false;

if (strncmp(msgBuf,\"+OK\ //服务器返回非+OK表示用户请求失败

return false;

msgBuf[msgLen] = '\\0';

return true;

5.7 quoted解码

int nDstLen; // 输出的字符计数

int i;

i = 0;

nDstLen = 0;

while (i < nSrcLen)

{

if (strncmp(pSrc, \"=\\r\\n\ // 软回车,跳过

{

pSrc += 3;

i += 3;

}

else

{

if (*pSrc == '=') {

if (!strncmp(pSrc, \"=A8\

{

int sd = 5;

// 是编码字节

}

sscanf(pSrc, \"=%02X\

pDst++;

pSrc += 3;

i += 3;

}

else // 非编码字节

{

*pDst++ = (unsigned char)*pSrc++;

i++;

}

nDstLen++;

}

}

// 输出加个结束符

*pDst = '\\0';

return nDstLen;

5.8 base64解码

short nDecTab[256];

short i;

UINT buf;

int nOffset, len = strlen(szCoded);

char *p = (char *)szCoded;

BYTE *r = pOut;

memset(nDecTab, -1, sizeof(short) * 256);

for(i = 0; i < 64; i++)

{

nDecTab[m_alphabet[i]] = i;

}

nDecTab['='] = -1;

while(*p)

{

//4 * 6 ==> 3 * 8

if(p + 4 - szCoded <= len)

{

buf = ((nDecTab[*p] & 0x3F) << 18) | ((nDecTab[*(p + 1)] & 0x3F) << 12) |

((nDecTab[*(p + 2)] & 0x3F) << 6) | (nDecTab[*(p + 3)] & 0x3F);

p += 4;

}

else

{

nOffset = p - szCoded;

if(nOffset != len)

{

buf = 0;

while(*p)

{

buf <<= 6;

buf |= (nDecTab[*p++] & 0x3F);

}

buf >>= ((len - nOffset) * 6) % 8;

for(i = ((len - nOffset) * 6) >> 3; i > 0; i--)

*r++ = (buf >> ((i - 1) << 3)) & 0xFF;

break;

}

}

*r++ = (buf >> 16) & 0xFF;

*r++ = (buf >> 8) & 0xFF;

*r++ = buf & 0xFF;

}

*r = 0;

5.9 将utf-8转换为GBK

char *utf8 = strSubject;

len = MultiByteToWideChar(CP_UTF8,0,(LPCSTR)utf8,-1,NULL,0); WCHAR *wszUtf8 = new WCHAR[len+1];

memset(wszUtf8,0,len*2 + 2);

MultiByteToWideChar(CP_UTF8,0,(LPCSTR)utf8,-1,wszUtf8,len);

len = WideCharToMultiByte(CP_ACP,0,(unsigned *)wszUtf8,-1,NULL,0,NULL,NULL);

GBKstr=new char[len + 1];

memset(GBKstr,0,len+1);

WideCharToMultiByte(CP_ACP,0,(unsigned *)wszUtf8,-1,GBKstr,len,NULL,NULL);

short

short

因篇幅问题不能全部显示,请点此查看更多更全内容