基础知识
FTP云盘使用C语言进行编写,在ubuntu环境下运行,相关知识有Linux进程,Linux网络编程、Linux文件编程、结构体、字符串等等。

实现思路
服务器

与客户端建立socket连接
读取客户端发送的命令
进行相关的命令识别和处理,将执行命令所获得的数据发送给客户端。


客户端
与服务器建立socket连接
获取用户输入的命令,进行相关的命令识别和处理
客户端会进行系统调用执行带l前缀命令,不带l前缀的命令将会发送给服务器
对服务器发送的数据进行处理


实现功能
服务器功能

监听客户端的连接,允许多个客户端连接
接收和区别不同客户端发来的命令,对相关命令进行处理


客户端功能
输入命令“lls”查看客户端当前工作目录下的文件列表

输入命令“lcd 路径名”修改当前工作目录,支持相对路径和绝对路径

输入命令“lpwd”查看当前工作目录的绝对路径名

输入命令“lquit”退出与服务器的连接

输入命令“ls”查看服务器当前工作目录下的文件列表

输入命令“cd”修改服务器当前工作目录,支持相对路径和绝对路径

输入命令“pwd”查看服务器当前工作目录的绝对路径名

输入命令“get 文件名”获取服务器服务器的当前目录下的文件

输入命令“put 文件名”将文件存放到服务器的当前目录下

运行展示
由于我只有两台Linux虚拟机,所以用一台作为服务器,一台作为客户端,客户端那台开设多个终端连接服务器

服务器



客户端
ls命令


cd命令和pwd命令



lls命令



lcd命令和lpwd命令



get命令



put命令


lquit命令



完整代码
服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
 
#define LS 1
#define CD 2
#define PWD 3
#define PUT 5
#define GET 15
 
struct Msg 
{
	int type;
	char cmd[50];
	char data[5120];
};
 
int cmd_distinguish(char *cmd)
{
	int ret;
	cmd = strtok(cmd,"\n");
	if(!strcmp("ls",cmd)){
		ret = LS;
	}else if(strstr(cmd,"cd") != NULL){
		ret = CD;
	}else if(strstr(cmd,"get") != NULL){
		ret = GET;
	}else if(strstr(cmd,"put") != NULL){
		ret = PUT;
	}else if(!strcmp("pwd",cmd)){
		ret = PWD;
	}else{
		ret = 0;
	}
	return ret;
}
 
 
char *str_decompose(char *cmd)
{
	strtok(cmd," ");
	cmd = strtok(NULL," ");
	return cmd;
}
 
void cmd_handle(int ret,struct Msg msg,int a_fd)
{
 
	FILE *fp = NULL;
	int n_fread;
	char file_path[20] = "./";
	switch(ret){
		case PWD:
			fp = popen("pwd","r");
			memset(msg.data,0,sizeof(msg.data));
			n_fread = fread(msg.data,1,sizeof(msg.data),fp);
			msg.type = 0;
			write(a_fd,&msg,sizeof(msg));
			fflush(stdout);
			break;
		case LS:
			fp = popen("ls","r");
			memset(msg.data,0,sizeof(msg.data));
			n_fread = fread(msg.data,1,sizeof(msg.data),fp);
			msg.type = 0;
			write(a_fd,&msg,sizeof(msg));
			fflush(stdout);
			break;
		case CD:
			if(chdir(str_decompose(msg.cmd)) <0){
				perror("chdir");
			}
			break;
		case GET:
			strcat(file_path,str_decompose(msg.cmd));
			if(access(file_path,F_OK) < 0){
				perror("access");
				memset(msg.data,0,sizeof(msg.data));
				msg.type = 0;
				strcpy(msg.data,"Target file does not exist");
				write(a_fd,&msg,sizeof(msg));
			}else if(access(file_path,R_OK) < 0){
				memset(msg.data,0,sizeof(msg.data));
				msg.type = 0;
				strcpy(msg.data,"The target file does not have read permission");
				write(a_fd,&msg,sizeof(msg));
			}else{
				int file_fd = open(file_path,O_RDONLY,0600);
				if(file_fd < 0){
					perror("open");
				}
				int file_size = lseek(file_fd,0,SEEK_END);
				if(file_size < 0){
					perror("lseek");
				}
				lseek(file_fd,0,SEEK_SET);
				memset(msg.data,0,sizeof(msg.data));
				msg.type = GET;
				if(read(file_fd,msg.data,file_size) < 0){
					perror("read");
				}
				close(file_fd);
				write(a_fd,&msg,sizeof(msg));
			}
			break;
		case PUT:
			strcat(file_path,str_decompose(msg.cmd));
			int file_fd = open(file_path,O_RDWR|O_CREAT,0666);
			if(file_fd < 0){
				perror("open");
			}
			if(write(file_fd,msg.data,strlen(msg.data)) < 0){
				perror("write");
			}
			close(file_fd);
			break;
	}
}
 
 
 
int main(int argc,char *argv[])
{
 
	struct sockaddr_in so_addr;
	struct sockaddr_in cl_addr;
 
	if(argc != 3){
		printf("Insufficient or inconsistent parameters\n");
		exit(-1);
	}
 
	memset(&so_addr,0,sizeof(struct sockaddr_in));
	memset(&cl_addr,0,sizeof(struct sockaddr_in));
 
	int s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		printf("socket is failusr\n");
		perror("socket");
		exit(-1);
	}
 
	so_addr.sin_family = AF_INET;
	so_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&so_addr.sin_addr);
 
	int bd = bind(s_fd,(struct sockaddr *)&so_addr,sizeof(struct sockaddr_in));
	if(bd == -1){
		printf("bind is failusr\n");
		perror("bind");
		exit(-1);
	}
 
	int ln = listen(s_fd,10);
	if(ln == -1){
		printf("listen is failusr\n");
		perror("listen");
		exit(-1);
	}
 
	int a_fd;
	int mark = 0;
	int clean = sizeof(struct sockaddr_in);
	pid_t pid;
	while(1){
		a_fd = accept(s_fd,(struct sockaddr *)&cl_addr,&clean);
		if(a_fd == -1){
			printf("accept is failusr\n");
			perror("accept:");
			exit(-1);
		}
		mark++;
		printf("get No.%d connect: %s\n",mark,inet_ntoa(cl_addr.sin_addr));
 
		pid = fork();
		if(pid < 0){		
			perror("fork");
			exit(-1);
		}
		else if(pid == 0){
			char *fp = NULL;
			struct Msg msg;
			int ret = 0;
			int c_read;
			while(1){
				memset(msg.cmd,0,sizeof(msg.cmd));
				c_read = read(a_fd,&msg,sizeof(msg));
				if(c_read <= 0){
					perror("read");
					break;
				}else{
					printf("get size is %d's cmd from NO.%d client:%s\n",c_read,mark,msg.cmd);
					ret = cmd_distinguish(msg.cmd);
					cmd_handle(ret,msg,a_fd);
 
				}
 
			}
		}
		close(a_fd);
	}
 
	close(s_fd);
	return 0;
}

客户端
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
 
struct Msg
{
	int type;
	char cmd[50];
	char data[5120];
};
 
#define LLS 1
#define LCD 2
#define LPWD 3
#define CD 4
#define LQUIT 7
#define PUT 8
#define CMD 9
#define QUIT 12
#define LS 13
#define PWD 14
#define GET 15
 
 
int cmd_distinguish(char *cmd)
{
	int ret;
	cmd = strtok(cmd,"\n");
	if(!strcmp("lls",cmd)){
		ret = LLS;
	}else if(strstr(cmd,"lcd") != NULL){
		ret = LCD;
	}else if(!strcmp("lpwd",cmd)){
		ret = LPWD;
	}else if(!strcmp("ls",cmd)){
		ret = LS;
	}else if(!strcmp("lquit",cmd)){
		ret = LQUIT;
	}else if(strstr(cmd,"cd") != NULL){
		ret = CD;
	}else if(strstr(cmd,"get") != NULL){
		ret = GET;
	}else if(strstr(cmd,"put") != NULL){
		ret = PUT;
	}else if(!strcmp("pwd",cmd)){
		ret = PWD;
	}else{
		ret = 0;
	}
	return ret;
}
 
 
char *str_decompose(char *cmd)
{
	strtok(cmd," ");
	cmd = strtok(NULL," ");
	return cmd;
}
 
 
int cmd_handle(int ret,struct Msg msg,int s_fd)
{
 
	FILE *fp = NULL;
	int n_fread;
	char file_path[20] = "./";
	char tmp_cmd[50];
	switch(ret){
		case LPWD:
			printf("--------------------------------------\n");
			printf("\n");
			system("pwd");
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
		case LLS:
			printf("--------------------------------------\n");
			printf("\n");
			system("ls");
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
		case LCD:
			if(chdir(str_decompose(msg.cmd)) < 0){
				perror("chdir");
			}                       
			printf("\n");
			break;
		case LQUIT:
			printf("--------------------------------------\n");
			printf("\n");
			printf("client is quit\n");
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			exit(0);
			break;
		case LS:	
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));
			break;
		case PWD:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));
			break;
		case CD:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));
			break;
		case GET:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));
			break;
		case PUT:
			memset(tmp_cmd,0,sizeof(tmp_cmd));
			strcpy(tmp_cmd,msg.cmd);
			strcat(file_path,str_decompose(msg.cmd));
			memset(msg.cmd,0,sizeof(msg.cmd));
			strcpy(msg.cmd,tmp_cmd);
			if(access(file_path,F_OK) < 0){
				printf("--------------------------------------\n");
				printf("\n");
				printf("Target file does not exist\n");
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
 
			}else if(access(file_path,R_OK) < 0){
				printf("--------------------------------------\n");
				printf("\n");
				printf("The target file does not have read permission\n");   	
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
			}else{
				int pfile_fd = open(file_path,O_RDONLY,0666);
				if(pfile_fd < 0){
					perror("open");
				}
				int file_size = lseek(pfile_fd,0,SEEK_END);
				if(file_size < 0){
					perror("lseek");
				}
				lseek(pfile_fd,0,SEEK_SET);
				memset(msg.data,0,sizeof(msg.data));
				if(read(pfile_fd,msg.data,file_size) < 0){
					perror("read");
				}
				close(pfile_fd);
				write(s_fd,&msg,sizeof(msg));
				printf("--------------------------------------\n");
				printf("\n");
				printf("Target file is on server\n");   	
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
			}
			break;
		default:
			printf("--------------------------------------\n");
			printf("\n");
			printf("Input error, please re-enter\n");   	
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
 
	}
	return ret;
}
 
void from_sorket_data_handle(struct Msg msg,int s_fd)
{
	struct Msg sorket_msg;
	int n_read;
	int file_fd;
	char file_path[20] = "./";
 
	n_read = read(s_fd,&sorket_msg,sizeof(sorket_msg));
	if(n_read == -1){
		perror("read sorket");
		exit(-1);
	}else if(sorket_msg.type == GET){
		strcat(file_path,str_decompose(msg.cmd));
		file_fd = open(file_path,O_RDWR|O_CREAT,0666);
		write(file_fd,sorket_msg.data,strlen(sorket_msg.data));
		fflush(stdout);
		close(file_fd);
		printf("--------------------------------------\n");
		printf("\n");
		printf("%s\n","Get target file from server\n");
		printf("--------------------------------------\n");
		printf("\n");
	}else{
		printf("--------------------------------------\n");
		printf("\n");
		printf("%s\n",sorket_msg.data);
		printf("\n");
		printf("--------------------------------------\n");
		printf("\n");
	}
 
}
 
int main(int argc,char *argv[])
{
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));
 
	int s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		printf("socket is failusr\n");
		perror("socket");
		exit(-1);
	}
 
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);
 
	if(connect(s_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
		perror("connect");
		exit(-1);
	}
 
	int n_read;
	struct Msg msg;
	int ret = 0;
	int mark = 0;
	int tmp = 0;
	while(1){
		memset(msg.cmd,0,sizeof(msg.cmd));
		memset(msg.data,0,sizeof(msg.data));
 
		fgets(msg.cmd,sizeof(msg.cmd),stdin);
		ret = cmd_distinguish(msg.cmd);
		tmp = cmd_handle(ret,msg,s_fd);
		if(tmp > CMD){
			from_sorket_data_handle(msg,s_fd);
		}
 
		fflush(stdout);
 
	}	
	close(s_fd);
	return 0;
}
 
相关调用函数
socket():创建一个网络通信端点

原型:int socket(int domain, int type, int protocol);

返回值:调用成功返回套接字描述符,调用失败返回-1

参数:

int domain:选择协议族



int type:选择套接字的类型



int protocol:选择协议,通常为0, 表示type参数对应的默认协议



atoi():将字符串转变成整型数据
原型:int atoi(const char *nptr);

返回值:返回转换之后得到的int型数据

参数:

const char *nptr:需要进行转换的字符串

htons():将16位无符号数从主机字节序转换成网络字节序
原型:
uint16_t htons(uint16_t hostshort);

返回值:转换之后的TCP/IP网络字节序

参数:

uint16_t hostshort:16位无符号整数
inet_aton():将IP地址从字符串类型转换成网络字节序
原型:
int inet_aton(const char _cp, struct in_addr _inp);

返回值:调用成功返回非0,调用失败返回0

参数:

const char _cp:字符串类型的IP地址
struct in_addr _inp:sockaddr_in类型结构体里的in_addr类型结构体地址



inet_ntoa():将IP地址从网络字节序转换成字符串类型
原型:
char *inet_ntoa(struct in_addr in);

返回值:指向字符串类型IP地址的字符串指针

参数:

struct in_addr in:sockaddr_in类型结构体里的in_addr类型结构体



bind():为套接字添加信息(协议族、IP地址、端口号)
原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

返回值:调用成功返回0,调用失败返回-1

参数:

int sockfd:套接字描述符
const struct sockaddr *addr:sockaddr类型结构体地址,在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换



socklen_t addrlen:参数二的内存空间大小

listen():监听客户端的连接
原型:
int listen(int sockfd, int backlog);

返回值:调用成功返回0,调用失败返回-1

参数:

int sockfd:套接字描述符
int backlog:指定在请求队列中允许的最大连接数

accept():等待客户端的连接,当没有客户端连接时,accept()会阻塞等待
原型:
int accept(int sockfd, struct sockaddr _addr, socklen_t _addrlen);

返回值:调用成功返回0,调用失败返回-1

参数:

int sockfd:套接字描述符
const struct sockaddr _addr:sockaddr类型结构体地址
socklen_t _addrlen:参数二内存空间大小的值的地址



connect():客户端连接服务器
原型:
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

返回值:调用成功返回套接字描述符,调用失败返回-1

参数:

int sockfd:套接字描述符
const struct sockaddr _addr:sockaddr类型结构体地址,在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换



socklen_t _addrlen:参数二内存空间大小
fork():创建一个子进程
原型:
pid_t fork(void)

返回值:调用成功返回两次,0或非负整数,调用失败返回-1

0: 子进程
非负整数(子进程的pid):父进程
open():打开一个文件
原型:
int open(const char *pathname, int flags, mode_t mode);

返回值:调用成功返回一个文件描述符,调用失败返回-1

参数:

const char _pathname:文件名(相对路径)
int flags:打开文件的方式及附带操作



mode_t mode:(可选参数)文件访问的权限
read():从某个文件中读取数据,没读到数据时会阻塞等待
原型:
int read(int fd, void _buf, size_t count);

返回值:读入成功时,返回读取的字节数(如果读文件开始前光标到达文件尾端则返回0)读入失败时,返回-1

参数:

fd:文件描述符
void _buf:读取的数据存放位置
size_t count:读取的字节数
write():向某个文件写入数据
原型:
int write(int fd, const void _buf, size_t count);

返回值:写入成功时,返回写入的字节数,写入数据为空返回0,写入失败返回-1

参数:

fd:文件描述符
void _buf:写入文件的数据
size_t count:写入文件的字节数
_*lseek():移动文件的光标,通常用lseek(fd,0,SEEK_END)获取文件长度
原型:__int lseek(int fd, off_t offset, int whence);

返回值:调用成功返回光标偏移字节,调用失败返回-1

参数:

fd:文件描述符
off_t offset:偏移字节,0为不偏移,-a为往前偏移a个字节,b为往前偏移b个字节
int whence:光标位置



close(),一般不用主动关闭文件,在进程终止时,内核自动关闭该进程打开的文件
原型:
int close(int fd)

返回值:调用成功返回0,调用失败时返回-1

参数:

fd:文件描述符

strtok():字符串分割函数
原型:
char _strtok(char _str, const char *delim);

返回值:

第一次传入字符串和分隔符时,返回分割的字符串
第二次传入NULL和分隔符时,返回分隔的字符串
直到最后一次传入NULL和空字符串时,返回最后一个分割的字符串
其中第一次字符串被分割后,值和大小为第一次分割出来的字符串
参数:

char _str:字符串
const char _delim:分隔符

strcmp():字符串比较函数
原型:
int strcmp(const char _s1, const char _s2);

返回值:如果两个字符串相等返回0,两个字符串不相等返回其他值

参数:

const char _s1:字符串1
const char _s2:字符串2

strstr():字符串包含函数
原型:
char _strstr(const char _s1, const char *s2);

返回值:如果字符串s1包含字符串s2,返回包含字符及后面字符,如果字符串s1不包含字符串s2则返回NULL

参数:

const char _s1:字符串1
const char _s2:字符串2

strcat():字符串拼接函数
原型:
char _strcat(char _str1, const char *str2);

返回值:返回指向字符串2拼接到字符串1的字符串地址的指针

参数:

const char _s1:字符串1
const char _s2:字符串2

strcpy():字符串复制函数
原型:
char _strcpy(char _str1, const char *str2)

返回值:返回指向字符串2复制到字符串1尾端的字符串地址的指针

参数:

const char _s1:字符串1
const char _s2:字符串2

memset():将一块内存设定成指定的值
原型:
void _memset(void _s, int c, size_t n);

返回值:返回一个指向内存s的指针

参数:

void *s:内存块
int c:指定值
size_t n:要被设置该值的字符数

system():执行某个命令,命令执行后继续从调用点执行程序
原型:
int system(const char *command);

返回值:

调用/bin/sh失败时返回127,其他失败原因返回-1
const char _command参数为NULL时返回非0值
调用成功返回执行shell命令后的返回值
_*参数:__

const char *command:命令

popen():执行某个命令,并将执行命令后得到的数据存放到一个文件流中
原型:
FILE _popen(const char _command, const char *type);

返回值:调用成功返回文件流,调用失败返回NULL

参数:

const char _command:命令
const char _type:返回的文件流权限,w:文件流可写,r: 文件流可读(与system等价)

fread():从某个文件流中读取数据
原型:
int fread(void _ptr, size_t size, size_t nmemb,FILE _stream);

返回值:读入成功时,返回读取的字节数,读入失败时,返回-1

参数:

const void _ptr:读取数据存放位置
size_t size:每次读的字节数
size_t nmemb:读的次数
FILE _stream:文件流

fflush():刷新缓冲区,将某个流未写的数据传送至内核
原型:
int fflush(FILE *stream);

返回值:调用成功返回0,调用失败返回EOF

参数:

FILE *stream:文件流
fflush(stdin):刷新标准输入缓冲区,把输入缓冲区里的东西丢弃
fflush(stdout):刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上

chdir():修改当前的工作目录
原型:
int chdir(const char *path);

返回值:调用成功返回0,调用失败返回-1

参数:

const char *path:新的当前工作目录,绝对路径或者相对路径

access():查看文件是否具有某个权限
原型:
int access(const char * pathname,int mode);

返回值:如果文件具有某种权限返回0,不具有某种权限返回-1

参数:

const char _pathname:文件名(相对路径)
int mode:权限



代码分析
服务器和客户端建立socket连接在往期文章《Linux网络编程》
由于使用结构体在网络中传输,因此,能够将命令和其他数据分开
_*服务器
主函数(建立socket连接),并读取客户端发送的命令__

int main(int argc,char *argv[])
{
	//建立socket连接
	struct sockaddr_in so_addr;  
	struct sockaddr_in cl_addr;
 
	if(argc != 3){
		printf("Insufficient or inconsistent parameters\n");
		exit(-1);
	}
 
	memset(&so_addr,0,sizeof(struct sockaddr_in));
	memset(&cl_addr,0,sizeof(struct sockaddr_in));
 
	int s_fd = socket(AF_INET,SOCK_STREAM,0);  //创建套接字:IPV4因特网域,流式套接字、TCP传输协议
	if(s_fd == -1){
		printf("socket is failusr\n");
		perror("socket");
		exit(-1);
	}
 
	so_addr.sin_family = AF_INET;  //IPV4因特网域
	so_addr.sin_port = htons(atoi(argv[2]));  //将端口号转化成网络字节序
	inet_aton(argv[1],&so_addr.sin_addr);  //将IP地址转化为网络字节序
 
	int bd = bind(s_fd,(struct sockaddr *)&so_addr,sizeof(struct sockaddr_in));  //为套接字添加网络信息
	if(bd == -1){
		printf("bind is failusr\n");
		perror("bind");
		exit(-1);
	}
 
	int ln = listen(s_fd,10);  //监听客户端的连接
	if(ln == -1){
		printf("listen is failusr\n");
		perror("listen");
		exit(-1);
	}
 
	int a_fd;  //连接文件描述符
	int mark = 0;  //客户端数量标志
	int clean = sizeof(struct sockaddr_in);
	pid_t pid;
	while(1){
		a_fd = accept(s_fd,(struct sockaddr *)&cl_addr,&clean);  //等待客户端的连接
		if(a_fd == -1){
			printf("accept is failusr\n");
			perror("accept:");
			exit(-1);
		}
		mark++;  //客户端标志+1
		printf("get No.%d connect: %s\n",mark,inet_ntoa(cl_addr.sin_addr));
 
		pid = fork();  //当有客户端连接时,创造一个子进程去读取客户端发送的命令
			perror("fork");
			exit(-1);
		if(pid < 0){
            perror("fork");
            exit(-1);
        }else if(pid == 0){
			char *fp = NULL;
			struct Msg msg;
			int ret = 0;
			int c_read;
			while(1){
				memset(msg.cmd,0,sizeof(msg.cmd));
				c_read = read(a_fd,&msg,sizeof(msg));  //读取客户端发送的命令
				if(c_read <= 0){
					perror("read");
					break;
				}else{
					printf("get size is %d  cmd from NO.%d client:%s\n",c_read,mark,msg.cmd);
					ret = cmd_distinguish(msg.cmd);  //对客户端传送的命令进行识别
					cmd_handle(ret,msg,a_fd);  //对相关命令进行处理
 
				}
 
			}
		}
		close(a_fd);
	}
 
	close(s_fd);
	return 0;
}

命令识别

int cmd_distinguish(char *cmd)
{
        cmd = strtok(cmd,"\n");  //由于客户端获取用户输入时,会使用enter键,因此传输到服务器的命令都带有换行符
        if(!strcmp("ls",cmd)) return LS;
        if(strstr(cmd,"cd") != NULL) return CD;
        if(strstr(cmd,"get") != NULL) return GET;
        if(strstr(cmd,"put") != NULL) return PUT;
        if(!strcmp("pwd",cmd)) return PWD;
}

命令分割

char *str_decompose(char *cmd)  //将带有文件名、路径名的命令进行处理分割
{								//如命令“get 1.c”,该函数就会返回1.c
	strtok(cmd," ");
	cmd = strtok(NULL," ");
	return cmd;
}

命令处理

char *str_decompose(char *cmd)  //将带有文件名、路径名的命令进行处理分割
{								//如命令“get 1.c”,该函数就会返回1.c
	strtok(cmd," ");
	cmd = strtok(NULL," ");
	return cmd;
}

客户端
主函数(建立socket连接),获取用户输入的命令
int main(int argc,char *argv[])
{
	//建立socket连接
	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));
 
	int s_fd = socket(AF_INET,SOCK_STREAM,0);  //创建套接字:IPV4因特网域,流式套接字、TCP传输协议
	if(s_fd == -1){
		printf("socket is failusr\n");
		perror("socket");
		exit(-1);
	}
 
	c_addr.sin_family = AF_INET;  //IPV4因特网域
	c_addr.sin_port = htons(atoi(argv[2]));  //将端口号转化成网络字节序
	inet_aton(argv[1],&c_addr.sin_addr);  //将IP地址转化为网络字节序
 
	if(connect(s_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){  //客户端连接服务器
		perror("connect");
		exit(-1);
	}
 
	int n_read;
	struct Msg msg;
	int ret = 0;
	int mark = 0;
	int tmp = 0;
	while(1){
		memset(msg.cmd,0,sizeof(msg.cmd));
		memset(msg.data,0,sizeof(msg.data));
 
		fgets(msg.cmd,sizeof(msg.cmd),stdin);  //获取用户输入的命令
		ret = cmd_distinguish(msg.cmd);  //对用户键入的命令进行识别
		tmp = cmd_handle(ret,msg,s_fd);  //对命令进行处理
		if(tmp > CMD){  
			from_sorket_data_handle(msg,s_fd);  //对服务器传送的数据进行处理
		}
 
		fflush(stdout);
 
	}	
	close(s_fd);
	return 0;
}

命令识别
//命令识别
int cmd_distinguish(char *cmd)
{
	int ret;
	cmd = strtok(cmd,"\n");  由于客户端获取用户输入时,会使用enter键
	if(!strcmp("lls",cmd)){
		ret = LLS;
	}else if(strstr(cmd,"lcd") != NULL){
		ret = LCD;
	}else if(!strcmp("lpwd",cmd)){
		ret = LPWD;
	}else if(!strcmp("ls",cmd)){
		ret = LS;
	}else if(!strcmp("lquit",cmd)){
		ret = LQUIT;
	}else if(strstr(cmd,"cd") != NULL){
		ret = CD;
	}else if(strstr(cmd,"get") != NULL){
		ret = GET;
	}else if(strstr(cmd,"put") != NULL){
		ret = PUT;
	}else if(!strcmp("pwd",cmd)){
		ret = PWD;
	}else{
		ret = 0;
	}
	return ret;
}

命令分割
//命令分割
char *str_decompose(char *cmd)  //将带有文件名、路径名的命令进行处理分割
{								//如命令“put 1.c”,该函数就会返回1.c
	strtok(cmd," ");
	cmd = strtok(NULL," ");
	return cmd;
}

命令处理
//命令处理 
int cmd_handle(int ret,struct Msg msg,int s_fd)
{
 
	FILE *fp = NULL;
	int n_fread;
	char file_path[20] = "./";
	char tmp_cmd[50];
	switch(ret){
		case LPWD:
			printf("--------------------------------------\n");
			printf("\n");
			system("pwd");  //调用终端执行pwd命令,并将数据输出到标准输出上
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
		case LLS:
			printf("--------------------------------------\n");
			printf("\n");
			system("ls");  //调用终端执行ls命令,并将数据输出到标准输出上
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
		case LCD:
			if(chdir(str_decompose(msg.cmd)) < 0){  //修改当前工作目录
				perror("chdir");
			}                       
			printf("\n");
			break;
		case LQUIT:
			printf("--------------------------------------\n");
			printf("\n");
			printf("client is quit\n");
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			exit(0);  //退出当前进程
			break;
		case LS:	
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));  //将用户输入发送给服务器,让服务器根据命令进行相应的行为
			break;
		case PWD:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));  //将用户输入发送给服务器,让服务器根据命令进行相应的行为
			break;
		case CD:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));  //将用户输入发送给服务器,让服务器根据命令进行相应的行为
			break;
		case GET:
			msg.type = 0;	
			write(s_fd,&msg,sizeof(msg));  //将用户输入发送给服务器,让服务器根据命令进行相应的行为
			break;
		case PUT:
			memset(tmp_cmd,0,sizeof(tmp_cmd)); 
			strcpy(tmp_cmd,msg.cmd);  //put 1.c ->tmp.cmd
			strcat(file_path,str_decompose(msg.cmd));  // ./1.c->file_path
			memset(msg.cmd,0,sizeof(msg.cmd));
			strcpy(msg.cmd,tmp_cmd);  //put 1.c -> msg.cmd
			if(access(file_path,F_OK) < 0){  //判断客户端是否拥有想要存放到服务器的文件
				printf("--------------------------------------\n");
				printf("\n");
				printf("Target file does not exist\n");
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
 
			}else if(access(file_path,R_OK) < 0){  //判断客户端是否对该文件有读权限
				printf("--------------------------------------\n");
				printf("\n");
				printf("The target file does not have read permission\n");   	
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
			}else{  //获取该文件的内容,发送给服务器
				int pfile_fd = open(file_path,O_RDONLY,0666);
				if(pfile_fd < 0){
					perror("open");
				}
				int file_size = lseek(pfile_fd,0,SEEK_END);
				if(file_size < 0){
					perror("lseek");
				}
				lseek(pfile_fd,0,SEEK_SET);
				memset(msg.data,0,sizeof(msg.data));
				if(read(pfile_fd,msg.data,file_size) < 0){
					perror("read");
				}
				close(pfile_fd);
				write(s_fd,&msg,sizeof(msg));
				printf("--------------------------------------\n");
				printf("\n");
				printf("Target file is on server\n");   	
				printf("\n");
				printf("--------------------------------------\n");
				printf("\n");
			}
			break;
		default:
			printf("--------------------------------------\n");
			printf("\n");
			printf("Input error, please re-enter\n");   	
			printf("\n");
			printf("--------------------------------------\n");
			printf("\n");
			break;
 
	}
	return ret;
}

对服务器发送的数据进行处理
//对服务器发送的数据进行处理
void from_sorket_data_handle(struct Msg msg,int s_fd)
{
    struct Msg sorket_msg;
    int n_read;
    int file_fd;
    char file_path[20] = "./";
    n_read = read(s_fd,&sorket_msg,sizeof(sorket_msg));
    if(n_read == -1){
        perror("read sorket");
        exit(-1);
    }else if(sorket_msg.type == GET){  //如果是GET指令返回的数据,就存进文件中
        strcat(file_path,str_decompose(msg.cmd));  // ./1.c -> file_path
        file_fd = open(file_path,O_RDWR|O_CREAT,0666);
        write(file_fd,sorket_msg.data,strlen(sorket_msg.data));
        fflush(stdout);
        close(file_fd);
        printf("--------------------------------------\n");
        printf("\n");
        printf("%s\n","Get target file from server\n");
        printf("--------------------------------------\n");
        printf("\n");
    }else{  //如果是其他数据就直接打印出来
        printf("--------------------------------------\n");
        printf("\n");
        printf("%s\n",sorket_msg.data);
        printf("\n");
        printf("--------------------------------------\n");
        printf("\n");
    }

}