《最新FTP服务器与客户端设计与开发.doc》由会员分享,可在线阅读,更多相关《最新FTP服务器与客户端设计与开发.doc(169页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Four short words sum up what has lifted most successful individuals above the crowd: a little bit more.-author-dateFTP服务器与客户端设计与开发详细设计 FTP服务器与客户端设计与开发详细设计程序包括5个主要功能:1. 服务器的运行:启动和停止FTP服务2. 用户管理:添加用户,删除用户和设置用户权限3. 服务器配置:设置服务器开放端口,最大连接数等4. 运行统计:统计当前服务器运行时期上传下载的流量等等5. 安全设置:允许连接服务器的IP列表,以及禁止访问的IP服务器的运行模块
2、功能:负责FTP服务器的运行。使用类:CFTPServer类,CApplicationDlg类,CListenSocket类,CConnectThread类,CConnectSocket类各种类的功能:1. CFTPServer类:是CWnd的子类,作为程序的顶层类,负责实现或者调用各个成员函数2. CApplicationDlg类:CDialog类的子类,实现程序主窗口。3. CListenSocket类:负责监听FTP客户端连接,并实现有效连接4. CConnectThread类:负责实现并保证多个连接的有效性。5. CConnectSocket类:实现FTP命令的解析,数据的发送和接收C
3、FTPServer类作为服务器的顶层类,实现服务器开始运行时的所有成员函数申明如下:class CFTPServer : public CWndfriend CConnectSocket;/CConnectSocket作为其友元类,可以访问内部私有数据成员public:void SetGoodbyeMessage(LPCTSTR lpszText);/发送退出信息void SetWelcomeMessage(LPCTSTR lpszText);/发送欢迎信息void SetTimeout(int nValue);/设置暂停时间void SetPort(int nValue);/设置端口void
4、 SetMaxUsers(int nValue);/设置最大连接数void SetStatisticsInterval(int nValue);/统计时间间隔BOOL IsActive();/是否有效void Stop();BOOL Start();CFTPServer();virtual CFTPServer();CUserManager m_UserManager;/用户管理对象CSecurityManager m_SecurityManager;/安全策略CFTPServer类最主要的成员函数是start()和stop(),分别负责ftp服务器的开始运行和结束运行函数声明如下:/*/*/
5、* Function name : Start*/* Description : Start listining on port 21 and accept new*/* connections.*/*/*/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
6、,0, NULL, 0)AddTraceLine(0, Failed to create notification window.);return FALSE;/ 开始创建socketif (m_ListenSocket.Create(m_nPort)/ start listeningif (m_ListenSocket.Listen()m_ListenSocket.m_pWndServer = this;m_bRunning = TRUE;SetTimer(1, m_nStatisticsInterval, NULL);AddTraceLine(0, FTP Server started o
7、n port %d., m_nPort);return TRUE;AddTraceLine(0, FTP Server failed to listen on port %d., m_nPort);/ destroy notification windowif (IsWindow(m_hWnd)DestroyWindow();m_hWnd = NULL;return FALSE;/*/*/* Function name : Stop*/* Description : Stop FTP server.*/*/*/void CFTPServer:Stop()if (!m_bRunning)retu
8、rn;/ stop statistics timerKillTimer(1);m_bRunning = FALSE;m_ListenSocket.Close();CConnectThread* pThread = NULL;/ close all running threadsdom_CriticalSection.Lock();POSITION pos = m_ThreadList.GetHeadPosition();if (pos != NULL)pThread = (CConnectThread *)m_ThreadList.GetAt(pos);m_CriticalSection.Un
9、lock();/ save thread membersint nThreadID = pThread-m_nThreadID;HANDLE hThread = pThread-m_hThread;AddTraceLine(0, %d Shutting down thread., nThreadID);/ tell thread to stoppThread-SetThreadPriority(THREAD_PRIORITY_HIGHEST);pThread-PostThreadMessage(WM_QUIT,0,0);/ wait for thread to end, while keepi
10、ng the messages pumping (max 5 seconds)if (WaitWithMessageLoop(hThread, 5000) = FALSE)/ thread doesnt want to stoppedAddTraceLine(0, %d Problem while killing thread., nThreadID);/ dont try again, so removem_CriticalSection.Lock();POSITION rmPos = m_ThreadList.Find(pThread);if (rmPos != NULL)m_Thread
11、List.RemoveAt(rmPos);m_CriticalSection.Unlock();elseAddTraceLine(0, %d Thread successfully stopped., nThreadID);elsem_CriticalSection.Unlock();pThread = NULL;while (pThread != NULL);AddTraceLine(0, FTP Server stopped.);if (IsWindow(m_hWnd)DestroyWindow();m_hWnd = NULL;CListenSocket类用于监听每个客户的连接,CList
12、enSocket类是CAsyncSocket的子类,其成员函数listen监听来自客户端的连接,当监听到可以接收的socket的时候通过OnAccept函数准备创建有效连接的进程。函数如下:void CListenSocket:OnAccept(int nErrorCode) / New connection is being establishedCSocket sockit;/ Accept the connection using a temp CSocket object.Accept(sockit);/ Create a thread to handle the connection
13、. The thread is created suspended so that we can/ set variables in CConnectThread before it starts executing.CConnectThread* pThread = (CConnectThread*)AfxBeginThread(RUNTIME_CLASS(CConnectThread), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);if (!pThread)sockit.Close();TRACE(Could not create thread
14、n);return;CFTPServer *pWnd = (CFTPServer *)m_pWndServer; / since everything is successful, add the thread to our listpWnd-m_CriticalSection.Lock(); pWnd-m_ThreadList.AddTail(pThread);pWnd-m_CriticalSection.Unlock();/ save pointerpThread-m_pWndServer = m_pWndServer;/ Pass the socket to the thread by
15、passing the socket handle. You cannot pass/ a CSocket object across threads.pThread-m_hSocket = sockit.Detach();/ Now start the thread.pThread-ResumeThread();CConnectThread类CConnectThread类负责为每个有效进程创建一个线程,每个进程完成数据传输的所有任务,穿件县城后通过InitInstance完成线程的初始化BOOL CConnectThread:InitInstance()try/ Attach the soc
16、ket handle to a CSocket object./ This makes sure that the socket notifications are sent to this thread.m_ConnectSocket.Attach(m_hSocket);m_ConnectSocket.m_pThread = this;CString strIPAddress;UINT nPort;m_ConnectSocket.GetPeerName(strIPAddress, nPort);/ notify server that theres a new connectionm_pWn
17、dServer-SendMessage(WM_THREADSTART, (WPARAM)this, 0);if (CFTPServer *)m_pWndServer)-CheckMaxUsers()m_ConnectSocket.SendResponse(421 Too many users are connected, please try again later.);PostThreadMessage(WM_QUIT,0,0);elseif (!(CFTPServer *)m_pWndServer)-IsIPAddressAllowed(strIPAddress)m_ConnectSock
18、et.SendResponse(421 Access denied, IP address was rejected by the server.);PostThreadMessage(WM_QUIT,0,0);else/ send welcome message to clientCString strText = (CFTPServer *)m_pWndServer)-GetWelcomeMessage();m_ConnectSocket.SendResponse(220 + strText);m_nTimerID = :SetTimer(NULL, 0, 1000, TimerProc)
19、; catch(CException *e) e-Delete();return TRUE;线程结束以后,通过ExitInstance函数实现资源的释放代码如下:int CConnectThread:ExitInstance()CFTPServer *pWnd = (CFTPServer *)m_pWndServer;trypWnd-m_CriticalSection.Lock();/ delete this thread from the linked listPOSITION pos = pWnd-m_ThreadList.Find(this);if(pos != NULL)pWnd-m_
20、ThreadList.RemoveAt(pos);pWnd-m_CriticalSection.Unlock(); / notify service main looppWnd-SendMessage(WM_THREADCLOSE, (WPARAM)this, 0);catch(CException *e) pWnd-m_CriticalSection.Unlock();e-Delete();return CWinThread:ExitInstance();为了了解传输过程中接收和发送的字节数,使用IncReceivedBytes和IncSentBytes来计算。这两个函数在CConnectS
21、ocket类中调用,代码如下:void CConnectThread:IncSentBytes(int nBytes)m_LastDataTransferTime = CTime:GetCurrentTime();m_nSentBytes += nBytes;/ notify server classm_pWndServer-PostMessage(WM_THREADMSG, (WPARAM)0, (LPARAM)nBytes);void CConnectThread:IncReceivedBytes(int nBytes)m_LastDataTransferTime = CTime:GetC
22、urrentTime();m_nReceivedBytes += nBytes;/ notify server classm_pWndServer-PostMessage(WM_THREADMSG, (WPARAM)1, (LPARAM)nBytes);CConnectSocket类每个线程都是通过一个CConnectSocket对象m_ConnectSocket来完成数据的接受和发送。当线程创建成功以后,m_ConnectSocket对象通过OnReceive函数获得数据,然后利用ParseCommand函数来解析其中FTP命令void CConnectSocket:OnReceive(in
23、t nErrorCode) TCHAR buffBUFFERSIZE;int nRead = Receive(buff, BUFFERSIZE);switch (nRead)case 0:Close();break;case SOCKET_ERROR:if (GetLastError() != WSAEWOULDBLOCK) TCHAR szError256;wsprintf(szError, OnReceive error: %d, GetLastError();AfxMessageBox (szError);break;default:if (nRead != SOCKET_ERROR &
24、 nRead != 0)(CConnectThread *)AfxGetThread()-IncReceivedBytes(nRead);/ terminate the stringbuffnRead = 0; m_RxBuffer += CString(buff);GetRxLine();break;CSocket:OnReceive(nErrorCode);ParseCommand函数是当前程序最重要的一个部分,它根据客户端提交的各种命令进行相应的操作代码如下void CConnectSocket:ParseCommand()static CFTPCommand commandList =
25、 TOK_USER,USER, TRUE,TOK_PASS,PASS, TRUE,TOK_CWD,CWD,TRUE,TOK_PWD,PWD,FALSE,TOK_PORT,PORT, TRUE,TOK_PASV,PASV, FALSE,TOK_TYPE,TYPE, TRUE,TOK_LIST,LIST, FALSE,TOK_REST,REST, TRUE,TOK_CDUP,CDUP, FALSE,TOK_RETR,RETR, TRUE,TOK_STOR,STOR, TRUE,TOK_SIZE,SIZE, TRUE,TOK_DELE,DELE, TRUE,TOK_RMD,RMD,TRUE,TOK_
26、MKD,MKD,TRUE,TOK_RNFR,RNFR, TRUE,TOK_RNTO,RNTO, TRUE,TOK_ABOR,ABOR, FALSE, TOK_SYST,SYST, FALSE,TOK_NOOP,NOOP, FALSE,TOK_BYE,BYE, FALSE,TOK_QUIT,QUIT, FALSE,TOK_ERROR,FALSE,;/ parse commandCString strCommand, strArguments;if (!GetRxCommand(strCommand, strArguments)return;int nCommand;/查找命令for (nComm
27、and = TOK_USER; nCommand TOK_PASS & !m_bLoggedon)SendResponse(530 Please log in with USER and PASS first.);return;/ proces commandswitch(nCommand)/ specify usernamecase TOK_USER:strArguments.MakeLower();m_bLoggedon = FALSE;m_strUserName = strArguments;CString strPeerAddress;UINT nPeerPort;GetPeerNam
28、e(strPeerAddress, nPeerPort);/ tell FTP server a new user has connectedCConnectThread *pThread = (CConnectThread *)m_pThread;(CFTPServer *)pThread-m_pWndServer)-m_pEventSink-OnFTPUserConnected(m_pThread-m_nThreadID, m_strUserName, strPeerAddress);SendResponse(331 Password required for + strArguments
29、);break;/ specify passwordcase TOK_PASS:/ already logged on ?if (m_bLoggedon)SendResponse(503 Bad sequence of commands.);else/ check user and passwordCUser user;if (theServer.m_UserManager.CheckUser(m_strUserName, strArguments, user)/设置用户主目录m_strCurrentDir = /;/ 成功登录提示m_bLoggedon = TRUE;SendResponse
30、(230 Logged on);else SendResponse(530 Login or password incorrect!);break;/ change current directorycase TOK_CWD:int nResult = theServer.m_UserManager.ChangeDirectory(m_strUserName, m_strCurrentDir, strArguments);CString str;switch(nResult)case 0:str.Format(250 CWD successful. %s is current director
31、y., m_strCurrentDir);SendResponse(str);break;case 1:str.Format(550 CWD failed. %s: Permission denied., strArguments);SendResponse(str);break;default:str.Format(550 CWD failed. %s: directory not found., strArguments);SendResponse(str);break;break; / print current directorycase TOK_PWD:CString str;str
32、.Format(257 %s is current directory., m_strCurrentDir);SendResponse(str);break;/ specify IP and port (PORT a1,a2,a3,a4,p1,p2) - IP address a1.a2.a3.a4, port p1*256+p2. case TOK_PORT:CString strSub;int nCount=0;while (AfxExtractSubString(strSub, strArguments, nCount+, ,)switch(nCount)case 1:/ a1m_Tra
33、nsferStatus.m_strRemoteHost = strSub;m_TransferStatus.m_strRemoteHost += .;break;case 2:/ a2m_TransferStatus.m_strRemoteHost += strSub;m_TransferStatus.m_strRemoteHost += .;break;case 3:/ a3m_TransferStatus.m_strRemoteHost += strSub;m_TransferStatus.m_strRemoteHost += .;break;case 4:/ a4m_TransferSt
34、atus.m_strRemoteHost += strSub;break;case 5:/ p1m_TransferStatus.m_nRemotePort = 256*atoi(strSub);break;case 6:/ p2m_TransferStatus.m_nRemotePort += atoi(strSub);break;m_TransferStatus.m_bPassiveMode = FALSE;SendResponse(200 Port command successful);break;/ switch to passive modecase TOK_PASV:/ dele
35、te existing datasocketDestroyDataSocket();/ create new data socketm_TransferStatus.m_pDataSocket = new CDataSocket(this, -1);if (!m_TransferStatus.m_pDataSocket-Create()DestroyDataSocket();SendResponse(421 Cant create socket);break;/ start listeningm_TransferStatus.m_pDataSocket-Listen();m_TransferS
36、tatus.m_pDataSocket-AsyncSelect();CString strIP, strTmp;UINT nPort;/ get our ip addressGetSockName(strIP, nPort);/ Now retrieve the portm_TransferStatus.m_pDataSocket-GetSockName(strTmp, nPort);/ Reformat the ipstrIP.Replace(.,);/ tell the client which address/port to connect toCString str;str.Forma
37、t(227 Entering Passive Mode (%s,%d,%d), strIP, nPort/256, nPort%256);SendResponse(str);m_TransferStatus.m_bPassiveMode = TRUE;break; case TOK_TYPE:SendResponse(200 Type set to + strArguments);break;/ list current directorycase TOK_LIST:if(!m_TransferStatus.m_bPassiveMode & (m_TransferStatus.m_strRemoteHost = | m_TransferStatus.m_nRemotePort = -1)SendResponse(503 Bad sequence of commands.);else/ if client did not specify a directory use current dirif (strArguments = )strArguments = m_strCurrentDir;
限制150内