Linux下的FTP服务器

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

总体功能

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

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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux服务器