FTP客户端-C++

以登录和文件上传、下载为例,搭建简单的TCP客户端,vs下C++编程

目录

FTP主要介绍

FTP命令与响应码

用户登录

被动模式-文件下载

被动模式-数据上传


FTP主要介绍

传送门:https://www.cnblogs.com/jzincnblogs/p/5213978.html

FTP是一种文件传输协议,属于应用层协议,基于TCP,所以客户端与服务器建立的连接是可靠、安全的,并且要经过三次握手的过程。

②FTP传输数据默认采用二进制模式,即将文件内容转换为二进制表示后再传送,而若HTML等文本文件传输时需要转换成ASCII模式。

③FTP客户端在连接服务器时需要用到两个端口,其中一个端口(默认为21)用作控制连接端口,负责发送命令和等待响应;另一个端口用作数据传输,用来建立数据传输通道,端口号为20或其他可用端口号。

④FTP有两种连接模式:主动模式与被动模式。主动模式命令为PORT,此模式下客户端需要向服务器提供一个IP地址和端口号,由服务器来连接客户端的指定端口;被动模式命令为PASV,此模式下由服务器向客户端提供IP地址与端口号,由此解决主动模式下服务器到客户端的数据端口的连接被防火墙过滤掉的问题

⑤FTP的工作流程为:客户端连接服务器->服务器连接成功返回->客户端发送初始化命令->服务器响应命令成功返回->客户端发送用户验证消息->服务器返回验证结果->验证通过,客户端进行数据操作->服务器响应客户端操作命令->客户端发送关闭连接命令->服务器响应关闭连接命令。

FTP命令与响应码

命令 描述 
ABOR中断数据连接程序
ACCT <account>系统特权帐号
ALLO <bytes> 为服务器上的文件存储器分配字节
APPE <filename>添加文件到服务器同名文件
CDUP <dir path>改变服务器上的父目录
CWD <dir path>改变服务器上的工作目录
DELE <filename>删除服务器上的指定文件
HELP <command>返回指定命令信息
LIST <name>如果是文件名列出文件信息,如果是目录则列出文件列表
MODE <mode>传输模式(S=流模式,B=块模式,C=压缩模式)
MKD <directory>在服务器上建立指定目录
NLST <directory>列出指定目录内容
NOOP无动作,除了来自服务器上的承认
PASS <password>系统登录密码
PASV请求服务器等待数据连接
PORT <address>IP 地址和两字节的端口 ID
PWD显示当前工作目录
QUIT从 FTP 服务器上退出登录
REIN重新初始化登录状态连接
REST <offset>由特定偏移量重启文件传递
RETR <filename>从服务器上找回(复制)文件
RMD <directory>在服务器上删除指定目录
RNFR <old path>对旧路径重命名
RNTO <new path>对新路径重命名
SITE <params>由服务器提供的站点特殊参数
SMNT <pathname>挂载指定文件结构
STAT <directory>在当前程序或目录上返回信息
STOR <filename>储存(复制)文件到服务器上
STOU <filename>储存文件到服务器名称上
STRU <type>数据结构(F=文件,R=记录,P=页面)
SYST返回服务器使用的操作系统
TYPE <data type>数据类型(A=ASCII,E=EBCDIC,I=binary)
USER <username>>系统登录的用户名

 

响应代码 解释说明 
110新文件指示器上的重启标记
120服务器准备就绪的时间(分钟数)
125打开数据连接,开始传输
150打开连接
200成功
202命令没有执行
211系统状态回复
212目录状态回复
213文件状态回复
214帮助信息回复
215系统类型回复
220服务就绪
221退出网络
225打开数据连接
226结束数据连接
227进入被动模式(IP 地址、ID 端口)
230登录因特网
250文件行为完成
257路径名建立
331要求密码
332要求帐号
350文件行为暂停
421服务关闭
425无法打开数据连接
426结束连接
450文件不可用
451遇到本地错误
452磁盘空间不足
500无效命令
501错误参数
502命令没有执行
503错误指令序列
504无效命令参数
530未登录网络
532存储文件需要帐号
550文件不可用
551不知道的页类型
552超过存储分配
553文件名不允许

用户登录

//参数及用法
Socket m_ftpClient;
SocketClient m_socketClient;//上一篇文章中的TCP-SocketClient文件
char m_ip[20];
int m_port;
char m_user[1000];
char m_password[1000];
int res = m_ftpClient.Connect("127.0.0.1", 21, "SCADA", "1234@qwer");
//*****************************************************************
int Login(char* ip, int port, char* user, char* password)
{
	int res = 0;
	strcpy_s(m_ip, ip);
	m_ip[strlen(ip)] = 0;
	m_port = port;
	strcpy_s(m_user, user);
	m_user[strlen(user)] = 0;
	strcpy_s(m_password, password);
	m_password[strlen(password)] = 0;
	res = m_socketClient.Connect(m_ip, m_port, 100, 100);//TCP连接IP端口
	if (res != 0)
	{
		goto ExitLine;
	}
	char cmdOut[100] = { 0 };
	char cmdIn[100] = { 0 };
	int len;
	len = 100;
	sprintf_s(cmdOut, "USER %s\r\n", user); //发送的命令都需要以\r\n即回车来结束,否则不识别
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);//发送USER命令
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);//获取返回结果
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "331", 3) != 0)//比较前3个字符,USER命令返回331即需要密码,否则失败
	{
		res = -1;
		goto ExitLine;
	}
	OS_Sleep(10);
	memset(cmdOut, 0, 100);
	sprintf_s(cmdOut, "PASS %s\r\n", password);
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);//发送PASS命令
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);//接收返回值
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "230", 3) != 0)//前3个字符为230即成功登录
	{
		res = -1;
		goto ExitLine;
	}
ExitLine:
	if (res != 0)
	{
		m_socketClient.Disconnect();
	}
	return res;
}

被动模式-文件下载

//参数及用法
len = 10000000;//返回的数据长度,设置最大值
char filePath[_MAX_PATH];//文件路径
memset(filePath, 0, _MAX_PATH);
GetFileData(filePath, data, &len)返回0即成功
//*******************************************************
int GetFileData(char* src, byte* bufFile, int* bufLen)
{
	int res = 0;
	SocketClient fileClient;
	char cmdOut[100] = { 0 };
	char cmdIn[1000] = { 0 };
	char ip[20] = { 0 };
	byte buf[1000];
	int len;
	if (!m_socketClient.IsConnected())//判断是否登录,登录后才可操作
	{
		res = Connect(m_ip, m_port, m_user, m_password);
		if (res != 0)
		{
			goto ExitLine;
		}
	}
	memset(cmdOut, 0, 100);
	sprintf_s(cmdOut, "PASV\r\n");
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);//PASV是被动模式,可以规避服务器防火墙
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);//接收返回结果
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "227", 3) != 0)//前3个字符返回227证明进入被动模式,cmdIn中含有服务器返回的IP和用于数据传输的端口号
	{
		res = -1;
		goto ExitLine;
	}
	int ip1, ip2, ip3, ip4;
	int p1, p2;
	sscanf_s(cmdIn, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", &ip1, &ip2, &ip3, &ip4, &p1, &p2);
	memset(cmdOut, 0, 100);
	sprintf_s(cmdOut, "RETR %s\r\n", src);	//从服务器上找回文件
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);//用命令管理端口发送命令
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 1000;
	res = m_socketClient.Read((byte *)cmdIn, len);//命令管理端口查看返回状态
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "150", 3) != 0)//150为找回文件成功
	{
		res = -1;
		goto ExitLine;
	}
	sprintf(ip, "%d.%d.%d.%d", ip1, ip2, ip3, ip4);
    SocketClient fileClient;//文件用Socket
	res = fileClient.Connect(ip, p1 * 256 + p2, 100, 100);//连接数据传输端口
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 1000;
	int readed = 0;
	while (fileClient.Read(buf, len) == ERRORCODE_OK)//读取文件数据
	{
		if (len == 0)
		{
			break;
		}
		int cpyLen = min(*bufLen, len);
		memcpy(bufFile + readed, buf, cpyLen);
		*bufLen -= cpyLen;
		readed += cpyLen;
		len = 1000;
	}
	*bufLen = readed;
	fileClient.Disconnect();//关闭fileClient Socket后,断开数据端口连接
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);//断开数据端口连接后,返回226确认关闭数据端口连接
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "226", 3) != 0)
	{
		res = -1;
		goto ExitLine;
	}
ExitLine:
	if (res != 0)
	{
		m_socketClient.Disconnect();
	}
	return res;
}

被动模式-数据上传

//参数和用法
//和文件下载类似,被动模式下,由命令管理端口发送命令,连接服务器返回的数据端口发送数据
//第一个是要读取的文件路径,第二个是要存储的文件路径
//********************************************
int UploadImage(char* filePath, char* storPath)
{
	int res = 0;
	SocketClient fileClient;
	char cmdOut[100] = { 0 };
	char cmdIn[100] = { 0 };
	char ip[20] = { 0 };
	byte buf[1000];
	int len;
	if (!m_socketClient.IsConnected())
	{
		res = Connect(m_ip, m_port, m_user, m_password);
		if (res != 0)
		{
			goto ExitLine;
		}
	}
	memset(cmdOut, 0, 100);
	sprintf_s(cmdOut, "PASV\r\n");
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "227", 3) != 0)
	{
		res = -1;
		goto ExitLine;
	}
	int ip1, ip2, ip3, ip4;
	int p1, p2;
	sscanf_s(cmdIn, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", &ip1, &ip2, &ip3, &ip4, &p1, &p2);
	memset(cmdOut, 0, 100);
	sprintf_s(cmdOut, "STOR %s\r\n", storPath);
	len = strlen(cmdOut);
	res = m_socketClient.Write((byte *)cmdOut, len);
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "150", 3) != 0)
	{
		res = -1;
		goto ExitLine;
	}
	sprintf(ip, "%d.%d.%d.%d", ip1, ip2, ip3, ip4);
	res = fileClient.Connect(ip, p1 * 256 + p2, 100, 100);
	if (res != 0)
	{
		goto ExitLine;
	}
	len = 1000;
	FILE* fp = fopen(filePath, "rb");
	if (fp == NULL)
	{
		res = -1;
		goto ExitLine;
	}
	//
	while (!feof(fp))
	{
		fread(buf, sizeof(buf), 1, fp);//要读取出的buf||读取的长度||要读取的次数||数据文件
		res = fileClient.Write(buf, len);
	}

	if (res != 0) {
		goto ExitLine;
	}
	fclose(fp);
	fileClient.Disconnect();
	len = 100;
	res = m_socketClient.Read((byte *)cmdIn, len);
	if (res != 0)
	{
		goto ExitLine;
	}
	if (memcmp(cmdIn, "226", 3) != 0)
	{
		res = -1;
		goto ExitLine;
	}
ExitLine:
	if (res != 0)
	{
		m_socketClient.Disconnect();
	}
	return res;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值