?? connectsocket.cpp
字號:
/********************************************************************/
/* */
/* CONNECTSOCKET.CPP */
/* */
/* Implementation of the Connect Socket. */
/* This class is a part of the CConnectThread which handles */
/* socket connections. Incomming data is processed in OnReceive */
/* */
/* Programmed by Pablo van der Meer */
/* Based partially on and inspired by FileZilla Server. */
/* */
/* http://www.pablovandermeer.nl */
/* */
/* Last updated: 15 july 2002 */
/* */
/********************************************************************/
//在ConnectSocket.cpp文件中實現連接套接字的創建,該類是CConnectThread類的
//一部分,收到數據由函數OnReceive()完成處理。
#include "stdafx.h"
#include "FTPServerApp.h"
#include "FTPServer.h"
#include "ConnectSocket.h"
#include "ConnectThread.h"
#include "ApplicationDlg.h"
#include "DataSocket.h"
extern CFTPServer theServer;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/********************************************************************/
/* */
/* Function name : CConnectSocket::CConnectSocket */
/* Description : Constructor */
/* */
/********************************************************************/
CConnectSocket::CConnectSocket()
{
m_bLoggedon = FALSE;
m_bRenameFile = FALSE;
m_pDataSocket = NULL;
m_strRemoteHost = "";
m_nRemotePort = -1;
m_dwRestartOffset = 0;
m_bPassiveMode = FALSE;
}
/********************************************************************/
/* */
/* Function name : CConnectSocket::~CConnectSocket */
/* Description : Destructor */
/* */
/********************************************************************/
CConnectSocket::~CConnectSocket()
{
DestroyDataConnection();
// tell our thread we have been closed
AfxGetThread()->PostThreadMessage(WM_QUIT,0,0);
TRACE0("CConnectSocket destroyed.\n");
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CConnectSocket, CSocket)
//{{AFX_MSG_MAP(CConnectSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/********************************************************************/
/* */
/* Function name : OnClose */
/* Description : Send WM_QUIT message to the thread containing */
/* the socket to shutdown once the connection is */
/* closed. */
/* */
/********************************************************************/
void CConnectSocket::OnClose(int nErrorCode)
{
Close();
// destroy connection
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
TRACE("CConnectSocket() OnClose()\n");
CSocket::OnClose(nErrorCode);
}
#define BUFFERSIZE 4096
/********************************************************************/
/* */
/* Function name : OnReceive */
/* Description : Called by the framework to notify this socket */
/* that there is data in the buffer. */
/* */
/********************************************************************/
//由主窗口調用通知套接字緩沖區中有數據要接收
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);
// 結束字符串
buff[nRead] = 0;
m_RxBuffer += CString(buff);
//獲得命令行
GetRxLine();
}
break;
}
CSocket::OnReceive(nErrorCode);
}
/********************************************************************/
/* */
/* Function name: GetRxCommand */
/* Description : Get command from receiver buffer. */
/* */
/********************************************************************/
//從接收緩沖區獲得命令
BOOL CConnectSocket::GetRxCommand(CString &strCommand, CString &strArguments)
{
if (!m_strCommands.IsEmpty())
{
CString strBuff = m_strCommands.RemoveHead();
int nIndex = strBuff.Find(" ");
if (nIndex != -1)
{
CString strPassword = strBuff;
strPassword.MakeUpper();
//使密碼不可見
if (strPassword.Left(5) == "PASS ")
{
for (int i=5; i < strPassword.GetLength(); i++)
{
strPassword.SetAt(i, '*');
}
FireStatusMessage(strPassword, 1);
}
else
{
FireStatusMessage(strBuff, 1);
}
strCommand = strBuff.Left(nIndex);
strArguments = strBuff.Mid(nIndex+1);
}
else
{
FireStatusMessage(strBuff, 1);
strCommand = strBuff;
}
if (strCommand != "")
{
strCommand.MakeUpper();
// who screwed up ???
if (strCommand.Right(4) == "ABOR")
{
strCommand = "ABOR";
}
TRACE2("COMMAND: %s, ARGS: %s\n", strCommand, strArguments);
return TRUE;
}
}
return FALSE;
}
/********************************************************************/
/* */
/* Function name: GetRxLine */
/* Description : Parse complete command line */
/* */
/********************************************************************/
//解析整個命令行
void CConnectSocket::GetRxLine()
{
CString strTemp;
int nIndex;
while(!m_RxBuffer.IsEmpty())
{
//尋找換行符
nIndex = m_RxBuffer.Find("\r\n");
if (nIndex != -1)
{
strTemp = m_RxBuffer.Left(nIndex);
m_RxBuffer = m_RxBuffer.Mid(nIndex + 2);
if (!strTemp.IsEmpty())
{
m_strCommands.AddTail(strTemp);
// 解析并執行命令
ParseCommand();
}
}
else
break;
}
}
/********************************************************************/
/* */
/* Function name: OnConnect */
/* Description : Called by the framework to notify this connecting */
/* socket that its connection attempt is completed. */
/* */
/********************************************************************/
void CConnectSocket::OnConnect(int nErrorCode)
{
CSocket::OnConnect(nErrorCode);
}
/********************************************************************/
/* */
/* Function name: HasConnectionDropped */
/* Description : Check if connection has been dropped. */
/* Used to detect if client has crashed. */
/* */
/********************************************************************/
BOOL CConnectSocket::HasConnectionDropped(void)
{
BOOL bConnDropped = FALSE;
INT iRet = 0;
BOOL bOK = TRUE;
if (m_hSocket == INVALID_SOCKET)
return TRUE;
struct timeval timeout = { 0, 0 };
fd_set readSocketSet;
FD_ZERO(&readSocketSet);
FD_SET(m_hSocket, &readSocketSet);
iRet = ::select(0, &readSocketSet, NULL, NULL, &timeout);
bOK = (iRet > 0);
if(bOK)
{
bOK = FD_ISSET(m_hSocket, &readSocketSet);
}
if(bOK)
{
CHAR szBuffer[1] = "";
iRet = ::recv(m_hSocket, szBuffer, 1, MSG_PEEK);
bOK = (iRet > 0);
if(!bOK)
{
INT iError = ::WSAGetLastError();
bConnDropped = (( iError == WSAENETRESET) ||
(iError == WSAECONNABORTED) ||
(iError == WSAECONNRESET) ||
(iError == WSAEINVAL) ||
(iRet == 0));
}
}
return(bConnDropped);
}
/********************************************************************/
/* */
/* Function name: SendResponse */
/* Description : Send response to client. */
/* */
/********************************************************************/
//發送響應給客戶端
BOOL CConnectSocket::SendResponse(LPCTSTR pstrFormat, ...)
{
CString str;
// 格式化參數
va_list args;
va_start(args, pstrFormat);
str.FormatV(pstrFormat, args);
//判斷連接是否在活動狀態
if (HasConnectionDropped())
{
FireStatusMessage("Could not send reply, disconnected.", 2);
Close();
// 通知線程已經停止,關閉連接
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
return FALSE;
}
int nBytes = CSocket::Send(str + "\r\n", str.GetLength()+2);
if (nBytes == SOCKET_ERROR)
{
Close();
FireStatusMessage("Could not send reply, disconnected.", 2);
//通知線程連接已經關閉
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
return FALSE;
}
FireStatusMessage(str, 2);
((CConnectThread *)AfxGetThread())->IncSentBytes(nBytes);
return TRUE;
}
/********************************************************************/
/* */
/* Function name: ParseCommand */
/* Description : Parse and execute command from client. */
/* */
/* Based on code provided by FileZilla Server. */
/* http://sourceforge.net/projects/filezilla */
/* */
/********************************************************************/
//從客戶端解析并執行命令
void CConnectSocket::ParseCommand()
{
//命令列表
static CFTPCommand commandList[] =
{
{TOK_ABOR, "ABOR", FALSE, "Abort transfer: ABOR"},
{TOK_BYE, "BYE", FALSE, "Logout or break the connection: BYE"},
{TOK_CDUP, "CDUP", FALSE, "Change to parent directory: CDUP"},
{TOK_CWD, "CWD", TRUE, "Change working directory: CWD [directory-name]"},
{TOK_DELE, "DELE", TRUE , "Delete file: DELE file-name"},
{TOK_DIR, "DIR", FALSE, "Get directory listing: DIR [path-name]"},
{TOK_HELP, "HELP", FALSE, "Show help: HELP [command]"},
{TOK_LIST, "LIST", FALSE, "Get directory listing: LIST [path-name]"},
{TOK_MKD, "MKD", TRUE, "Make directory: MKD path-name"},
{TOK_NOOP, "NOOP", FALSE, "Do nothing: NOOP"},
{TOK_PASS, "PASS", TRUE, "Supply a user password: PASS password"},
{TOK_PASV, "PASV", FALSE, "Set server in passive mode: PASV"},
{TOK_PORT, "PORT", TRUE, "Specify the client port number: PORT a0,a1,a2,a3,a4,a5"},
{TOK_PWD, "PWD", FALSE, "Get current directory: PWD"},
{TOK_QUIT, "QUIT", FALSE, "Logout or break the connection: QUIT"},
{TOK_REST, "REST", TRUE, "Set restart transfer marker: REST marker"},
{TOK_RETR, "RETR", TRUE, "Get file: RETR file-name"},
{TOK_RMD, "RMD", TRUE, "Remove directory: RMD path-name"},
{TOK_RNFR, "RNFR", TRUE, "Specify old path name of file to be renamed: RNFR file-name"},
{TOK_RNTO, "RNTO", TRUE, "Specify new path name of file to be renamed: RNTO file-name"},
{TOK_SIZE, "SIZE", TRUE, "Get filesize: SIZE file-name"},
{TOK_STOR, "STOR", TRUE, "Store file: STOR file-name"},
{TOK_SYST, "SYST", FALSE, "Get operating system type: SYST"},
{TOK_TYPE, "TYPE", TRUE, "Set filetype: TYPE [A | I]"},
{TOK_USER, "USER", TRUE, "Supply a username: USER username"},
{TOK_ERROR, "", FALSE, ""},
};
// 解析命令
CString strCommand, strArguments;
if (!GetRxCommand(strCommand, strArguments))
{
return;
}
int nCommand;
//在命令列表中查找命令
for (nCommand = TOK_ABOR; nCommand < TOK_ERROR; nCommand++)
{
// 判斷是否找到命令
if (strCommand == commandList[nCommand].m_pszName)
{
if (commandList[nCommand].m_bHasArguments && (strArguments.IsEmpty()))
{
SendResponse("501 Syntax error: Invalid number of parameters.");
return;
}
break;
}
}
if (nCommand == TOK_ERROR)
{
// 命令不在我們的列表中
SendResponse("501 Syntax error: Command not understood.");
return;
}
//判斷是否成功登錄服務器
if ((nCommand != TOK_USER && nCommand != TOK_PASS) && !m_bLoggedon)
{
SendResponse("530 Please login with USER and PASS.");
return;
}
// 處理命令
switch(nCommand)
{
//指定用戶名
case TOK_USER:
{
strArguments.MakeLower();
m_bLoggedon = FALSE;
m_strUserName = strArguments;
CString strPeerAddress;
UINT nPeerPort;
//獲得客戶端IP地址和端口
GetPeerName(strPeerAddress, nPeerPort);
// 通知FTP服務器一個新的用戶已經連接
CConnectThread *pThread = (CConnectThread *)m_pThread;
((CFTPServer *)pThread->m_pWndServer)->m_pEventSink->OnFTPUserConnected(m_pThread->m_nThreadID, m_strUserName, strPeerAddress);
SendResponse("331 User name ok, need password.");
}
break;
// 指定密碼
case TOK_PASS:
{
//判斷是否登錄成功
if (m_bLoggedon)
{
SendResponse("503 Login with USER first.");
}
else
{
// 用用戶名和密碼登錄客戶端
CUser user;
// 檢查用戶名
if (theServer.m_UserManager.GetUser(m_strUserName, user))
{
// 檢查密碼
if ((!user.m_strPassword.Compare(strArguments) || user.m_strPassword.IsEmpty()) && !user.m_bAccountDisabled)
{
// 設置用戶路徑
m_strCurrentDir = "/";
// 成功登錄
m_bLoggedon = TRUE;
SendResponse("230 User successfully logged in.");
break;
}
}
SendResponse("530 Not logged in, user or password incorrect!");
}
}
break;
// 改變傳輸模式
case TOK_TYPE:
{
SendResponse("200 Type set to %s", strArguments);
}
break;
//打印當前路徑
case TOK_PWD:
{
SendResponse("257 \"%s\" is current directory.", m_strCurrentDir);
}
break;
// 改變上層目錄
case TOK_CDUP:
strArguments = "..";
// 改變工作目錄
case TOK_CWD:
{
//改變到指定目錄
int nResult = theServer.m_UserManager.ChangeDirectory(m_strUserName, m_strCurrentDir, strArguments);
switch(nResult)
{
case 0:
SendResponse("250 CWD command successful. \"%s\" is current directory.", m_strCurrentDir);
break;
case 1:
SendResponse("550 CWD command failed. \"%s\": Permission denied.", strArguments);
break;
default:
SendResponse("550 CWD command failed. \"%s\": Directory not found.", strArguments);
break;
}
}
break;
// 指定IP地址和端口
case TOK_PORT:
{
CString strSub;
int nCount=0;
while (AfxExtractSubString(strSub, strArguments, nCount++, ','))
{
switch(nCount)
{
case 1:
m_strRemoteHost = strSub;
m_strRemoteHost += ".";
break;
case 2:
m_strRemoteHost += strSub;
m_strRemoteHost += ".";
break;
case 3:
m_strRemoteHost += strSub;
m_strRemoteHost += ".";
break;
case 4:
m_strRemoteHost += strSub;
break;
case 5:
m_nRemotePort = 256*atoi(strSub);
break;
case 6:
m_nRemotePort += atoi(strSub);
break;
}
}
m_bPassiveMode = FALSE;
SendResponse("200 Port command successful.");
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -