掌握socket基于事件机制的网络程序设计,掌握多线程技术的FTP Server端设计方法,掌握FTP标准基本协议及其程序的实现,掌握文件内容的网络传输设计方法。
利用CFtpServer类接收和解析客户端命令,编写FTP客户端程序,服务器端使用多线程,实现多用户同时登录管理;
利用CFtpConnection和CInternetSession类,编写FTP客户端,实现简单文件操作功能。
1.服务器窗口界面设计
服务器主界面
2.功能实现:
(1)初始化FTP并启动监听
BOOL CFTPServer::Start()
{
????if (m_bRunning)
????????return FALSE;
?
????// create dummy window for message routing
if (!CWnd::CreateEx(0, AfxRegisterWndClass(0), "FTP Server Notification Sink", WS_POPUP, 0,0,0,0, NULL, 0))
????{
????????AddTraceLine(0, "Failed to create notification window.");
????????return FALSE;
????}
????// created the listen socket
????if (m_ListenSocket.Create(m_nPort))
????{
????????// start listening
????????if (m_ListenSocket.Listen())
????????{
????????????m_ListenSocket.m_pWndServer = this;
????????????m_bRunning = TRUE;????
????????????
????????????SetTimer(1, m_nStatisticsInterval, NULL);
?
????????????AddTraceLine(0, "FTP Server started on port %d.", m_nPort);
????????????return TRUE;
????????}
????}
????AddTraceLine(0, "FTP Server failed to listen on port %d.", m_nPort);
?
????// destroy notification window
????if (IsWindow(m_hWnd))
????????DestroyWindow();
????m_hWnd = NULL;
?
????return FALSE;
}
?
(2)停止FTP
通知各线程停止监听,关闭进程,释放连接,实现代码如下
void CFTPServer::Stop()
{
????if (!m_bRunning)
????????return;
?
????// stop statistics timer
????KillTimer(1);
?
????m_bRunning = FALSE;????
????m_ListenSocket.Close();
?
????CConnectThread* pThread = NULL;
?
????// close all running threads
????do
????{
????????m_CriticalSection.Lock();
?
????????POSITION pos = m_ThreadList.GetHeadPosition();
????????if (pos != NULL)
????????{
????????????pThread = (CConnectThread *)m_ThreadList.GetAt(pos);
????????
????????????m_CriticalSection.Unlock();
?
????????????// save thread members
????????????int nThreadID = pThread->m_nThreadID;
????????????HANDLE hThread = pThread->m_hThread;
?
????????????AddTraceLine(0, "[%u] Shutting down thread...", nThreadID);
?
????????????// tell thread to stop
????????????pThread->SetThreadPriority(THREAD_PRIORITY_HIGHEST);
????????????pThread->PostThreadMessage(WM_QUIT,0,0);
?
????????????// wait for thread to end, while keeping the messages pumping (max 5 seconds)
????????????if (WaitWithMessageLoop(hThread, 5000) == FALSE)
????????????{
????????????????// thread doesn‘t want to stopped
????????????????AddTraceLine(0, "[%u] Problem while killing thread.", nThreadID);
????????????????// don‘t try again, so remove
????????????????m_CriticalSection.Lock();
????????????????POSITION rmPos = m_ThreadList.Find(pThread);
????????????????if (rmPos != NULL)
????????????????????m_ThreadList.RemoveAt(rmPos);
????????????????m_CriticalSection.Unlock();
????????????}
????????????else
????????????{
????????????????AddTraceLine(0, "[%u] Thread successfully stopped.", nThreadID);
????????????}
????????}
????????else
????????{
????????????m_CriticalSection.Unlock();????
????????????pThread = NULL;
????????}
????}
????while (pThread != NULL);
?
????AddTraceLine(0, "FTP Server stopped.");
?
????if (IsWindow(m_hWnd))
????????DestroyWindow();
?
????m_hWnd = NULL;
}
(3)账号操作
用户账号操作包括:添加,编辑、删除和保存,具体实现代码如下:
//添加账号
void CUserAccountPage::OnAddUser()
{
????CAddUserDlg dlg;
????if (dlg.DoModal() == IDOK)
????{
????????for (int i=0; i<m_UsersList.GetItemCount(); i++)
????????{
????????????CString strName;
????????????strName = m_UsersList.GetItemText(i, 0);
????????????if (strName.CompareNoCase(dlg.m_strName) == 0)
????????????{
????????????????AfxMessageBox("Sorry, this user already exists!");
????????????????return;
????????????}
????????}
?
????????CUser user;
????????user.m_strName = dlg.m_strName;
????????user.m_strPassword = "";
?
????????int nItem = m_UsersList.InsertItem(0, user.m_strName, 0);
????????if (nItem <= m_nPreviousIndex)
????????????m_nPreviousIndex++;
?
????????// add home directory item
????????user.m_bAllowCreateDirectory = FALSE;
????????user.m_bAllowDelete = FALSE;
????????user.m_bAllowDownload = TRUE;
????????user.m_bAllowRename = FALSE;
????????user.m_bAllowUpload = FALSE;
????????user.m_strHomeDirectory = "";
?
????????int index = m_UserArray.Add(user);
????????
????????m_UsersList.SetItemData(nItem, index);
????????m_UsersList.SetItemState(nItem, LVIS_SELECTED | LVIS_FOCUSED , LVIS_SELECTED | LVIS_FOCUSED);
?
????????OnSelchangeUserlist();
?
????????// update user manager
????????theServer.m_UserManager.UpdateUserList(m_UserArray);
????????SetModified();
?
????????// launch directory browser
????????PostMessage(WM_COMMAND, IDC_BROWSE);
????}
}
?
//更改用户名
void CUserAccountPage::OnEditUser()
{
????// get selected user
????int nSelIndex = m_UsersList.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
if(nSelIndex == -1)
return;
?
????int nUserIndex = m_UsersList.GetItemData(nSelIndex);
?
????CAddUserDlg dlg;
????dlg.m_strTitle = "修改";
????dlg.m_strName = m_UserArray[nUserIndex].m_strName;
?
????if (dlg.DoModal() == IDOK)
????{
????????// check if user already exists
????????for (int i=0; i<m_UsersList.GetItemCount(); i++)
????????{
????????????if (i != nSelIndex)
????????????{
????????????????CString strName;
????????????????strName = m_UsersList.GetItemText(i, 0);
????????????????if (strName.CompareNoCase(dlg.m_strName) == 0)
????????????????{
????????????????????AfxMessageBox("Sorry, this user already exists!");
????????????????????return;
????????????????}
????????????}
????????}
?
????????m_UserArray[nUserIndex].m_strName = dlg.m_strName;
?
????????m_UsersList.DeleteItem(nSelIndex);
????????nSelIndex = m_UsersList.InsertItem(0, dlg.m_strName, 0);
?
????????m_UsersList.SetItemData(nSelIndex, nUserIndex);
????????m_UsersList.SetItemState(nSelIndex, LVIS_SELECTED | LVIS_FOCUSED , LVIS_SELECTED | LVIS_FOCUSED);
????????m_nPreviousIndex = nSelIndex;
????????
????????OnSelchangeUserlist();
?
????????// update user manager
????????theServer.m_UserManager.UpdateUserList(m_UserArray);
????????SetModified(FALSE);
????}
}
//更改用户目录
void CUserAccountPage::OnBrowse()
{
????CString strDir = BrowseForFolder(m_hWnd, "Select a home directory:", BIF_RETURNONLYFSDIRS);
????if (!strDir.IsEmpty())
????{
????????m_strHomeDirectory = strDir;
????????UpdateData(FALSE);
????}????
}
?
//删除账号
void CUserAccountPage::OnDelUser()
{
????int nSelIndex = m_UsersList.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
if(nSelIndex == -1)
return;
?
????CString strText;
?
????strText.Format("Are you sure you want to delete ‘%s‘?", m_UsersList.GetItemText(nSelIndex, 0));
????if (MessageBox(strText, "FTP Server", MB_YESNO | MB_ICONQUESTION) != IDYES)
????{
????????return;
????}
?
????int nUserIndex = m_UsersList.GetItemData(nSelIndex);
?
????// remove user from array
????m_UserArray.RemoveAt(nUserIndex);
?
????m_nPreviousIndex = -1;
????// update item data values
????
????m_UsersList.SetRedraw(FALSE);
????m_UsersList.DeleteAllItems();
????
????// update user list
????for (int i=0; i < m_UserArray.GetSize(); i++)
????{
????????int nIndex = m_UsersList.InsertItem(0, m_UserArray[i].m_strName, 0);
????????m_UsersList.SetItemData(nIndex, i);
????}????
????m_UsersList.SetRedraw(TRUE);
?
????// update user manager
????theServer.m_UserManager.UpdateUserList(m_UserArray);
????SetModified(FALSE);
?
????m_UsersList.SetItemState(nSelIndex-1, LVIS_SELECTED | LVIS_FOCUSED , LVIS_SELECTED | LVIS_FOCUSED);
????OnSelchangeUserlist();
}
?
//保存当前修改
BOOL CUserAccountPage::UpdateAccount(int nSelIndex)
{
????if (nSelIndex == -1)
????{
????????// get selected user
????????nSelIndex = m_UsersList.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
????}
?
????if (nSelIndex != -1)
????{
????????UpdateData();
?
????????int nUserIndex = m_UsersList.GetItemData(nSelIndex);
?
????????m_UserArray[nUserIndex].m_strPassword = m_strPassword;
????????m_UserArray[nUserIndex].m_bAccountDisabled = m_bDisableAccount;
?
????????// check if it‘s a valid directory
????????if (GetFileAttributes(m_strHomeDirectory) == 0xFFFFFFFF)
????????{
????????????MessageBox("Please enter a valid home directory", "FTP Server", MB_OK | MB_ICONEXCLAMATION);
????????????GetDlgItem(IDC_HOME_DIRECTORY)->SetFocus();
????????????m_UsersList.SetItemState(m_nPreviousIndex, LVIS_SELECTED | LVIS_FOCUSED , LVIS_SELECTED | LVIS_FOCUSED);
????????????return FALSE;
????????}
?
????????m_UserArray[nUserIndex].m_strHomeDirectory = m_strHomeDirectory;
????????m_UserArray[nUserIndex].m_bAllowCreateDirectory = m_bAllowCreateDirectory;
????????m_UserArray[nUserIndex].m_bAllowDelete = m_bAllowDelete;
????????m_UserArray[nUserIndex].m_bAllowDownload = m_bAllowDownload;
????????m_UserArray[nUserIndex].m_bAllowRename = m_bAllowRename;
????????m_UserArray[nUserIndex].m_bAllowUpload = m_bAllowUpload;
????????
????????// update user manager
????????theServer.m_UserManager.UpdateUserList(m_UserArray);
????????return TRUE;
????}
????return FALSE;
}
?
(4)查看在线用户及其使用的线程号
在线用户显示页面主要代码如下:
BOOL COnlineUsersPage::OnInitDialog()
{
????CDialog::OnInitDialog();
?
????m_OnlineUsers.InsertColumn(0, "ThreadID");????????
????m_OnlineUsers.InsertColumn(1, "Username");????
????m_OnlineUsers.InsertColumn(2, "IP Adress");????
????m_OnlineUsers.InsertColumn(3, "Login Time");
????
????DWORD dwStyle = m_OnlineUsers.GetExtendedStyle();
????dwStyle |= LVS_EX_FULLROWSELECT;
m_OnlineUsers.SetExtendedStyle(dwStyle);
????return TRUE;
}
void COnlineUsersPage::OnSize(UINT nType, int cx, int cy)
{
????CDialog::OnSize(nType, cx, cy);
????
????if (IsWindow(::GetDlgItem(m_hWnd, IDC_ONLINE_USERS)))
????{
????????CRect rect;
????????GetClientRect(rect);
????????m_OnlineUsers.MoveWindow(rect);
????????m_OnlineUsers.SetColumnWidth(0, 0);
????????m_OnlineUsers.SetColumnWidth(1, rect.Width()/3-2);
????????m_OnlineUsers.SetColumnWidth(2, rect.Width()/3-2);
????????m_OnlineUsers.SetColumnWidth(3, rect.Width()/3-2);
????}????
}
?
void COnlineUsersPage::AddUser(DWORD nThreadID, LPCTSTR lpszName, LPCTSTR lpszAddress)
{
????CString strThreadID;
????strThreadID.Format("%d", nThreadID);
?
????LVFINDINFO info;
????
????info.flags = LVFI_PARTIAL|LVFI_STRING;
????info.psz = (LPCTSTR)strThreadID;
?
????int nIndex = m_OnlineUsers.FindItem(&info);
????if (nIndex == -1)
????{
????????nIndex = m_OnlineUsers.InsertItem(0, strThreadID);
????}
?
????m_OnlineUsers.SetItemText(nIndex, 1, lpszName);
????m_OnlineUsers.SetItemText(nIndex, 2, lpszAddress);
????m_OnlineUsers.SetItemText(nIndex, 3, CTime::GetCurrentTime().Format("%H:%M:%S"));
????
}
void COnlineUsersPage::RemoveUser(DWORD nThreadID)
{
????LVFINDINFO info;
????
????CString strThreadID;
????strThreadID.Format("%d", nThreadID);
?
????info.flags = LVFI_PARTIAL|LVFI_STRING;
????info.psz = (LPCTSTR)strThreadID;
?
????int nIndex = m_OnlineUsers.FindItem(&info);
????if (nIndex != -1)
????{
????????m_OnlineUsers.DeleteItem(nIndex);
????}
}
void COnlineUsersPage::OnContextMenu(CWnd* pWnd, CPoint point)
{
????// get selected user
????int nIndex = m_OnlineUsers.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
if(nIndex == -1)
return;
?
????CMenu menu;
????menu.LoadMenu(MAKEINTRESOURCE(IDR_ONLINE_MENU));
????menu.GetSubMenu(0)->TrackPopupMenu(0, point.x, point.y, this, NULL);????????????
?
}
void COnlineUsersPage::OnKickUser()
{
????int nIndex = m_OnlineUsers.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
????while (nIndex != -1)
????{
????????CString strThreadID = m_OnlineUsers.GetItemText(nIndex, 0);
????????PostThreadMessage(atoi(strThreadID), WM_QUIT, 0 ,0);
????????nIndex = m_OnlineUsers.GetNextItem(nIndex, LVNI_ALL | LVNI_SELECTED);
????}
}
?
?
void COnlineUsersPage::OnCancel()
{
//????CDialog::OnCancel();
}
?
void COnlineUsersPage::OnOK()
{
//????CDialog::OnOK();
}
?
为检验FTP客户端的可用性和进一步掌握FTP标准基本协议及文件内容的网络传输设计方法,我实现了一个简单的FTP客户端程序。
1.客户端窗口界面设计
FTP客户端程序主界面
?
2.功能实现:
(1)服务器的连接与断开
void CFTPClientDlg::OnButtonConnect()
{
????// TODO: Add your control notification handler code here
????UpdateData(TRUE);
????if(!m_ftpConnection){
????????if(m_str_address!=""){
????????????m_ftpConnection=m_internetSession.GetFtpConnection(m_str_address,m_str_username,m_str_password);
????????????if(m_ftpConnection){
????????????????m_ftpConnection->GetCurrentDirectory(m_str_server_dir);
????????????????UpdateData(FALSE);
????????????????LoadFileList();
????????????????m_btn_connect.SetWindowText("断开");
????????????}
????????}
????}
????else{
????????m_ftpConnection->Close();
????????delete m_ftpConnection;
????????m_ftpConnection=NULL;
?
????????m_btn_connect.SetWindowText("连接");
????????m_str_server_dir="";
????????m_listbox_files.ResetContent();
????????
????????UpdateData(FALSE);
????}
}
(2)加载文件、目录信息
void CFTPClientDlg::LoadFileList()
{
????m_listbox_files.ResetContent();
????CFtpFileFind fileFind(m_ftpConnection);
????CString fileName;
????BOOL bMoreFiles=fileFind.FindFile();
????while(bMoreFiles)
????{
????????bMoreFiles=fileFind.FindNextFile();
????????fileName=fileFind.GetFileName();
????????if(fileFind.IsDirectory()){
????????????fileName+=" <dir>";
????????}
????????m_listbox_files.AddString(fileName);????
????}
????fileFind.Close();
}
(3)下载文件
void CFTPClientDlg::OnButtonDownload()
{
????// TODO: Add your control notification handler code here
????UpdateData(TRUE);
????if(m_str_filename!=""){
????????if(m_str_filename.Right(5)=="<dir>"){
????????????MessageBox("不能下载目录");
????????}
????????else{
????????????CFileDialog saveDialog(FALSE,NULL,m_str_filename);
????????????if(saveDialog.DoModal()==IDOK)
????????????{
????????????????if(!m_ftpConnection->GetFile(m_str_filename,saveDialog.GetFileName()),FALSE)
????????????????????MessageBox("文件下载失败!");
????????????????else
????????????????????MessageBox("成功下载 "+m_str_filename);
????????????}
????????}
????}
}
?
五. 实验结果
服务器运行效果如下:
主界面,运行状态显示
?
账号列表,编辑用户信息
?
在线用户列表
客户端运行效果如下:
?
不足的是,本次设计的FTP客户端程序较简单,只是为了简单验证服务器的可用性,所以只实现了文件的下载功能,后期将会尝试加上文件上传、删除以及目录创建等功能。
原文:http://www.cnblogs.com/leftshine/p/5698743.html