资源描述
武汉大学计算机学院
课程实验(设计)报告
课程名称: 计算机网络应用设计
专业、班:
姓 名:
学 号:
学 期: 2010年上学期
第二部分 软件设计
一、实验内容
完成FTP客户端、POP3客户端两个系统程序,以及一个应用程序。
客户端操作系统为Windows XP。
FTP程序要求具有图形化界面、上传、下载功能,鼓励实现断点续传功能,POP3程序具有邮件接收、阅读、删除等基本功能。FTP、POP3程序使用socket方式编程,从创建socket、建立TCP连接开始,实现FTP、SMTP、POP3协议的功能,不得调用第三方控件(可使用操作系统自身的API函数)。编程工具可以使用C#或VC++。
应用程序要求实现一个网上书店的基本功能,要求具有前端(用户)和后端(管理)功能,基于Web运行方式。前端具有浏览书目、购物车等功能,后端具有管理书目、基本统计功能。编程工具可以是MS .NET(C#)或者J2EE 二选一,数据库可以是SQL Server、DB2、Oracle、Mysql、PostgreSQL之一。
二、实验步骤与实验结果
1. FTP源程序
说明:本程序使用VC++编写,开发环境为VS2008
/******************************************************************************
// 连接FTP 服务器
******************************************************************************/
void Cmfcftp3Dlg::OnBnClickedButton1()
{
m_pInetSession=new CInternetSession(AfxGetAppName(),1,PRE_CONFIG_INTERNET_ACCESS);
UpdateData(TRUE);
try
{
//根据给出的参数连接到FTP服务器
m_pFtpConnection=m_pInetSession->GetFtpConnection(m_host,m_username,m_password,m_port);
if (m_pFtpConnection != NULL)
{
CString m_i;
m_i="连接成功\r\n";
m_ftpinfo +=m_i;
UpdateData(FALSE);
List();
}
}
catch (CInternetException * pEx)
{
CString m_i;
m_i="连接no成功\r\n";
m_ftpinfo +=m_i;
UpdateData(FALSE);
TCHAR szError[1024];
if ( pEx->GetErrorMessage(szError,1024))
{
m_i=(CString) szError;
m_ftpinfo +=m_i;
UpdateData(FALSE);
}
else
AfxMessageBox("There was an exception");
pEx->Delete();
m_pFtpConnection=NULL;
}
}
/*********************************************************************
// 列出FTP服务器上的所有文件
****************************************************************************/
void Cmfcftp3Dlg::List()
{
CString m_i;
CString m_ii;
CStringArray m_Dir;
CFtpFileFind finder(m_pFtpConnection);
BOOL bWorking=finder.FindFile(_T("*"));
while (bWorking)
{
bWorking = finder.FindNextFile();
if ( finder.IsDots() )
continue;
if (finder.IsDirectory()) // 如果是目录。
{
m_Dir.Add( finder.GetFileName());
}
else
{
m_i=finder.GetFileName()+"\r\n";
for (int j=0;j<n;j++)
{
m_ii="\t";
m_ftpinfo=m_ftpinfo+m_ii;
}
m_ftpinfo +=m_i;
UpdateData(FALSE);
}
}
finder.Close();
for(int i=0;i<m_Dir.GetSize();i++)
{
n++;
m_i="["+m_Dir.GetAt(i)+"]"+"\r\n";
for (int j=1;j<n;j++)
{
m_ii="\t";
m_ftpinfo=m_ftpinfo+m_ii;
}
m_ftpinfo +=m_i;
UpdateData(FALSE);
BOOL m_suc=0;
while (!m_suc)
{
m_suc=m_pFtpConnection->SetCurrentDirectory(m_Dir.GetAt(i));
}
List();
BOOL m_suc1=0;
while(!m_suc1)
{
m_suc1= m_pFtpConnection->SetCurrentDirectory("..");
}
n--;
}
}
2. POP3源程序
说明:本程序是使用VC++编写,开发环境为VS2008,这里只贴了与实现相关的代码,界面部分的代码不在此描述了。
MyPop3App.cpp 文件:
BOOL CMyPop3App::InitInstance()
{
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
WORD wVersionRequested;
wVersionRequested = MAKEWORD(1, 1);
int err = WSAStartup(wVersionRequested, &wsaData); //The Windows Sockets WSAStartup function initiates use of Ws2_32.dll by a process
if (err != 0)
return FALSE;
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
return FALSE;
CMyPop3Dlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
// “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
// “取消”来关闭对话框的代码
}
// 由于对话框已关闭,所以将返回FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
return FALSE;
}
int CMyPop3App::ExitInstance()
{
// TODO: 在此添加专用代码和/或调用基类
WSACleanup();
return CWinApp::ExitInstance();
}
/**********************************************************************
// 自定义的通过套接字发送数据函数
*************************************************************************/
int CMyPop3Dlg::Send(SOCKET& sock, TCHAR const * buf, int len, int flag)
{
int bytes(0), count(0);
while ( count < len ) // 循环执行send 函数,直至数据发送完毕为止。
{
bytes = send(sock, buf + count, len - count, flag);
if ( bytes == -1 || bytes == 0 )
return -1;
count += bytes;
}
return count;
}
/******************************************************************************
// 自定义的接收POP3 发送的数据的函数
******************************************************************************/
int CMyPop3Dlg::Pop3Recv(SOCKET& m_sock, TCHAR* 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 ) /* error occur */
return -1;
offset += rs;
buf[offset] = '\0';
} while ( strstr(buf, "\r\n.\r\n") == (char*)NULL );
return offset;
}
/******************************************************************************
// 从服务器对LIST 命令的返回信息中,获取邮件总数
******************************************************************************/
int CMyPop3Dlg::getMailSum(TCHAR* responseToLIST)
{
int sum = 0;
TCHAR* p = strstr(responseToLIST, "\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;
}
/******************************************************************************
// 从服务器返回的邮件内容中抽取邮件标题
***********************************************************************************/
bool CMyPop3Dlg::GetSubject(TCHAR* subject, TCHAR const * buf)
{
TCHAR const * p = strstr(buf, "Subject: ");
if ( p == NULL )
return false;
p = p + 9;
for (int i = 0; i < 32; i++)
{
if ( p[i] == '\r' || p[i] == '\n' )
{
subject[i] = '\0';
break;
}
subject[i] = p[i];
}
return true;
}
/******************************************************************************
// 登录失败时的善后处理(关闭套接字、禁用注销按钮、启用登录按钮)
******************************************************************************/
int CMyPop3Dlg::loginFail()
{
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);
closesocket(m_sock);
return 0;
}
/******************************************************************************
// 点击登录按钮
******************************************************************************/
void CMyPop3Dlg::OnLogin()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
if (m_serverAddr.IsEmpty())
{
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("请填写Pop3 服务器地址"));
return;
}
else if (m_portNo.IsEmpty())
{
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("请填写端口号"));
return;
}
else if (m_userName.IsEmpty())
{
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("请填写您的用户名"));
return;
}
else if (m_passwd.IsEmpty())
{
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("请填写您的邮箱密码"));
return;
}
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
AfxBeginThread(connToServerThread, this);
return;
}
/******************************************************************************
// 点击注销按钮
******************************************************************************/
void CMyPop3Dlg::OnLogoff()
{
// TODO: 在此添加控件通知处理程序代码
TCHAR sendbuf[10], recvbuf[128];
memset(recvbuf, 0, sizeof(recvbuf));
strcpy(sendbuf, "QUIT \r\n");
// 发送QUIT 消息,结束本次会话。
send(m_sock, sendbuf, strlen(sendbuf), 0);
int rt = recv(m_sock, recvbuf, sizeof(recvbuf), 0);
// 显示服务器返回的信息。
m_CmdInfo += "\r\nResponse to Quit: ";
m_CmdInfo += recvbuf;
SetDlgItemText(IDC_EDIT6, m_CmdInfo);
if (rt <= 0 || strncmp(recvbuf, "+OK", 3) !=0)
{
SetDlgItemText(IDC_STATIC_ERRORINFO, "注销失败!");
return;
}
loginFail();
SetDlgItemText(IDC_STATIC_ERRORINFO, "注销成功!");
}
/******************************************************************************
// 点击收取邮件按钮
******************************************************************************/
void CMyPop3Dlg::OnReceiveLetters()
{
// TODO: 在此添加控件通知处理程序代码
AfxBeginThread(recvLetterThread, this);
return;
}
/******************************************************************************
// 连接服务器线程
******************************************************************************/
UINT CMyPop3Dlg::connToServerThread(LPVOID lpVoid)
{
CMyPop3Dlg* pthis = (CMyPop3Dlg *)lpVoid;
pthis->m_sock = socket(AF_INET, SOCK_STREAM, 0); // 创建流式套接字。
// 获取pop3 服务器的IP地址。
struct hostent * p;
if ((p = gethostbyname(pthis->m_serverAddr)) ==NULL)
{
pthis->loginFail();
return 1;
}
TCHAR serverIP[100];
sprintf(serverIP, "%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]);
// 创建一个SOCKADDR_IN
SOCKADDR_IN sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(atoi((LPCSTR)(LPCTSTR)pthis->m_portNo));
sockaddr.sin_addr.S_un.S_addr = inet_addr(serverIP);
// 连接到服务器
if (0 != connect(pthis->m_sock, (SOCKADDR*)&sockaddr, sizeof(SOCKADDR)))
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("连接服务器失败。输入是否有误?"));
pthis->loginFail();
return 1;
}
// 检查服务器返回的信息。
TCHAR buf[128];
memset(buf, 0, sizeof(buf));
int returnval(0);
returnval = recv(pthis->m_sock, buf, sizeof(buf), 0);
if (returnval<=0 || strncmp(buf, "+OK", 3) != 0)
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("连接服务器失败。"));
pthis->loginFail();
return 1;
}
// 保存服务器传回来的信息。
pthis->m_CmdInfo += "\r\nResponse to Conn: ";
pthis->m_CmdInfo += buf;
pthis->SendMessage(WM_CONNSERVER_SUCX, 0, 0);
}
/****************************************************************************
// 连接服务器成功的消息响应函数
******************************************************************************/
LRESULT CMyPop3Dlg::login(WPARAM wParam, LPARAM lParam)
{
// 显示服务器传回来的信息
SetDlgItemText(IDC_EDIT6, m_CmdInfo);
// 更新界面
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("连接服务器成功!"));
AfxBeginThread(loginThread, this); // 开启登录线程
return 0;
}
/******************************************************************************
// 登录线程
******************************************************************************/
UINT CMyPop3Dlg::loginThread(LPVOID lpVoid)
{
CMyPop3Dlg* pthis = (CMyPop3Dlg*)lpVoid;
TCHAR sendbuf[128], recvbuf[128];
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
int rs(0); // send 和recv 的返回值。
// 发送用户名
sprintf(sendbuf, "USER %s\r\n", pthis->m_userName);
rs = Send(pthis->m_sock, sendbuf, strlen(sendbuf), 0);
if (-1 == rs)
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("发送用户名失败"));
pthis->loginFail();
return 1;
}
rs = recv(pthis->m_sock, recvbuf, sizeof(recvbuf), 0);
// 保存并显示服务器返回结果。
pthis->m_CmdInfo += _T("\r\nResponse to UserName: ");
pthis->m_CmdInfo += recvbuf;
pthis->SetDlgItemText(IDC_EDIT6, pthis->m_CmdInfo);
if (rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0)
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("用户名不正确"));
pthis->loginFail();
return 1;
}
// 发送密码
sprintf(sendbuf, "PASS %s\r\n", pthis->m_passwd);
rs = Send(pthis->m_sock, sendbuf, strlen(sendbuf), 0);
if (-1 == rs)
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("发送密码失败"));
pthis->loginFail();
return 1;
}
rs = recv(pthis->m_sock, recvbuf, sizeof(recvbuf), 0);
// 保存并显示服务器返回结果。
pthis->m_CmdInfo += _T("\r\nResponse to Passwd: ");
pthis->m_CmdInfo += recvbuf;
pthis->SetDlgItemText(IDC_EDIT6, pthis->m_CmdInfo);
if (rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0)
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("密码不正确"));
pthis->loginFail();
return 1;
}
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("登录成功!"));
return 0;
}
/******************************************************************************
// 收取邮件线程
******************************************************************************/
UINT CMyPop3Dlg::recvLetterThread(LPVOID lpVoid)
{
CMyPop3Dlg* pthis = (CMyPop3Dlg*)lpVoid;
TCHAR sendbuf[128];
TCHAR recvbuf[256];
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
sprintf(sendbuf, "LIST \r\n");
send(pthis->m_sock, sendbuf, strlen(sendbuf), 0);
// 发送LIST消息,以便获取邮件总数。
int rs = Pop3Recv(pthis->m_sock, recvbuf, sizeof(recvbuf), 0);
pthis->m_CmdInfo += "\r\nResponse to List: ";
pthis->m_CmdInfo += recvbuf;
pthis->SetDlgItemText(IDC_EDIT6, pthis->m_CmdInfo);
if ( rs <= 0 || strncmp(recvbuf, "+OK", 3) != 0 )
{
pthis->SetDlgItemText(IDC_STATIC_ERRORINFO, _T("获取邮件信息失败"));
return 1;
}
recvbuf[rs] = '\0';
int sum = pthis->getMailSum(recvbuf);
pthis->showLetters(sum);
return 0;
}
/******************************************************************************
// 收取邮件并显示
******************************************************************************/
int CMyPop3Dlg::showLetters(int sum)
{
if (0 >= sum)
{
m_letterInfo = _T("您的邮箱当前没有新邮件");
SetDlgItemText(IDC_EDIT5, m_letterInfo);
return 0;
}
else
{
for (int i(1); i<=sum; ++i) // 注意,邮件编号从开始,不是从开始。
fetchMail(i);
}
return 0;
}
/*****************************************************************************
// 收取第sum 封邮件
******************************************************************************/
int CMyPop3Dlg::fetchMail(int sum)
{
int rs;
FILE* fp;
unsigned int len;
TCHAR filename[32];
TCHAR sendbuf[128], recvbuf[10240];
memset(filename, 0, sizeof(filename));
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
bool create(true);
// 发送RETR 命令,获取某封邮件的内容。
sprintf(sendbuf, "RETR %d\r\n", sum);
send(m_sock, sendbuf, strlen(sendbuf), 0);
do //邮件可能非常大,以至于缓冲区放不下,这是就需要循环多次的recv
{
// 清空接收缓冲区。
memset(recvbuf, 0, sizeof(recvbuf));
//接收并显示邮件内容。
rs = Pop3Recv(m_sock, recvbuf, sizeof(recvbuf)-1, 0); //长度必须减一,最后一个字符必须是‘\0'。
m_letterInfo += "\r\n Response to RETR: ";
m_letterInfo += recvbuf;
SetDlgItemText(IDC_EDIT5, m_letterInfo);
if ( rs < 0 )
{
SetDlgItemText(IDC_STATIC_ERRORINFO, _T("收取邮件出现错误"));
return 1;
}
if (create) //循环第一次执行时获取邮件标题,创建文件并打开。直到循环结束才关闭文件。
{
create = false; // 循环下次执行到这里时跳过。
//获取邮件标题,以此标题作为eml 文件的文件名。
memset(filename, 0, sizeof(filename));
GetSubject(filename, recvbuf);
strcat(filename, ".eml");
if ( (fp = fopen(filename, "wb")) == NULL )
return 2;
}
len = strlen(recvbuf)-1;
if ( fwrite(recvbuf, 1, len, fp) != len )
{
fclose(fp);
return 3;
}
fflush(fp);
} while ( strst
展开阅读全文