⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connectsocket.cpp

📁 用套接字实现的ftp文件传输源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/********************************************************************/
/*																	*/
/*  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 + -