miniftp项目总结(五)

其他杂项命令的实现
cwd命令(改变当前工作目录):

static void do_cwd(session_t *sess)
{
	if (chdir(sess->arg) < 0) 
	{
		ftp_reply(sess, FTP_NOPERM, "Failed to change directory.");
		return;
	}

	ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}

系统函数:
头文件:#include<unistd.h>
定义函数:in chdir(const char* path)
函数说明:chdir()函数用来将当前的工作目录改变成以参数path所指的目录
返回值:成功返回0,失败返回-1,errno为错误代码

cdup命令(返回上一级目录):

static void do_cdup(session_t *sess)
{
	if (chdir("..") < 0) 
	{
		ftp_reply(sess, FTP_NOPERM, "Failed to change directory.");
		return;
	}

	ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}

mkd命令(创建一个文件夹):

static void do_mkd(session_t *sess)
{
	if(mkdir(sess->arg, 0755) < 0)
	{
		if(errno == EEXIST) 
		{
			// 550 Create directory operation failed.
			ftp_reply(sess, FTP_FILEFAIL, "Create directory operation failed.");
		}
		else
		{
			//550 Permission denied.
			ftp_reply(sess, FTP_NOPERM, "Permission denied.");
		}
		return;
	}
	// 257 "/home/51cc/C12/Project/Test1" created
	char buf[MAX_BUFFER_SIZE] = {0};
	if(getcwd(buf, MAX_BUFFER_SIZE) == NULL)
	{
		//ftp_rely();
	}
	else
	{
		//printf("buf = %s\n",buf);
		sprintf(buf, "\"%s\%s\" created",buf,sess->arg);
		ftp_reply(sess, FTP_MKDIROK, buf);
	}
}

系统函数

int mkdir(const char *pathname, mode_t mode);
今天使用linux的mkdir创建目录。
函数说明:
mkdir()函数以mode方式创建一个以参数pathname命名的目录,mode定义新创建目录的权限。
返回值:
若目录创建成功,则返回0;否则返回-1,并将错误记录到全局变量errno中。

头文件:#include <unistd.h>
定义函数:char * getcwd(char * buf, size_t size);
函数说明:getcwd()会将当前的工作目录绝对路径复制到参数buf 所指的内存空间,参数size 为buf 的空间大小。
返回值:执行成功则将结果复制到参数buf 所指的内存空间, 或是返回自动配置的字符串指针. 失败返回NULL,错误代码存于errno.

rmd命令(删除目录):

static void do_rmd(session_t *sess)
{
	if(rmdir(sess->arg) < 0)
	{
		//550 Remove directory operation failed.
		ftp_reply(sess, FTP_NOPERM, "Remove directory operation failed.");
		return;
	}
	//250 Remove directory operation successful.
	ftp_reply(sess, FTP_RMDIROK, "Remove directory operation successful.");
}

系统函数:
函数:rmdir
函数原型:int rmdir(const char* dirname);
函数功能:删除一个目录,若成功返回0,否则返回-1
dele命令;

static void do_dele(session_t *sess)
{
	if(unlink(sess->arg) < 0)
	{
		//550 Delete operation failed.
		ftp_reply(sess, FTP_NOPERM, "Delete operation failed.");
		return;
	}

	// 250 Delete operation successful.
	ftp_reply(sess, FTP_DELEOK, "Delete operation successful.");

}

size命令:

static void do_size(session_t *sess)
{
	struct stat sbuf;
	if(stat(sess->arg, &sbuf) < 0)
	{
		// 550 Could not get file size.
		ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
		return;
	}

	// 213 6
	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "%d", sbuf.st_size);
	ftp_reply(sess, FTP_STATFILE_OK, buf);
}

系统函数:
头文件:#include<sys/stat.h> #include<unistd.h>
定义函数:int stat(const char* file_name, struct stat* buf);
函数说明:stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中
返回值:执行成功返回0,失败返回-1,错误代码存在于errno中

rnfr命令和rnto命令(实现对文件的重命名):

static void do_rnfr(session_t *sess)
{
	sess->rnfr_name = (char*)malloc(strlen(sess->arg)+1);
	assert(sess->rnfr_name != NULL);

	strcpy(sess->rnfr_name, sess->arg);
	//350 Ready for RNTO.
	ftp_reply(sess, FTP_RNFROK, "Ready for RNTO.");
}
static void do_rnto(session_t *sess)
{
	if(sess->rnfr_name == 0)
	{
		 //503 RNFR required first.
		 ftp_reply(sess, FTP_NEEDRNFR, "RNFR required first.");
		 return;
	}
	int ret = rename(sess->rnfr_name, sess->arg);

	free(sess->rnfr_name);
	sess->rnfr_name = 0;
	
	if(ret < 0)
	{
		// 550 Rename failed.
		ftp_reply(sess, FTP_FILEFAIL, "Rename failed.");
		return;
	}
	//250 Rename successful.
	ftp_reply(sess, FTP_RENAMEOK, "Rename successful.");
}

上传stor实现

static void do_retr(session_t *sess)
{
	//1建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	struct stat sbuf;
	stat(sess->arg, &sbuf);
	
	char buf[MAX_BUFFER_SIZE] = {0};
	//2判断传输模式
	if(sess->is_ascii)
		sprintf(buf,  "Opening ASCII mode data connection for %s (%ld bytes)", 
			sess->arg, sbuf.st_size);//Ascii
	else
		sprintf(buf, "Opening BINARY mode data connection for %s (%ld bytes)", 
			sess->arg, sbuf.st_size);

	//3回复150
	ftp_reply(sess, FTP_DATACONN, buf);

	//4传输数据
	int fd = open(sess->arg, O_RDONLY);
	if(fd < 0)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}

	long long file_total_size = sbuf.st_size;
	long long offset = sess->restart_pos;
	sess->restart_pos = 0;

	if(offset >= file_total_size)
	{
		ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
		return;
	}

	unsigned long left_bytes = file_total_size - offset;

	lseek(fd, offset, SEEK_SET);

	int count = 0;
	int flag = 0, ret = 0;
	while(left_bytes > 0)
	{
		memset(buf, 0, sizeof(buf));
		count = read(fd, buf, MAX_BUFFER_SIZE);
		if(count < 0)
		{
			flag = 1;
		}
		ret = write(sess->data_fd, buf, count);
		if(ret < 0)
		{
			flag = 2;
		}
		else if(ret != count)
		{
			flag = 3;
		}
		left_bytes -= count;
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
	//5回复226
	if(flag == 0)
		ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
	else if(flag == 1)
		ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
	else if(flag == 2)
		ftp_reply(sess, FTP_BADSENDFILE, "Failure writing from net file.");
	else if(flag == 3)
		ftp_reply(sess, FTP_BADSENDNET, "Failure writting to network stream.");
}

系统函数:
头文件:#include <sys/types.h> #include<unistd.h>
定义函数:off_t lseek(int fildes, off_t offset, int whence);
函数说明:每一个已经打开的文件都有一个读写位置,当打开文件时通常其读写位置是指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或是write()时,读写位置会随之增加,lseek()便是用来控制该文件的读写位置,参数fildes为已打开的文件描述词,参数offset为根据参数wherece来移动读写位置位移数。
参数whence为下列其中一种:

  • SEEK_SET 参数offset 即为新的读写位置.

  • SEEK_CUR 以目前的读写位置往后增加offset 个位移量.

  • SEEK_END 将读写位置指向文件尾后再增加offset 个位移量.

PS:当whence 值为SEEK_CUR 或 SEEK_END 时, 参数offet 允许负值的出现

下面是一些特别的使用方式:

  • 将读写位置移到文件开头时:lseek(int fildes,0,SEEK_SET);
  • 将读写位置移到文件尾时:lseek(int fildes,0,SEEK_END);
  • 取得文件目前的位置时:lseek(int fildes,0,SEEK_CUR);

返回值:当调用成功时则返回目前读写位置,也就是距离文件开头多少个字节。若有错误则返回-1,error会存放错误代码。

头文件:#include<unistd.h>
定义函数:ssize_t read(int fd, void* buf, size_t count);
函数说明:read函数会把参数fd所指的文件传送count个字节到buf指针所指的内存中。若参数count为0,则read函数不会有作用并返回0。返回值为实际读取到的字节数,如果返回0表示已经到达文件尾或是无可读取的数据,此外文件读写位置会随读到的字节移动。
返回值:当有错误发生的时候则返回-1,错误代码存入error中,而文件读写位置都无法预测
错误代码:

  • EINTR 此调用被信号所中断.

  • EAGAIN 当使用不可阻断I/O 时(O_NONBLOCK), 若无数据可读取则返回此值.

  • EBADF 参数fd 非有效的文件描述词, 或该文件已关闭

头文件:#include <unistd.h>

定义函数:ssize_t write (int fd, const void * buf, size_t count);

函数说明:write()会把参数buf 所指的内存写入count 个字节到参数fd 所指的文件内. 当然, 文件读写位置也会随之移动.

返回值:如果顺利write()会返回实际写入的字节数. 当有错误发生时则返回-1, 错误代码存入errno 中.

错误代码:

  • EINTR 此调用被信号所中断.
  • EAGAIN 当使用不可阻断I/O 时 (O_NONBLOCK), 若无数据可读取则返回此值.
  • EADF 参数fd 非有效的文件描述词, 或该文件已关闭.

下载retr实现

static void do_stor(session_t *sess)
{
	
	//1建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	//150 Ok to send data.
	ftp_reply(sess, FTP_DATACONN, "Ok to send data.");

	//4传输数据
	int fd = open(sess->arg, O_CREAT|O_WRONLY, 0755);
	if(fd < 0)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}


	unsigned long offset = sess->restart_pos;
	sess->restart_pos = 0;
	lseek(fd, offset, SEEK_SET);

	char buf[MAX_BUFFER_SIZE];
	int count = 0;
	int flag = 0, ret = 0;
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		count = read(sess->data_fd, buf, MAX_BUFFER_SIZE);
		if(count == 0)
		{
			break;
		}
		else if(count < 0)
		{
			flag = 1;
			break;
		}
		ret = write(fd, buf, count);
		if(ret < 0)
		{
			flag = 2;
			break;
		}
		else if(ret != count)
		{
			flag = 3;
			break;
		}
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
	//5回复226
	if(flag == 0)
		ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
	else if(flag == 1)
		ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
	else if(flag == 2)
		ftp_reply(sess, FTP_BADSENDFILE, "Failure writing from net file.");
	else if(flag == 3)
		ftp_reply(sess, FTP_BADSENDNET, "Failure writting to network stream.");
}

在这里插入图片描述PS:上传和下载的实质就是从一端读文件,然后写到另一端的过程。
续传和续载实现
记住传输的位置是关键!
续传命令rest:

static void do_rest(session_t *sess)
{
	sess->restart_pos = str_to_longlong(sess->arg);
	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "Restart position accepted (%u).", sess->restart_pos);
	// 350 Restart position accepted (4161536).
	ftp_reply(sess, FTP_RESTOK, buf);
}

配置项解析
做三张表,然后去查表解析config文件。

#include"parseconf.h"

static struct parseconf_bool_setting
{
	const char *p_setting_name;
	int *p_variable;
}
parseconf_bool_array[] =
{
	{ "pasv_enable", &tunable_pasv_enable },
	{ "port_enable", &tunable_port_enable },
	{ NULL, NULL }
};

static struct parseconf_uint_setting
{
	const char *p_setting_name;
	unsigned int *p_variable;
}
parseconf_uint_array[] =
{
	{ "listen_port", &tunable_listen_port },
	{ "max_clients", &tunable_max_clients },
	{ "max_per_ip", &tunable_max_per_ip },
	{ "accept_timeout", &tunable_accept_timeout },
	{ "connect_timeout", &tunable_connect_timeout },
	{ "idle_session_timeout", &tunable_idle_session_timeout },
	{ "data_connection_timeout", &tunable_data_connection_timeout },
	{ "local_umask", &tunable_local_umask },
	{ "upload_max_rate", &tunable_upload_max_rate },
	{ "download_max_rate", &tunable_download_max_rate },
	{ NULL, NULL }
};

static struct parseconf_str_setting
{
	const char *p_setting_name;
	const char **p_variable;//字符串本来就是char*,指向字符串要是char**
}
parseconf_str_array[] =
{
	{ "listen_address", &tunable_listen_address },
	{ NULL, NULL }
};

void parseconf_load_file(const char *path)//加载配置文件
{
	FILE *fp = fopen(path, "r");
	if(NULL == fp)
		ERR_EXIT("fopen");

	char setting_line[1024] = {0};
	while(fgets(setting_line, sizeof(setting_line), fp) != NULL)
	{
		if(strlen(setting_line) == 0
			|| setting_line[0] == '#')//读命令失败或者开头遇到'#'跳过
			continue;

		//key=value
		str_trim_crlf(setting_line);
		parseconf_load_setting(setting_line);//设置命令
		memset(setting_line, 0, sizeof(setting_line));
	}
	fclose(fp);
}

void parseconf_load_setting(const char *setting)
{
	char key[128] = {0};
	char value[128] = {0};
	str_split(setting, key, value, '=');

	const struct parseconf_str_setting *p_str_setting = parseconf_str_array;
	while (p_str_setting->p_setting_name != NULL)
	{
		if (strcmp(key, p_str_setting->p_setting_name) == 0)
		{
			const char **p_cur_setting = p_str_setting->p_variable;
			if (*p_cur_setting)
				free((char*)*p_cur_setting);

			*p_cur_setting = strdup(value);//复制字符串,返回char*
			return;
		}
		p_str_setting++;
	}

	const struct parseconf_bool_setting *p_bool_setting = parseconf_bool_array;
	while (p_bool_setting->p_setting_name != NULL)
	{
		if (strcmp(key, p_bool_setting->p_setting_name) == 0)
		{
			//str_upper(value);
			if (strcmp(value, "YES") == 0)
			{
				*(p_bool_setting->p_variable) = 1;
			}
			else if (strcmp(value, "NO") == 0)
			{
				*(p_bool_setting->p_variable) = 0;
			}
			else
			{
				fprintf(stderr, "bad bool value in config file for: %s\n", key);
				exit(EXIT_FAILURE);
			}
			return;
		}
		p_bool_setting++;
	}
	const struct parseconf_uint_setting *p_uint_setting = parseconf_uint_array;
	while (p_uint_setting->p_setting_name != NULL)
	{
		if (strcmp(key, p_uint_setting->p_setting_name) == 0)
		{
			if (value[0] == '0')
			{
				//*(p_uint_setting->p_variable) = str_octal_to_uint(value);
			}
			else
				*(p_uint_setting->p_variable) = atoi(value);
			return;
		}
		p_uint_setting++;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FTP服务器,又称为File Transfer Protocol Server,是网络上的一种文件传输协议,主要用于将文件从一个网络地址传输到另一个网络地址。其中,FTP Server指的是运行FTP协议的服务器,它负责接受来自客户端的请求,并按照FTP协议的规定进行响应。 FTP服务器有着广泛的应用,其中最常见的是Web服务器中的FTP服务器。FTP服务器可以提供文件上传和下载服务,主要用于网站的文件发布、备份和存储等。此外,FTP服务器还可以用于文件的共享、远程控制、安装程序等多种应用场景。 在工作中,我们可能需要使用一些迷你FTP服务器,以方便我们进行一些文件的传输和共享。所谓迷你FTP服务器,指的是一些小型的FTP服务器软件,它们具有简单易用的操作界面和快速高效的文件传输速度,适用于小型团队和个人使用。 与传统的FTP服务器相比,迷你FTP服务器不仅功能强大,而且占用资源较少,使得它们得以在性能不足的电脑上运行。此外,迷你FTP服务器还可以方便地进行设置和配置,以满足用户的个性化需求。 总的来说,迷你FTP服务器是一种实用的工具,它为用户提供了一种快速、安全、高效的文件传输和共享方式,值得我们在工作和生活中多加利用。 ### 回答2: FTP服务器也就是File Transfer Protocol服务器,是一种可以实现文件传输的服务器,可以提供文件上传、下载和管理等功能。FTP服务器分为大型和迷你类两种,其中迷你FTP服务器由于其轻量、易用、便携等特性,成为了广泛应用的一种FTP服务器。 迷你FTP服务器具有简单易用的特点,可以轻松搭建FTP服务器并实现文件的上传和下载。其操作简单,用户只需创建FTP用户账号,指定其访问目录,就可以实现上传下载管理等功能。同时,迷你FTP服务器的便携性也是其具有优秀特性之一,它可以在不同计算机上使用,只需将软件安装在u盘或移动硬盘中,便携性大大提高了其使用的便利性。 迷你FTP服务器还具有可扩展性和高度可定制性。FTP服务器可以通过插件等方式来扩展其功能,并可以通过调整参数和配置来实现自定义的功能和使用方式。这样,用户就可以根据自己的需求进行定制化,提高FTP服务器的作用和效率。 总而言之,迷你FTP服务器是一种轻量级FTP服务器,具有简单易用、便携性、可扩展性和高度可定制性等优点。对于需要上传、下载和管理文件的小型组织或个人用户而言,迷你FTP服务器是一个不错的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值