收藏 分销(赏)

FTP服务器与客户端设计与开发.doc

上传人:精**** 文档编号:3068496 上传时间:2024-06-14 格式:DOC 页数:32 大小:99.50KB 下载积分:12 金币
下载 相关 举报
FTP服务器与客户端设计与开发.doc_第1页
第1页 / 共32页
FTP服务器与客户端设计与开发.doc_第2页
第2页 / 共32页


点击查看更多>>
资源描述
FTP服务器与客户端设计与开发 详细设计 程序包括5个主要功能: 1. 服务器的运行:启动和停止FTP服务 2. 用户管理:添加用户,删除用户和设置用户权限 3. 服务器配置:设置服务器开放端口,最大连接数等 4. 运行统计:统计当前服务器运行时期上传下载的流量等等 5. 安全设置:允许连接服务器的IP列表,以及禁止访问的IP 服务器的运行模块 功能:负责FTP服务器的运行。 使用类:CFTPServer类,CApplicationDlg类,CListenSocket类,CConnectThread类,CConnectSocket类 各种类的功能: 1. CFTPServer类:是CWnd的子类,作为程序的顶层类,负责实现或者调用各个成员函数 2. CApplicationDlg类:CDialog类的子类,实现程序主窗口。 3. CListenSocket类:负责监听FTP客户端连接,并实现有效连接 4. CConnectThread类:负责实现并保证多个连接的有效性。 5. CConnectSocket类:实现FTP命令的解析,数据的发送和接收 CFTPServer类 作为服务器的顶层类,实现服务器开始运行时的所有成员函数 申明如下: class CFTPServer : public CWnd { friend CConnectSocket;//CConnectSocket作为其友元类,可以访问内部私有数据成员 public: void SetGoodbyeMessage(LPCTSTR lpszText);//发送退出信息 void SetWelcomeMessage(LPCTSTR lpszText);//发送欢迎信息 void SetTimeout(int nValue);//设置暂停时间 void SetPort(int nValue);//设置端口 void 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服务器的开始运行和结束运行 函数声明如下: /********************************************************************/ /* */ /* 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,0, NULL, 0)) { AddTraceLine(0, "Failed to create notification window."); return FALSE; } // 开始创建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; } /********************************************************************/ /* */ /* Function name : Stop */ /* Description : Stop FTP server. */ /* */ /********************************************************************/ 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, "[%d] 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, "[%d] 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, "[%d] 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; } CListenSocket类 用于监听每个客户的连接,CListenSocket类是CAsyncSocket的子类,其成员函数listen监听来自客户端的连接,当监听到可以接收的socket的时候通过OnAccept函数准备创建有效连接的进程。 函数如下: void CListenSocket::OnAccept(int nErrorCode) { // New connection is being established CSocket sockit; // Accept the connection using a temp CSocket object. Accept(sockit); // Create a thread to handle the connection. 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\n"); return; } CFTPServer *pWnd = (CFTPServer *)m_pWndServer; // since everything is successful, add the thread to our list pWnd->m_CriticalSection.Lock(); pWnd->m_ThreadList.AddTail(pThread); pWnd->m_CriticalSection.Unlock(); // save pointer pThread->m_pWndServer = m_pWndServer; // Pass the socket to the thread by 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 socket 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 there's a new connection m_pWndServer->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); } else if (!((CFTPServer *)m_pWndServer)->IsIPAddressAllowed(strIPAddress)) { m_ConnectSocket.SendResponse("421 Access denied, IP address was rejected by the server."); PostThreadMessage(WM_QUIT,0,0); } else { // send welcome message to client CString strText = ((CFTPServer *)m_pWndServer)->GetWelcomeMessage(); m_ConnectSocket.SendResponse("220 " + strText); m_nTimerID = ::SetTimer(NULL, 0, 1000, TimerProc); } } catch(CException *e) { e->Delete(); } return TRUE; } 线程结束以后,通过ExitInstance函数实现资源的释放代码如下: int CConnectThread::ExitInstance() { CFTPServer *pWnd = (CFTPServer *)m_pWndServer; try { pWnd->m_CriticalSection.Lock(); // delete this thread from the linked list POSITION pos = pWnd->m_ThreadList.Find(this); if(pos != NULL) { pWnd->m_ThreadList.RemoveAt(pos); } pWnd->m_CriticalSection.Unlock(); // notify service main loop pWnd->SendMessage(WM_THREADCLOSE, (WPARAM)this, 0); } catch(CException *e) { pWnd->m_CriticalSection.Unlock(); e->Delete(); } return CWinThread::ExitInstance(); } 为了了解传输过程中接收和发送的字节数,使用IncReceivedBytes和IncSentBytes来计算。这两个函数在CConnectSocket类中调用,代码如下: void CConnectThread::IncSentBytes(int nBytes) { m_LastDataTransferTime = CTime::GetCurrentTime(); m_nSentBytes += nBytes; // notify server class m_pWndServer->PostMessage(WM_THREADMSG, (WPARAM)0, (LPARAM)nBytes); } void CConnectThread::IncReceivedBytes(int nBytes) { m_LastDataTransferTime = CTime::GetCurrentTime(); m_nReceivedBytes += nBytes; // notify server class m_pWndServer->PostMessage(WM_THREADMSG, (WPARAM)1, (LPARAM)nBytes); } CConnectSocket类 每个线程都是通过一个CConnectSocket对象m_ConnectSocket来完成数据的接受和发送。当线程创建成功以后,m_ConnectSocket对象通过OnReceive函数获得数据,然后利用ParseCommand函数来解析其中FTP命令 void CConnectSocket::OnReceive(int nErrorCode) { TCHAR buff[BUFFERSIZE]; int nRead = Receive(buff, BUFFERSIZE); switch (nRead) { case 0: Close(); break; case SOCKET_ERROR: if (GetLastError() != WSAEWOULDBLOCK) { TCHAR szError[256]; wsprintf(szError, "OnReceive error: %d", GetLastError()); AfxMessageBox (szError); } break; default: if (nRead != SOCKET_ERROR && nRead != 0) { ((CConnectThread *)AfxGetThread())->IncReceivedBytes(nRead); // terminate the string buff[nRead] = 0; m_RxBuffer += CString(buff); GetRxLine(); } break; } CSocket::OnReceive(nErrorCode); } ParseCommand函数 是当前程序最重要的一个部分,它根据客户端提交的各种命令进行相应的操作代码如下 void CConnectSocket::ParseCommand() { static CFTPCommand commandList[] = { {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_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 command CString strCommand, strArguments; if (!GetRxCommand(strCommand, strArguments)) { return; } int nCommand; //查找命令 for (nCommand = TOK_USER; nCommand < TOK_ERROR; nCommand++) { // found command ? if (strCommand == commandList[nCommand].m_pszName) { // did we expect an argument ? if (commandList[nCommand].m_bHasArguments && (strArguments == "")) { SendResponse("501 Syntax error"); return; } break; } } if (nCommand == TOK_ERROR) { // command is not in our list SendResponse("500 Syntax error, command unrecognized."); return; } // no commands are excepted before successfull logged on if (nCommand > TOK_PASS && !m_bLoggedon) { SendResponse("530 Please log in with USER and PASS first."); return; } // proces command switch(nCommand) { // specify username case TOK_USER: { strArguments.MakeLower(); m_bLoggedon = FALSE; m_strUserName = strArguments; CString strPeerAddress; UINT nPeerPort; GetPeerName(strPeerAddress, nPeerPort); // tell FTP server a new user has connected CConnectThread *pThread = (CConnectThread *)m_pThread; ((CFTPServer *)pThread->m_pWndServer)->m_pEventSink->OnFTPUserConnected(m_pThread->m_nThreadID, m_strUserName, strPeerAddress); SendResponse("331 Password required for " + strArguments); } break; // specify password case TOK_PASS: { // already logged on ? if (m_bLoggedon) { SendResponse("503 Bad sequence of commands."); } else { // check user and password CUser user; if (theServer.m_UserManager.CheckUser(m_strUserName, strArguments, user)) { //设置用户主目录 m_strCurrentDir = "/"; // 成功登录提示 m_bLoggedon = TRUE; SendResponse("230 Logged on"); } else SendResponse("530 Login or password incorrect!"); } } break; // change current directory case 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 directory.", 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 directory case TOK_PWD: { CString str; str.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: // a1 m_TransferStatus.m_strRemoteHost = strSub; m_TransferStatus.m_strRemoteHost += "."; break; case 2: // a2 m_TransferStatus.m_strRemoteHost += strSub; m_TransferStatus.m_strRemoteHost += "."; break; case 3: // a3 m_TransferStatus.m_strRemoteHost += strSub; m_TransferStatus.m_strRemoteHost += "."; break; case 4: // a4 m_TransferStatus.m_strRemoteHost += strSub; break; case 5: // p1 m_TransferStatus.m_nRemotePort = 256*atoi(strSub); break; case 6: // p2 m_TransferStatus.m_nRemotePort += atoi(strSub); break; } } m_TransferStatus.m_bPassiveMode = FALSE; SendResponse("200 Port command successful"); break; } // switch to passive mode case TOK_PASV: { // delete existing datasocket DestroyDataSocket(); // create new data socket m_TransferStatus.m_pDataSocket = new CDataSocket(this, -1); if (!m_TransferStatus.m_pDataSocket->Create()) { DestroyDataSocket(); SendResponse("421 Can't create socket"); break; } // start listening m_TransferStatus.m_pDataSocket->Listen(); m_TransferStatus.m_pDataSocket->AsyncSelect(); CString strIP, strTmp; UINT nPort; // get our ip address GetSockName(strIP, nPort); // Now retrieve the port m_TransferStatus.m_pDataSocket->GetSockName(strTmp, nPort); // Reformat the ip strIP.Replace(".",","); // tell the client which address/port to connect to CString str; str.Format("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 directory case 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 dir if (strArguments == "") { strArguments = m_strCurrentDir; } else { // check if argument is file or directory CString strResult; int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_LIST, strResult); if (nRes
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服