一. 实验目的
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 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 因篇幅问题不能全部显示,请点此查看更多更全内容