Linux下的FTP服务器

news2024/9/28 19:18:48

目录

总体功能

功能1----------------查看服务器端的文件列表信息

功能2 ---------------从服务器段下载文件到客户端

功能3 ---------------向服务器端上传文件

功能4----------------客户端退出,服务器继续等待链接


总体功能

功能1----------------查看服务器端的文件列表信息
input your choice: >>> list
***Makefile
***server
***server.c
.......
服务器目录已经接收完毕

服务器应答
目录清单已经成功发送

功能2 --------------从服务器段下载文件到客户端
input your choice: >>> get server.c
下载完毕
ls 客户端所在目录可以看到server.c的文件

服务器提示:
文件传送完成


功能3 ------------向服务器端上传文件
input your choice: >>> put hello.c(自己定义一个文件,输出hello world就行)
上传完毕

服务器提示:
接收文件成功
client client.c client.o Makefile server server.c server.o hello.c


功能4--------------客户端退出,服务器继续等待链接
input your choice: >>> quit

服务器端打印客户端退出

功能1----------------查看服务器端的文件列表信息

学习一下怎么使用C语言来读取目录下的文件

Linux C 读取文件夹下所有文件(包括子文件夹)的文件名 - Boblim - 博客园

通信结构体

typedef struct {
	int type;                        //命令
	char data[1024];               //文件具体内容
	char filename[256][256];     //文件名
	int len = 0;                //文件数量
}MSG;

我改进了一下查询当前目录下的文件

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
char filename[256][256];
int len = 0;
int trave_dir(char* path)
{
    DIR *d; //声明一个句柄
    struct dirent *file; //readdir函数的返回值就存放在这个结构体中
    struct stat sb;

    if(!(d = opendir(path)))
    {
        printf("error opendir %s!!!\n",path);
        return -1;
    }
    while((file = readdir(d)) != NULL)
    {
        //把当前目录.,上一级目录..及隐藏文件都去掉,避免死循环遍历目录
        if(strncmp(file->d_name, ".", 1) == 0)
            continue;
        strcpy(filename[len++], file->d_name); //保存遍历到的文件名
    }
    closedir(d);
    return 0;
}
int main()
{
    int i;
    trave_dir("./");
    for(i = 0; i < len; i++)
    {
        printf("%s\n", filename[i]);
    }
    return 0;
}

应用到服务器上

没有问题程序如下:

服务器

void do_list(int acceptfd, MSG *msg)
{
	DIR *d; //声明一个句柄
    struct dirent *file; //readdir函数的返回值就存放在这个结构体中
    struct stat sb;
	msg->len = 0;
	char * errmsg;
	char sql[512];
    if(!(d = opendir("./")))
    {
        printf("error opendir %s!!!\n","./");
        exit(1);
    }
    while((file = readdir(d)) != NULL)
    {
        //把当前目录.,上一级目录..及隐藏文件都去掉,避免死循环遍历目录
        if(strncmp(file->d_name, ".", 1) == 0)
            continue;
        strcpy(msg->filename[msg->len++], file->d_name); //保存遍历到的文件名
    }
	closedir(d);


	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		return ;
	}
	printf("目录清单已经成功发送");
}

 客户端

int do_list(int sockfd, MSG *msg)
{
	int i = 0;
	msg->type = L;
	msg->len = 0;
	if(send(sockfd, msg, sizeof(MSG),0) < 0)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}
    for(i = 0; i < msg->len; i++)
    {
        printf("%s\n", msg->filename[i]);
    }

	return 0;
}

功能2 ---------------从服务器端下载文件到客户端

        我想可以把服务器端的文件读到我们的通信结构体里,然后再客户端创建相同文件名的文件并把结构体里的内容写到文件中。

读取文件就要知道大小,所以写个计算文件大小的程序:

long back_size(const char *file)
{
	//打开需要计算大小的文件
	FILE *frp = fopen(file,"r");
	if(NULL == frp)
	{
		perror("fopen");
		exit(EXIT_FAILURE);
	}
	//将文件指针置于文件末尾
	fseek(frp,0,SEEK_END);
	//计算文件大小并返回
	return ftell(frp);
}

写了2个小时结果不行。之前有次成功了,但是乱码,哪里有问题。我再试试

真的吐了,查了好几天发现是读了两次,第二次读没有数据。

 服务器端:

void Download(int acceptfd, MSG *msg)
{
	FILE* fd = NULL;
	size_t num_read;
	fd = fopen(msg->filename[0], "r"); // 打开文件
	if(fd == NULL)
	{
		perror("fopen");
		msg->error = 1;
		if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
		{
			perror("fail to send");
			return ;
		}
		return;
	}
	msg->len = back_size(msg->filename[0]);
	num_read = fread(msg->data, 1, msg->len, fd); // 读文件内容
	printf("file is %d bit\n",msg->len);
	if (num_read < 0){ 
		printf("error in fread()\n");
		fclose(fd);
		return ;
	}
	msg->error = 0;
	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		msg->error = 1;
		if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
		{
			perror("fail to send");
			fclose(fd);
			return ;
		}
		fclose(fd);
		return ;
	}
	printf("%s已经成功发送\n",msg->filename[0]);
	fclose(fd);
}

客户端:

int Download(int sockfd, MSG *msg)
{
	msg->type = G;
	size_t num_write;
	FILE* fd = NULL;
	memset(msg->filename, 0,sizeof(msg->filename));
	memset(msg->data, 0,sizeof(msg->data));
	msg->len = 0;
	printf("please input filename\n");	
	scanf("%s", msg->filename[0]);
	getchar();
	printf("%s\n", msg->filename[0]);
	if(send(sockfd, msg, sizeof(MSG),0) < 0)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}
	if(msg->error == 0){
		fd = fopen(msg->filename[0], "w"); // 打开文件
		if(fd == NULL)
		{
			perror("fopen");
			return -1;
		}
		num_write = fwrite(msg->data, 1, msg->len, fd); //写文件内容
		if (num_write < 0){ 
			printf("error in fwrite()\n");
			fclose(fd);
			return -1;
		}
		printf("%s创建并写入完成\n",msg->filename[0]);
		fclose(fd);
		return 0;
	}else{
		printf(" server error\n");
		return -1;	
	}	
}

功能3 ---------------向服务器端上传文件

上传和下载功能发过来就行,应该没什么别的难点,我们先写一下试试

一顿优化忘记作记录了,直接给大家上成果吧

客户端:

int put(int sockfd, MSG *msg)
{
	msg->type = P;
	FILE* fd = NULL;
	size_t num_read;
	DIR *d; //声明一个句柄
    struct dirent *file; //readdir函数的返回值就存放在这个结构体中
    struct stat sb;
	msg->len = 0;
	int i;
	if(!(d = opendir("./")))
    {
        printf("error opendir %s!!!\n","./");
        exit(1);
    }
    while((file = readdir(d)) != NULL)
    {
        //把当前目录.,上一级目录..及隐藏文件都去掉,避免死循环遍历目录
        if(strncmp(file->d_name, ".", 1) == 0)
            continue;
        strcpy(msg->filename[msg->len++], file->d_name); //保存遍历到的文件名
    }
	closedir(d);
	printf("*** ");	
	for(i = 0; i < msg->len; i++)
    {
        printf(" %s ", msg->filename[i]);
    }
	printf(" ***\n");
	printf("filename>>>");
	scanf("%s",msg->filename[0]);
	getchar();
	fd = fopen(msg->filename[0], "r"); // 打开文件
	msg->len = back_size(msg->filename[0]);
	num_read = fread(msg->data, 1, msg->len, fd); // 读文件内容
	printf("file is %d bit\n",msg->len);
	if (num_read < 0){ 
		printf("error in fread()\n");
		fclose(fd);
		return -1;
	}
	msg->error = 0;
	if(send(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		fclose(fd);
		return -1;
	}
	printf("%s已经成功上传\n",msg->filename[0]);
	fclose(fd);
	return 0;
}

服务器:


void put(int acceptfd, MSG *msg)
{
	size_t num_write;
	FILE* fd = NULL;
	if(msg->error == 0){
		fd = fopen(msg->filename[0], "w"); // 打开文件
		if(fd == NULL)
		{
			perror("fopen");
			return ;
		}
		num_write = fwrite(msg->data, 1, msg->len, fd); //写文件内容
		if (num_write < 0){ 
			printf("error in fwrite()\n");
			fclose(fd);
			return ;
		}
		printf("%s创建并写入完成\n",msg->filename[0]);
		fclose(fd);
		return ;
	}else{
		printf(" server error\n");
		return ;	
	}	
}

 

 

功能4----------------客户端退出,服务器继续等待链接

退出就非常简单了,没什么好写的

所以在这块顺便介绍一下功能选择

int do_client(int acceptfd, sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd, &msg, sizeof(msg), 0) > 0)
	{
	  printf("type:%d\n", msg.type);
	   switch(msg.type)
	   {
	  	 case L:
			 do_list(acceptfd, &msg);
			 break;
		 case G:
			 Download(acceptfd, &msg);
			 break;
		 case P:
			 put(acceptfd, &msg);
			 break;
		 case Q:
			 //do_history(acceptfd, &msg, db);
			 
			//TODO
			break;
		 default:
			 printf("Invalid data msg.\n");
	   }

	}

	printf("client exit.\n");
	close(acceptfd);
	exit(0);

	return 0;
}

上面这段程序是服务器处理客户端信号的。这个sqlite就是数据库,用的话可以使用一下比如账号密码登录什么的,还可以来个访问记录查询

下面是客户端的一个简单的轮询程序:

	while(1)
	{
		printf("****************************************\n");
		printf("***输入 help 查看选项或者直接输入命令****\n");
		printf("****************************************\n");
		printf("input your choice: >>>");
		memset(buf, 0,sizeof(buf));
		scanf("%s",buf);
		getchar();
		if(strncmp(buf, "help", 4) == 0){
			printf("*************************************************************\n");
			printf("******** 输入 ********************功能***********************\n");
			printf("*******1:list:*********查看服务器所在目录的所有文件***********\n");
			printf("*******2:get filename:****下载服务器目录的文件****************\n");
			printf("*******3:put filename:******上传文件到服务器******************\n");
			printf("*******4:quit :****************关闭客户端 *******************\n");
			printf("*************************************************************\n");
			
		}else{
			//printf("input your choice: >>>");
			//scanf("%d", &n);
			//getchar();
			switch(buf[0])
			{
			case '1':
				do_list(sockfd, &msg);			
				break;
			case '2':
				Download(sockfd, &msg);
				break;
			case '3':
				put(sockfd, &msg);
				break;
			case '4':
				close(sockfd);
				exit(0);
				break;
			default:
				printf("Invalid data cmd.\n");
			}
		}
	}

---------------------------------------------------------------------------------------------------------------------------------

一直在拖着,这次终于给他完成了,管老师要的程序也没用上,思维不一样最后还是自己写的。正好今天除夕,上一年的活就要上一年完成哈哈。成熟的女生确实有吸引力,但是驾驭不了呀,搞得和旺财一样,算了吧。水泥封心三十岁再说。

这句话在书上看见,感觉有点道理。emmmm收藏一下吧,有点中二但是不无道理:

记住:别人恭维你时,偷偷高兴一下就可以了,但不可全当真,因为那十有八九是哄你的;别人批评你时,稍稍不开心一下就可以了,但不可生气,因为那十有八九是真的。我们可以哭,可以笑,但是不可以不坚强。有时候即便是莫名躺着中枪,也要姿势漂亮!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/174874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 SPI读写速度评估

目的&#xff1a;测试STM32H7 系列芯片的SPI读写速度。 测试环境&#xff1a;使用STM32H743, 逻辑分析仪&#xff0c; cubeIDE&#xff1b; 测试方法&#xff1a;使用了3种方法&#xff1a;软件IO模拟SPI&#xff0c; 软件控制CSMCU的SPI模块&#xff0c;完全使用SPI模块&…

Android 深入系统完全讲解(30)

下图是生命周期的说明图&#xff1a;如图可以看到&#xff1a; 当创建编解码器的时候处于未初始化状态。首先你需要调用 configure(…)方法让它处于 Configured 状态&#xff0c;然后调用 start()方法让其处于 Executing 状态。在 Executing 状态下&#xff0c;你就 可以使用…

LCHub:全新华为云Astro低代码平台,重塑企业数字化转型

为什么技术创新与业务突破难以挂钩?为什么寻求的卓越成果总难以实现?华为云Astro呈上解决之道,抓住重点一招致胜,让人人皆可高效开发,使创新按照你的步调进行。 华为云Astro低代码平台,重塑企业数字化转型 当企业倍受数字化升级困扰时,若仍延用传统编码开发,则会阻碍整…

Bootstrap4 之栅格系统

Bootstrap4 之栅格系统参考描述栅格系统&#xff08;网格系统&#xff09;原理容器.container 与视口宽度行列样式栅格等级网格宽度特点左对齐另起一行自适应布局对齐垂直对齐行列水平对齐排列响应式优先级偏移显隐参考 项目描述Bootstrap 入门经典珍妮弗、凯瑞恩Bootstrap 基…

内存屏障由来的理解和使用《编程高手必学的内存知识》学习笔记 Day 5

系列文章目录 这是本周期内系列打卡文章的所有文章的目录 《Go 并发数据结构和算法实践》学习笔记 Day 1《Go 并发数据结构和算法实践》学习笔记 Day 2《说透芯片》学习笔记 Day 3《深入浅出计算机组成原理》学习笔记 Day 4 文章目录系列文章目录前言一、概念理解&#xff08;…

JUC面试(六)——Java锁

Java锁 公平锁和非公平锁 概念 公平锁 是指多个线程按照申请锁的顺序来获取锁&#xff0c;类似于排队买饭&#xff0c;先来后到&#xff0c;先来先服务&#xff0c;就是公平的&#xff0c;也就是队列 非公平锁 是指多个线程获取锁的顺序&#xff0c;并不是按照申请锁的顺…

steam搬砖项目,每天1-2小时,月入1w+(内附教学资料)

steam搬砖就非常合适&#xff0c;最大的优点&#xff0c;操作性简单&#xff0c;0门槛也不用担心&#xff0c;一台电脑就能搞定。利用空余时间完成就可以 话不多说&#xff0c;先上目录&#xff0c;以便大家阅读~ 一.steam搬砖玩法 1、steam搬砖项目介绍 2、项目原理与产出…

ROS2机器人编程简述humble-第二章-Parameters .3.4

ROS2机器人编程简述humble-第二章-Launchers .3.3机器人程序通常需要配置各类参数&#xff0c;官网和一些书中都有介绍。概述ROS中的参数与各个节点相关。参数用于在启动时&#xff08;以及运行时&#xff09;配置节点&#xff0c;而无需更改代码。参数的生存期与节点的生存期相…

【区块链】深入剖析免费赚钱app的本质

你对免费赚钱软件好奇吗&#xff1f;前言一、揭开“免费赚钱app”神秘面纱1.常见的赚钱app2.app真的在做慈善吗&#xff1f;3.羊毛党的价值4.真正的游戏规则二、区块链1.哈希算法2.互联网挖矿三、深入探讨“区块链”的套路1.免费赚钱app真正的价值2.虚拟货币的本质3.虚拟货币泡…

Java设计模式-观察者模式/观察者模式适合那些场景?怎么使用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.7 观察者模式 6.7.1 定义 又称发布-订阅模式&#xff0c;定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象主体对象在状态变化…

[前端笔记——CSS] 9.CSS处理文件的标准流程+DOM

[前端笔记——CSS] 9.CSS处理文件的标准流程DOM1.CSS处理文件的标准流程2.关于DOM2.1 举个例子2.2 应用 CSS 到 DOM1.CSS处理文件的标准流程 当浏览器展示一个文件的时候&#xff0c;它必须兼顾文件的内容和文件的样式信息&#xff0c;CSS处理文件的标准流程如下&#xff1a; …

06_FreeRTOS临界区代码保护

目录 临界段代码保护简介 临界段代码保护函数介绍 任务级临界区函数详解 中断级临界区函数详解 临界段代码保护简介 什么是临界段:临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段 适用场合如: 1.外设:需严格按照时序初始化的外设:IIC、SPI等等 2.系统…

国家发明专利:基于改进型黏菌优化算法的业务资源分配方法

国家发明专利&#xff1a;基于改进型黏菌优化算法的业务资源分配方法 摘要 本发明公开了一种基于改进型黏菌优化算法的业务资源分配方法&#xff0c;其步骤包括&#xff1a;1将生产任务分成若干个环节&#xff0c;构建有向环状分配网络&#xff1b;2构建工厂参数矩阵并归一化&…

力扣98.验证二叉搜索树

文章目录力扣98.验证二叉搜索树题目描述算法思路代码实现力扣98.验证二叉搜索树 题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 …

领域驱动设计(DDD)的几种典型架构介绍

我们生活中都听说了DDD&#xff0c;也了解了DDD&#xff0c;那么怎么将一个新项目从头开始按照DDD的过程进行划分与架构设计呢&#xff1f; 一、专业术语 各种服务 IAAS&#xff1a;基础设施服务&#xff0c;Infrastructure-as-a-service PAAS&#xff1a;平台服务&#xff0c…

【Java寒假打卡】JavaWeb-Servlet基础

【Java寒假打卡】JavaWeb-Servlet基础介绍servlet快速入门servlet的执行过程servlet关系视图Servlet实现方式-继承HTTPSERVLETServlet的生命周期线程安全问题servlet的映射方式案例-多路径映射问题-指定名称的方式Servlet的创建时机默认Servlet介绍 servlet是运行在Java服务器…

免费分享一套 SpringBoot + Vue + ElementUI 的人力资源管理系统,挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的SpringBoot Vue ElementUI 的人力资源管理系统&#xff0c;分享下哈。 项目介绍 项目背景 人力资源管理是企业运营中必不可少的一环&#xff0c;它关系到企业的前途与发展。尤其对于中小微企业来说&#xff0c;对企业…

积分分离PID控制算法及仿真-1

在普通 PID 控制中引入积分环节的目的&#xff0c;主要是为了消除静差&#xff0c;提高控制精度。但在过程的启动、结束或大幅度增减设定时&#xff0c;短时间内系统输出有很大的偏差&#xff0c;会造成PID运算的积分积累&#xff0c;致使控制量超过执行机构可能允许的最大动作…

Vue项目基础环境搭建完整步骤

使用vue官方脚手架(vue-cli) vue-cli简介 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统&#xff0c;是一个官方脚手架&#xff0c;可以帮助我们快速创建vue项目工程目录&#xff0c;目前最新版本4.x。 Vue CLI 致力于将 Vue 生态中的工具基础标准化。它确保了各种构建…

第三天总结 之 商品管理界面的实现 之 模糊查询 与 数据在页面展示

商品管理界面的实现 模糊查询 第一步&#xff1a; 明确 查询时 需要的 条件 即sql语句中 where 后的条件 如 &#xff1a; 根据前端 可以发现 模糊查询可以通过 商品名称 日期 商品类型 来查询 所以在对应的controller层下的GoodsFuzzySelectServlet中首先要获取这三个属性的…