今天小编给大家讲解EMAIL的客户端程序 ,这个程序也是小编我在东北大学读研究生期间学习的,今天分享给大家。
1. 实验目的
随着互联网的发展,越来越多的人使用电子邮件交流工作,电子邮件已经成为生活中必不可少的一部分。本系统目的通过客户端软件实现电子邮件的发送和接收。通过SMTP协议来发送邮件到邮件服务器,通过POP3协议从邮件服务器中读取邮件。
2. 实验环境
Windows平台下,用Visual studio 2010 对C 程序进行编写,编译,实现SMTP协议和POP3协议,进而实现对邮件的接收和发送。
3. 实验内容和要求
实现基本的发送和接收邮件的功能。
其中,发送邮件的程序必须能够做与接收电子邮件的服务器相连接,将相关的信息送入收件人的信箱。邮件接收的程序能够实现与存放电子邮件的服务器相连,读取相关的邮件信息,并且在本地显示。
4. 实验原理
POP3,全名为“Post Office Protocol - Version 3”,即“邮局协议版本3”。是TCP/IP协议族中的一员,由RFC1939 定义。本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。提供了SSL加密的POP3协议被称为POP3S。
POP 协议支持“离线”邮件处理。其具体过程是:邮件发送到服务器上,电子邮件客户端调用邮件客户机程序以连接服务器,并下载所有未阅读的电子邮件。这种离线访问模式是一种存储转发服务,将邮件从邮件服务器端送到个人终端机器上,一般是PC机或 MAC。一旦邮件发送到 PC 机或MAC上,邮件服务器上的邮件将会被删除。但目前的POP3邮件服务器大都可以“只下载邮件,服务器端并不删除”,也就是改进的POP3协议。
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。通过SMTP协议所指定的服务器,就可以把E-mail寄到收信人的服务器上了,整个过程只要几分钟。SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中转发出的电子邮件。
它使用由TCP提供的可靠的数据传输服务把邮件消息从发信人的邮件服务器传送到收信人的邮件服务器。跟大多数应用层协议一样,SMTP也存在两个 端:在发信人的邮件服务器上执行的客户端和在收信人的邮件服务器上执行的服务器端。SMTP的客户端和服务器端同时运行在每个邮件服务器上。当一个邮件服 务器在向其他邮件服务器发送邮件消息时,它是作为SMTP客户在运行。
SMTP协议与人们用于面对面交互的礼仪之间有许多相似之处。首先,运行在发送端邮件服务器主机上的SMTP客户,发起建立一个到运行在接收端邮件服务 器主机上的SMTP服务器端口号25之间的TCP连接。如果接收邮件服务器当前不在工作,SMTP客户就等待一段时间后再尝试建立该连接。SMTP客户和服务器先执行一些应用层握手操作。就像人们在转手东西之前往往先自我介绍那样,SMTP客户和服务器也在传送信息之前先自我介绍一下。 在这个SMTP握手阶段,SMTP客户向服务器分别指出发信人和收信人的电子邮件地址。彼此自我介绍完毕之后,客户发出邮件消息。
5. 实验代码
该实验代码并不完整,需要完整的代码可以站内联系我。
/*****************************************************************************************************
POP 3 的实现的部分代码
Program File Name: popserver.cpp
Program Description: A Simple pop3 server
Author: Alan Zhuang
*****************************************************************************************************/
#include <stdio.h>
#include <time.h>
#include <winsock.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
/*****************************************************************************************************/
#define MAX_CONNECTION_NUM 50 //定义最大连接数
#define POP_SERVER_PORT 110 //定义服务器端口
#define BUFFER_SIZE 1024 //定义缓冲区大小
/*****************************************************************************************************/
u_short serverPort=POP_SERVER_PORT; //定义服务器端口为默认端口
char *readBuf; //定义接受数据缓冲区
WSAData wsaData; //定义WSAData结构
SOCKET clientSocket[MAX_CONNECTION_NUM]; //定义所有客户端socket
SOCKET serverSocket; //定义服务器监听socket
struct sockaddr_in clientSockAddr[MAX_CONNECTION_NUM]; //定义所有客户端信息
struct sockaddr_in server; //保存要设置的服务器信息
/*****************************************************************************************************/
struct UserInfo //定义用户信息结构
{
char username[20]; //用户名
char password[30]; //密码
};
struct ClientContent //客户端输入的信息
{
char command[10]; //保存命令字符串
char *param; //指向命令参数
};
struct CommandFinished //命令执行状态结构,共2条命令
{
char USER; //如果该命令已经成功执行的话
char PASS; //就为字符'Y',否则为0
};
/*****************************************************************************************************/
DWORD WINAPI PopBeginService(LPVOID lpParam); //对客户进行服务的函数,其中lpParam传入服务Socket序号
void WriteClientInfo(int index); //index为传入的客户Socket序号
void CommandHandle(int index); //对index位置的客户命令进行处理
void GetCommandAndContent(char *buf,ClientContent *clientContent); //分析buf指向的信息为命令和参数
int GetFileSize(char *fileName,struct UserInfo *userInfo); //得到文件fileName的大小
int GetMailFileTotalSize(UserInfo *userInfo); //得到某个用户邮件总大小
int GetMailFileNum(UserInfo *userInfo); //得到某个用户邮件数目
void SortMailFile(UserInfo *userInfo); //对删除后的邮件重新排序
void USER_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理USER命令
void PASS_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理PASS命令
void STAT_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理STAT命令
void UIDL_Handle(int index,CommandFinished *commandFinished,char *param); //处理UIDL命令
void DELE_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理DELE命令
void LIST_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理LIST命令
void RETR_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理RETR命令
void RSET_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理RSET命令
//void TOP_Handle(int index,CommandFinished *commandFinished,char *param); //处理TOP命令
void LOOP_Handle(int index,CommandFinished *commandFinished,char *param); //处理LOOP命令
void QUIT_Handle(int index,CommandFinished *commandFinished,char *param,UserInfo *userInfo); //处理QUIT命令
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: main
Used To: 在110端口监听客户连接并创建新线程为客户服务
*****************************************************************************************************/
int main(int argc,char *argv[])
{
/*****************************************************************************************************/
struct sockaddr_in tempSockAddr; //保存临时接受的客户信息
SOCKET tempSocket; //保存临时接受的SOCKET
for(int i=0;i<MAX_CONNECTION_NUM;i++)clientSocket[i]=NULL; //初始化所有客户端socket为无连接状态
server.sin_family=AF_INET; //填充服务器端信息
server.sin_port=htons(serverPort);
server.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
int sockAddrLen=sizeof(tempSockAddr);
/*****************************************************************************************************/
if(-1==WSAStartup(MAKEWORD(2,0),&wsaData))
{
printf("加载Winsock连接库失败!错误号为%d",WSAGetLastError());
WSACleanup();
return -1;
}
serverSocket=socket(AF_INET,SOCK_STREAM,0); //服务器建立socket并开始监听
if(INVALID_SOCKET ==serverSocket)
{
printf("服务器创建socket失败!错误号为%d",WSAGetLastError());
WSACleanup();
return -1;
}
if(SOCKET_ERROR==bind(serverSocket,(struct sockaddr*)&server,sizeof(server)))
{
printf("服务器套接字绑定发生错误!错误号为:%d",WSAGetLastError());
WSACleanup();
return -1;
}
if(SOCKET_ERROR==listen(serverSocket,MAX_CONNECTION_NUM+1))
{
printf("服务器套接字监听失败!错误号为:%d",WSAGetLastError());
WSACleanup();
return -1;
}
printf("Pop Server is running.....");
/*****************************************************************************************************/
while(true) //服务器进入无限监听状态
{
tempSocket=accept(serverSocket,(struct sockaddr*)&tempSockAddr,&sockAddrLen);
for(i=0;i<MAX_CONNECTION_NUM;i++) //查找队列中有没有空位
{
if(NULL!=clientSocket[i])continue; //该位置不为空,被占用了,继续看下一个
else //有空位就把临时客户信息保存
{
clientSocket[i]=tempSocket;
clientSockAddr[i]=tempSockAddr;
tempSocket=NULL;
memset((void *)(struct sockaddr*)&tempSockAddr,0,sizeof(tempSockAddr));
int index=i; //保存空位号
DWORD dwThreadID;
HANDLE handle; //下面创建一个服务线程,SmtpBeginService为线程入口
handle=::CreateThread(NULL,NULL,PopBeginService,&index,CREATE_SUSPENDED,&dwThreadID);
::ResumeThread(handle);
break;
}
}
if(NULL!=tempSocket) //如果没有找到空位,即队列满了则发送人数满了并关闭连接
{
send(tempSocket,"Sorry,the server queue is full,please wait",sizeof("Sorry,the server queue is full,please wait"),0);
closesocket(tempSocket);
tempSocket=NULL;
}
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: SmtpBeginService
Used To: 为客户服务的线程入口点
Params: lpParam--主线程传入的客户服务号
*****************************************************************************************************/
DWORD WINAPI PopBeginService(LPVOID lpParam)
{
int *index=(int *)lpParam; //首先取得客户服务号,以下发送欢迎信息
send(clientSocket[*index],"Welcome to LinLin POP3 Server...\r\n",sizeof("Welcome to LinLin POP3 Server...\r\n"),0);
WriteClientInfo(*index); //把第*index位置客户登录信息存入日志文件
CommandHandle(*index); //进入与客户命令响应函数
return 0;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: CommandHandle
Used To: 接受客户命令并处理命令
Params: index--客户服务号
*****************************************************************************************************/
void CommandHandle(int index)
{
/*****************************************************************************************************/
UserInfo userInfo; //保存用户名及密码
struct ClientContent clientContent; //保存接受的信息内容(包含命令和参数)
struct CommandFinished commandFinished; //保存命令完成状态
readBuf=(char *)malloc(BUFFER_SIZE+1); //分配接收数据的缓冲区,最后为\0结束符
memset(readBuf,0,BUFFER_SIZE+1); //清空接受数据的缓冲区
memset(&userInfo,0,sizeof(struct UserInfo)); //清空用户名和密码
memset(&commandFinished,0,sizeof(struct CommandFinished)); //初始化每个命令未执行
memset(&clientContent,0,sizeof(struct ClientContent)); //清空命令字和参数
/*****************************************************************************************************/
recv(clientSocket[index],readBuf,BUFFER_SIZE,0); //读取客户发送的命令信息
GetCommandAndContent(readBuf,&clientContent); //获得命令信息保存到clientContent中
/*****************************************************************************************************/
while(0!=strcmp("QUIT",clientContent.command)) //当客户发送的命令不为QUIT时
{
if(0==strcmp("USER",clientContent.command)) //处理USER命令
{
USER_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0接受下个命令
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("PASS",clientContent.command)) //处理PASS命令
{
PASS_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("STAT",clientContent.command)) //处理STAT命令
{
STAT_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("UIDL",clientContent.command)) //处理UIDL命令
{
UIDL_Handle(index,&commandFinished,clientContent.param);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("LIST",clientContent.command)) //处理LIST命令
{
LIST_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("RETR",clientContent.command)) //处理RETR命令
{
RETR_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("RSET",clientContent.command)) //处理RSET命令
{
RSET_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
/** else if(0==strcmp("TOP",clientContent.command)) //处理TOP命令
{
TOP_Handle(index,&commandFinished,clientContent.param);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}**/
else if(0==strcmp("LOOP",clientContent.command)) //处理LOOP命令
{
LOOP_Handle(index,&commandFinished,clientContent.param);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("DELE",clientContent.command)) //处理DELE命令
{
DELE_Handle(index,&commandFinished,clientContent.param,&userInfo);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else //处理不认识的命令;
{
send(clientSocket[index],"502 Command not implemented",sizeof("502 Command not implemented"),0);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
}
//以下处理QUIT命令
QUIT_Handle(index,&commandFinished,clientContent.param,&userInfo);
}
/****************************************************************************************************/
/*****************************************************************************************************
Function_Name: USER_HANDLE
Used TO: 处理USER命令(从用户文件中查找用户名是否存在,并保存用户名)
/*****************************************************************************************************/
void USER_Handle( int index, //客户服务号
CommandFinished *commandFinished, //命令执行状态
char *param, //指向命令参数
UserInfo *userInfo) //指向保存用户信息的内存
{
int fileLength; //定义文件长度
int usercount; //定义用户数量
UserInfo tempUserInfo; //保存用户名和密码
FILE *fp=NULL; //用户文件指针
memset(&tempUserInfo,0,sizeof(struct UserInfo)); //清空用户信息
while(' '==*param)param++; //去掉USER命令后面的空格
fp=fopen("user.dat","rb"); //大打开文件
if(NULL==fp) return ; //打开文件失败就返回
fseek(fp,0,2); //文件指针指向文件最后
fileLength=ftell(fp); //得到文件长度
usercount=fileLength/(sizeof(struct UserInfo)); //得到用户数量
for(int i=0;i<usercount;i++) //遍历每个用户记录
{
fseek(fp,i*sizeof(struct UserInfo),0); //将文件指针指向该记录的开始位
fread(&tempUserInfo,sizeof(struct UserInfo),1,fp);//将该记录读入结构userInfo中
if(strlen(tempUserInfo.username)!=strlen(param))continue;
if(0==strcmp(tempUserInfo.username,param))
{
memcpy(userInfo,&tempUserInfo,sizeof(struct UserInfo)); //保存用户名
commandFinished->USER='Y'; //修改USER命令为执行状态并发送成功信息
send(clientSocket[index],"+OK USER Command Finished",sizeof("+OK USER Command Finished"),0);
return ;
}
}
send(clientSocket[index],"-ERR User does not exit",sizeof("-ERR User does not exit"),0);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: PASS_HANDLE
Used TO: 处理PASS命令(得到客户的密码并检查密码是否正确)
/*****************************************************************************************************/
void PASS_Handle( int index, //客户服务号
CommandFinished *commandFinished, //命令执行状态
char *param, //指向命令参数
UserInfo *userInfo) //指向保存用户信息的内存
{
if('Y'!=commandFinished->USER) //USER没有
{
send(clientSocket[index],"-ERR Please enter user command first",sizeof("-ERR Please enter user command first"),0);
return ;
}
else
{
while(' '==*param) param++; //去掉密码前面的空格
if(0!=strcmp(userInfo->password,param))
{
send(clientSocket[index],"-ERR Password is not right",sizeof("-ERR Password is not right"),0);
return ;
}
commandFinished->PASS='Y'; //设置PASS命令成功执行
send(clientSocket[index],"+OK PASS Command Finished",sizeof("+OK PASS Command Finished"),0);
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: STAT_HANDLE
Used To: 处理客户STAT命令(统计用户邮箱信息并发送给用户)
*****************************************************************************************************/
void STAT_Handle( int index, //客户服务id号
CommandFinished *commandFinished, //命令完成状态结构
char *param, //命令参数
UserInfo *userInfo) //指向用户信息
{
int mailNum; //定义该用户邮件数
int totalSize; //定义邮件总大小
char mailNumString[4]; //保存邮件数的字符串
char totalSizeString[10]; //保存总大小的字符串
char sendData[100]; //将发送给客户的信息字符串
memset(mailNumString,0,4); //清空信息
memset(totalSizeString,0,10);
memset(sendData,0,100);
if('Y'!=commandFinished->USER || 'Y'!=commandFinished->PASS)
{
send(clientSocket[index],"-ERR USER and PASS Command should be finished before",sizeof("-ERR USER and PASS Command should be finished before"),0);
return ;
}
else
{
mailNum=GetMailFileNum(userInfo); //得到该用户邮件数
totalSize=GetMailFileTotalSize(userInfo); //得到邮件总大小
itoa(mailNum,mailNumString,10); //将邮件数目转换为字符串
itoa(totalSize,totalSizeString,10); //将邮件总大小转换为字符串
strcat(sendData,"+OK ");
strcat(sendData,mailNumString); //构造待发送的字符串
strcat(sendData," ");
strcat(sendData,totalSizeString);
strcat(sendData,"\r\n");
send(clientSocket[index],sendData,strlen(sendData),0); //发送总的信息数据
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: UIDL_Handle
Used To: 还没实现,待SMTP添加该功能后在说吧
*****************************************************************************************************/
void UIDL_Handle( int index,
CommandFinished *commandFinished,
char *param) //处理UIDL命令
{
return ;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: LIST_Handle
Used To: 处理List命令,无参数时给出所有邮件信息,带参数时给出某个邮件信息
*****************************************************************************************************/
void LIST_Handle( int index,
CommandFinished *commandFinished,
char *param,
UserInfo *userInfo) //处理LIST命令
{
int mailNum; //定义该用户的邮件数
int totalSize; //定义总邮件大小
int fileSize; // 保存某个邮件的大小
char mailNumString[4]; //保存邮件数的字符串
char totalSizeString[10]; //保存总大小的字符串
char fileSizeStr[10]; //保存文件大小的字符串
char path[50]; //待打开的文件路径
char sendData[1024]; //将发送给客户的总信息字符串
FILE *fp=NULL; //定义文件指针
memset(mailNumString,0x00,sizeof(mailNumString));
memset(totalSizeString,0x00,sizeof(totalSizeString));
memset(fileSizeStr,0x00,sizeof(fileSizeStr));
memset(path,0,50); //路径清空
memset(sendData,0,1024); //清空总信息
if('Y'!=commandFinished->USER || 'Y'!=commandFinished->PASS)
{
send(clientSocket[index],"-ERR USER and PASS Command should be finished before",sizeof("-ERR USER and PASS Command should be finished before"),0);
return ;
}
else
{
mailNum=GetMailFileNum(userInfo); //得到用户邮件数
totalSize=GetMailFileTotalSize(userInfo); //得到邮件总大小为
param++; //param指向命令参数
if(0!=*param) //如果List命令带参数
{
for(int i=0;i<strlen(param);i++) //判断param参数是否正确
{
if(!isdigit(*(param+i)))
{
send(clientSocket[index],"-ERR LIST command params error",sizeof("-ERR LIST command params error"),0);
return ;
}
}
strcat(path,userInfo->username); //构造路径字符串
strcat(path,"\\RecieveBox\\");
strcat(path,param);
strcat(path,".mil");
fp=fopen(path,"rb");
fseek(fp,0,2);
fileSize=ftell(fp);
fclose(fp);
itoa(fileSize,fileSizeStr,10);
strcat(sendData,"+OK "); //构造待发送的字符串
strcat(sendData,param);
strcat(sendData," ");
strcat(sendData,fileSizeStr);
send(clientSocket[index],sendData,strlen(sendData),0);
}
else //LIST命令不带参数时处理
{
itoa(mailNum,mailNumString,10); //将邮件数目转换为字符串
itoa(totalSize,totalSizeString,10); //将邮件总大小转换为字符串
strcat(sendData,"+OK ");
strcat(sendData,mailNumString); //构造待发送的字符串
strcat(sendData," ");
strcat(sendData,totalSizeString);
strcat(sendData,"\r\n");
char numStr[4];
memset(numStr,0,4);
for(int i=1;i<=mailNum;i++) //发送每个邮件的信息
{
strcat(path,userInfo->username); //构造路径字符串
strcat(path,"\\RecieveBox\\");
itoa(i,numStr,10);
strcat(path,numStr);
strcat(path,".mil");
fp=fopen(path,"rb");
fseek(fp,0,2);
fileSize=ftell(fp);
fclose(fp);
itoa(fileSize,fileSizeStr,10);
strcat(sendData,numStr); //构造待发送的字符串
strcat(sendData," ");
strcat(sendData,fileSizeStr);
strcat(sendData,"\r\n");
memset(path,0x00,sizeof(path));
memset(numStr,0x00,sizeof(numStr));
memset(fileSizeStr,0x00,sizeof(fileSizeStr));
}
strcat(sendData,".\r\n");
send(clientSocket[index],sendData,strlen(sendData),0);
}
}
}
/****************************************************************************************************/
/*****************************************************************************************************
Function_Name: RETR_Handle
Used To: 处理RETR命令
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void RETR_Handle( int index,
CommandFinished *commandFinished,
char *param,
UserInfo *userInfo) //处理RETR命令
{
FILE *fp=NULL; //待打开的文件指针
char path[50]; //待打开的文件路径
int fileSize; //保存邮件大小
char fileSizeStr[10]; //保存邮件大小的字符;
char *sendData; //待发送的数据指针
int sendDataLength=0; //待发送的数据长度
memset(path,0,50);
memset(fileSizeStr,0,10);
if('Y'!=commandFinished->USER || 'Y'!=commandFinished->PASS)
{
send(clientSocket[index],"-ERR USER and PASS Command should be finished before",sizeof("-ERR USER and PASS Command should be finished before"),0);
return ;
}
else
{
param++; //param指向命令参数
if(0==*param)
{
send(clientSocket[index],"-ERR RETR Command needed param",sizeof("-ERR RETR Command needed param"),0);
return ;
}
for(int i=0;i<strlen(param);i++) //判断param参数是否正确
{
if(!isdigit(*(param+i)))
{
send(clientSocket[index],"-ERR RETR command params error",sizeof("-ERR RETR command params error"),0);
return ;
}
}
strcat(path,param);
strcat(path,".mil");
fileSize=GetFileSize(path,userInfo);
itoa(fileSize,fileSizeStr,10);
sendDataLength+=strlen(param); //计算发送数据长度
sendDataLength+=strlen(fileSizeStr);
sendDataLength+=7;
sendDataLength+=fileSize;
sendData=(char *)malloc(sizeof(char)*sendDataLength+1);//分配发送数据的缓冲区
memset(sendData,0,sendDataLength+1); //清空发送数据缓冲区
strcat(sendData,"+OK ");
strcat(sendData,param);
strcat(sendData," ");
strcat(sendData,fileSizeStr);
strcat(sendData,"\r\n");
memset(path,0x00,sizeof(path));
strcat(path,userInfo->username);
strcat(path,"\\RecieveBox\\");
strcat(path,param);
strcat(path,".mil");
fp=fopen(path,"rb");
fread(sendData+strlen(param)+strlen(fileSizeStr)+7,1,fileSize,fp);
fclose(fp);
send(clientSocket[index],sendData,strlen(sendData),0);
}
}
/****************************************************************************************************/
/*****************************************************************************************************
Function_Name: DELE_Handle
Used To: 标记某个邮件为删除标记记录到文件dele.dat中以备quit命令执行时更新
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void DELE_Handle( int index,
CommandFinished *commandFinished,
char *param,
UserInfo *userInfo) //处理DELE命令
{
FILE *fpDele; //保存已删除标记邮件信息的文件指针
char delePath[50]; //上述文件的路径(用户名\dele.dat)
memset(delePath,0,50);
if('Y'!=commandFinished->USER || 'Y'!=commandFinished->PASS)
{
send(clientSocket[index],"-ERR USER and PASS Command should be finished before",sizeof("-ERR USER and PASS Command should be finished before"),0);
return ;
}
else
{
param++; //param指向命令参数
if(0==*param)
{
send(clientSocket[index],"-ERR DELE command need param",sizeof("-ERR DELE command need param"),0);
return ;
}
for(int i=0;i<strlen(param);i++) //判断param参数是否正确
{
if(!isdigit(*(param+i)))
{
send(clientSocket[index],"-ERR DELE command params error",sizeof("-ERR DELE command params error"),0);
return ;
}
}
strcat(delePath,userInfo->username); //构造dele.dat的路径
strcat(delePath,"\\dele.dat");
fpDele=fopen(delePath,"a"); //打开文件写邮件删除标记
fputs(param,fpDele);
fputs(",",fpDele);
fclose(fpDele);
send(clientSocket[index],"+OK Delete Command Finished",sizeof("+OK Delete Command Finished"),0);
}
}
/****************************************************************************************************/
/****************************************************************************************************
Function_Name: RSET_Handle
Used To: 处理RSET命令
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void RSET_Handle( int index,
CommandFinished *commandFinished,
char *param,
UserInfo *userInfo) //处理RSET命令
{
char delePath[50]; //用户名\dele.dat 文件路径
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
memset(delePath,0,50);
strcat(delePath,userInfo->username);
strcat(delePath,"\\dele.dat");
hFind=FindFirstFile(delePath,&FindFileData);
if(INVALID_HANDLE_VALUE!=hFind) //假如找到该文件,说明该文件存在,就删除
remove(delePath); //删除该文件
FindClose(hFind);
}
/****************************************************************************************************/
/****************************************************************************************************
Function_Name: LOOP_Handle
Used To: 仅仅返回肯定响应,不做任何操作
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void LOOP_Handle( int index,
CommandFinished *commandFinished,
char *param) //处理LOOP命令
{
send(clientSocket[index],"+OK Connection is OK!",sizeof("+OK Connection is OK!"),0);
return ;
}
/****************************************************************************************************/
/****************************************************************************************************
Function_Name: QUIT_Handle
Used To: 处理QUIT命令(执行删除标记为dele的邮件并关闭客户端连接)
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void QUIT_Handle( int index,
CommandFinished *commandFinished,
char *param,
UserInfo *userInfo) //处理QUIT命令
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
FILE *fpDele=NULL; //指向username\dele.dat的文件指针
int deleFileLength; //保存dele.dat的长度
char *deleFileData; //保存dele.dat的内容
char delePath[50]; //用户名\dele.dat 文件路径
char deleFilePath[50]; //保存欲删除文件的路径
memset(delePath,0,50);
memset(deleFilePath,0,50);
strcat(delePath,userInfo->username);
strcat(delePath,"\\dele.dat");
strcat(deleFilePath,userInfo->username);
strcat(deleFilePath,"\\RecieveBox\\");
//如果dele.dat存在就删除里面标记的邮件并删除username\dele.dat并重新调整邮件的名字
hFind=FindFirstFile(delePath,&FindFileData);
if(INVALID_HANDLE_VALUE!=hFind) //假如找到该文件,说明该文件存在,就必须删除某些邮件
{
fpDele=fopen(delePath,"rb");
fseek(fpDele,0,2);
deleFileLength=ftell(fpDele);
fseek(fpDele,0,0);
deleFileData=(char *)malloc(sizeof(char)*deleFileLength+1);
memset(deleFileData,0,sizeof(char)*deleFileLength+1);
fread(deleFileData,1,deleFileLength,fpDele); //读入dele.dat内容
fclose(fpDele);
::DeleteFile(delePath);
for(int i=0;i<strlen(deleFileData);i++)
{
if(','!=deleFileData[i]) //当前字符不为','
{
deleFilePath[strlen(deleFilePath)]=deleFileData[i];//复制欲删除的文件路径
}
else
{
strcat(deleFilePath,".mil"); //补全文件路径扩展名
DeleteFile(deleFilePath); //删除该文件
memset(deleFilePath,0,50); //清空以保存下个文件路径
strcat(deleFilePath,userInfo->username);
strcat(deleFilePath,"\\RecieveBox\\");
}
}
}
FindClose(hFind);
SortMailFile(userInfo);
send(clientSocket[index],"Good Bye!",sizeof("Good Bye"),0);
closesocket(clientSocket[index]);
clientSocket[index]=NULL;
}
/****************************************************************************************************/
void SortMailFile(UserInfo *userInfo) //对邮件重新按顺序命名
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
int mailNum=1;
char mailNumStr[4];
char newNamePath[50]; //保存新的临时文件名
char oldNamePath[50];
char filter[50]; //重命令.mil邮件的过滤字符串
memset(newNamePath,0,50);
memset(mailNumStr,0,4);
memset(oldNamePath,0,50);
memset(filter,0x00,sizeof(filter));
strcat(filter,userInfo->username);
strcat(filter,"\\RecieveBox\\*.mil");
itoa(mailNum,mailNumStr,10);
strcat(oldNamePath,userInfo->username);
strcat(oldNamePath,"\\RecieveBox\\");
strcat(newNamePath,userInfo->username);
strcat(newNamePath,"\\RecieveBox\\");
strcat(newNamePath,mailNumStr);
strcat(newNamePath,".tmp");
hFind=FindFirstFile(filter,&FindFileData); //重新命名每个邮件的名称
if(INVALID_HANDLE_VALUE!=hFind)
{
strcat(oldNamePath,FindFileData.cFileName);
::MoveFile(oldNamePath,newNamePath);
while(FindNextFile(hFind,&FindFileData))
{
mailNum++;
memset(mailNumStr,0,4);
memset(newNamePath,0,50);
memset(oldNamePath,0,50);
itoa(mailNum,mailNumStr,10);
strcat(oldNamePath,userInfo->username);
strcat(oldNamePath,"\\RecieveBox\\");
strcat(oldNamePath,FindFileData.cFileName);
strcat(newNamePath,userInfo->username);
strcat(newNamePath,"\\RecieveBox\\");
strcat(newNamePath,mailNumStr);
strcat(newNamePath,".tmp");
::MoveFile(oldNamePath,newNamePath);
}
}
FindClose(hFind);
memset(filter,0x00,sizeof(filter));
memset(oldNamePath,0x00,sizeof(oldNamePath));
memset(newNamePath,0x00,sizeof(newNamePath));
mailNum=1;
itoa(mailNum,mailNumStr,10);
strcat(filter,userInfo->username);
strcat(filter,"\\RecieveBox\\*.tmp");
strcat(oldNamePath,userInfo->username);
strcat(oldNamePath,"\\RecieveBox\\");
strcat(newNamePath,userInfo->username);
strcat(newNamePath,"\\RecieveBox\\");
strcat(newNamePath,mailNumStr);
strcat(newNamePath,".mil");
hFind=FindFirstFile(filter,&FindFileData); //重新命名每个邮件的名称
if(INVALID_HANDLE_VALUE!=hFind)
{
strcat(oldNamePath,FindFileData.cFileName);
::MoveFile(oldNamePath,newNamePath);
while(FindNextFile(hFind,&FindFileData))
{
mailNum++;
memset(mailNumStr,0,4);
memset(newNamePath,0,50);
memset(oldNamePath,0,50);
itoa(mailNum,mailNumStr,10);
strcat(oldNamePath,userInfo->username);
strcat(oldNamePath,"\\RecieveBox\\");
strcat(oldNamePath,FindFileData.cFileName);
strcat(newNamePath,userInfo->username);
strcat(newNamePath,"\\RecieveBox\\");
strcat(newNamePath,mailNumStr);
strcat(newNamePath,".mil");
::MoveFile(oldNamePath,newNamePath);
}
}
FindClose(hFind);
}
/****************************************************************************************************
Function_Name: WriteClientIpInfo
Used To: 记录客户登录的ip信息和登录时间信息等
Param: index-客户服务号,用于访问客户socket和客户机信息
*****************************************************************************************************/
void WriteClientInfo(int index)
{
char *clientAddr;
char *timestr=(char *)malloc(sizeof(char)*40);
FILE *fp;
time_t ttime;
time(&ttime);//返回当前日历
tm *tmtime=localtime(&ttime); //将日历转换为当地时间
strftime(timestr,50,"%Y年%m月%d日%H时%M分%S秒",tmtime);
clientAddr=inet_ntoa(clientSockAddr[index].sin_addr);//inet_ntoa将网络地址转换成.间隔的字符串
// char clf[3]={10,13,0};//定义回车换行
//strcat(clientAddr,clf);
fp=fopen("ServerLog.txt","a");
fputs(clientAddr,fp);
fputs(" ",fp);
fputs(timestr,fp);
fputs("\r\n",fp);
fclose(fp);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: GetCommandAndContent
Used To: 分析从客户接收的buf数据,分离出命令和参数并保存到clientContent中
Params: buf--传入从客户接收到数据的开始位置 clientContent--分析后保存命令和参数的结构
*****************************************************************************************************/
void GetCommandAndContent( char *buf,
ClientContent *clientContent)
{
if(0==strncmp(buf,"USER",4))
{
strcpy(clientContent->command,"USER");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"PASS",4)) //如果buf接收数据的前4个字符为PASS
{
strcpy(clientContent->command,"PASS"); //保存命令为PASS
clientContent->param=buf+4; //命令参数指向buf命令后的位置
}
else if(0==strncmp(buf,"LIST",4))
{
strcpy(clientContent->command,"LIST");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"STAT",4))
{
strcpy(clientContent->command,"STAT");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"UIDL",4))
{
strcpy(clientContent->command,"UIDL");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"RETR",4))
{
strcpy(clientContent->command,"RETR");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"DELE",4))
{
strcpy(clientContent->command,"DELE");
clientContent->param=buf+4;
}
else if(0==strncmp(buf,"RSET",4))
{
strcpy(clientContent->command,"RSET");
clientContent->param=NULL;
}
else if(0==strncmp(buf,"NOOP",4))
{
strcpy(clientContent->command,"LOOP");
clientContent->param=NULL;
}
else if(0==strncmp(buf,"QUIT",4))
{
strcpy(clientContent->command,"QUIT");
clientContent->param=NULL;
}
else
{
strcpy(clientContent->command,"UNKNOWN");
clientContent->param=NULL;
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: GetFileSize
Used To: 根据参数fileName得到文件userInfo->username\RecieveBox\fileName的大小
*****************************************************************************************************/
int GetFileSize(char *fileName,struct UserInfo *userInfo)
{
FILE *fp=NULL;
int fileSize=0;
char filePath[50];
memset(filePath,0,50);
strcat(filePath,userInfo->username);
strcat(filePath,"\\RecieveBox\\");
strcat(filePath,fileName);
fp=fopen(filePath,"rb");
if(NULL==fp)return 0;
fseek(fp,0,2); //将文件指针指向最后
fileSize=ftell(fp);
fclose(fp);
return fileSize;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: GetMailFileNum
Used To: 根据参数userInfo返回userInfo->username\RecieveBox下邮件的数目
*****************************************************************************************************/
int GetMailFileNum(UserInfo *userInfo)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
int mailNum=0;
char *filter; //文件过滤条件
filter=(char *)malloc(sizeof(char)*40); //构造过滤条件
memset(filter,0x00,40);
strcat(filter,userInfo->username);
memcpy(filter+strlen(userInfo->username),"\\RecieveBox\\*.mil",17);
hFind=FindFirstFile(filter,&FindFileData);
if(INVALID_HANDLE_VALUE!=hFind)
{
mailNum++;
while(FindNextFile(hFind,&FindFileData))mailNum++;
}
FindClose(hFind);
free(filter);
return mailNum;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: GetMailFileTotalSize
Used To: 根据参数userInfo返回userInfo->username\RecieveBox下邮件总大小
*****************************************************************************************************/
int GetMailFileTotalSize(UserInfo *userInfo)
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
char *filter; //文件过滤条件
int totalMailSize=0; //初始化总的邮件大小为0
filter=(char *)malloc(sizeof(char)*40); //构造过滤条件
memset(filter,0x00,40);
strcat(filter,userInfo->username);
memcpy(filter+strlen(userInfo->username),"\\RecieveBox\\*.mil",17);
hFind=FindFirstFile(filter,&FindFileData);
if(INVALID_HANDLE_VALUE!=hFind)
{
totalMailSize+=GetFileSize(FindFileData.cFileName,userInfo);
while(FindNextFile(hFind,&FindFileData))
totalMailSize+=GetFileSize(FindFileData.cFileName,userInfo);
}
FindClose(hFind);
free(filter);
return totalMailSize;
}
6. 实验结果