Linux下的FTP服务器

该文描述了一个使用C语言编写的文件传输服务,包括查看服务器端文件列表、从服务器下载文件、向服务器上传文件以及客户端退出的功能。通过结构体进行通信,实现了基本的文件操作,如读取目录、计算文件大小等。客户端和服务器端分别有相应的处理函数来执行这些功能。
摘要由CSDN通过智能技术生成

目录

总体功能

功能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收藏一下吧,有点中二但是不无道理:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇努力学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值