Linux 基于server和client的FTP文件项目
功能需求:
- 远程操作:
①获取服务器的文件: get xxx
②显示服务器有哪些文件: ls
③进入服务器某文件夹: cd xxx
④上传文件到服务器: put xxx
⑤打印当前服务器路径: pwd
⑥退出服务器: quit - 本地操作:
①查看客户端本地文件: lls
②进入客户端文件夹: lcd
基本思路
- 利用客户端和服务断框架,一旦服务端接收到客户端的连接请求,服务器fork()创建一个子进程负责处理客户端的指令,客户端发出指令,服务端根据指令做出相应的处理并返回值给客户端,客户端根据返回值打印结果。
- 利用宏定义指令简化代码
- 用函数封装实现小功能
- 利用结构体储存并传递指令和返回值
- 当客户端读取到指令输入,用cmd_handler把指令转化为宏,输送给服务端。服务端接收到指令,msg_handler进行处理,并返回data给客户端,然后客户端再用server_handler处理返回的数据,最后输出结果。
小功能实现
- 宏定义以及结构体构建
这里直接自定义一个头文件,包含宏定义指令和要用的结构体,以便于引用
congfig.h:
#define LS 0
#define CD 1
#define PWD 2
#define GET 3
#define IFRET 4
#define LLS 5
#define LCD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
struct Msg //传递的信息结构体
{
int type ; //服务器返回类型,0表示不用返回,1表示字符串,DOFILE表示要创建文件
char data[32]; //指令
char secondBuf[10240];//数据,用于server和client间的通信
};
- getdir()用空格分割处理ls的输出结果,并返回。strtok()首次调用,使用空格第一次分割,之后调用传入NULL,字符串固定用法。我们二次调用,返回值是被分割出来的第二个字符串,即文件名。
char *getdir(char *cmd)
{
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
- cd/pwd
与ls类似,使用popen(),调用相应的命令,返回输出结果,注意cd改变目录时,调用的是chdir(),表示进入相应的文件,不能用system(),否则,是在shell窗口下进入。主窗口下没有变化。
服务端:
case PWD:
msg.type = 0;
FILE *r = popen(msg.data,"r");
fread(msg.data,sizeof(msg.data),1,r);
write(fd,&msg,sizeof(msg));
break;
case CD:
msg.type = 1;
char *dir = getDesDir(msg.data);
printf("dir:%s\n",dir);
chdir(dir);
break;
- lls/lcd
同理,在客户端,直接调用相关函数:
case LLS:
system("ls");
break;
case LCD:
dir = getdir(msg.data);
chdir(dir);
break;
- quit
服务端调用extit(-1)退出。
代码
- servser.c
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include<linux/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>
#include"config.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/*struct sockaddr_in {
sa_family_t sin_family; //协议族
in_port_t sin_port; //端口号
struct in_addr sin_addr; //IP地址结构体
unsigned char sin_zero[8]; //填充,没有实际意义,只是为更sockaddr结构在内存中相互对齐,这样才能相互转换
}*/
/*struct in_addr {
__be32 s_addr;
};*/
int get_cmd_type( char *cmd){
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("lls",cmd)) return LLS;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("pwd",cmd)) return PWD;
if(strstr(cmd,"lcd")!=NULL) return LCD;
if(strstr(cmd,"cd")!=NULL) return CD;
if(strstr(cmd,"get")!=NULL) return GET;
if(strstr(cmd,"put")!=NULL) return PUT;
return -1;
}
char *getdesdir(char *cmd){
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
void msghandler(int fd,struct Msg msg){
int ret = get_cmd_type(msg.data);
char *file = NULL;
int fdfile;
char databuf[10240] = {0};
char *dir;
char msg_databuf[32] = {0};
printf("cmd:%s\n",msg.data);
switch(ret){
case LS:
case PWD:
msg.type = 1;
FILE *r = popen(msg.data,"r");
fread(msg.secondBuf,sizeof(msg.secondBuf),1,r);
write(fd,&msg,sizeof(msg));
break;
case GET:
strcpy(msg_databuf,msg.data);
file = getdesdir(msg.data);
strcpy(msg.data,msg_databuf);
if(access(file,F_OK) == -1){//判断文件名是否存在
msg.type = 1;
strcpy(msg.secondBuf,"file is not exist");
write(fd,&msg,sizeof(msg));
}else {
msg.type = DOFILE;
fdfile = open(file,O_RDWR);
read(fdfile,databuf,sizeof(databuf));
close(fdfile);
strcpy(msg.secondBuf,databuf);
write(fd,&msg,sizeof(msg));
}
break;
case PUT:
file = getdesdir(msg.data);
if(access(file,F_OK) != -1){//判断文件名是否存在
msg.type = 1;
strcpy(msg.secondBuf,"file is exist");
write(fd,&msg,sizeof(msg));
}else{
msg.type = 0;
fdfile = open(file,O_RDWR|O_CREAT,0600);
write(fdfile,msg.secondBuf,sizeof(msg.secondBuf));
close(fdfile);
write(fd,&msg,sizeof(msg));
}
break;
case CD:
dir = getdesdir(msg.data);
if(access(dir,F_OK) == -1){
msg.type = 1;
strcpy(msg.secondBuf,"dir is not exist");
}else{
msg.type = 0;
chdir(dir);
}
write(fd,&msg,sizeof(msg));
break;
case QUIT:
msg.type = 0;
write(fd,&msg,sizeof(msg));
printf("client quit\n");
exit(-1);
break;
case -1:
break;
}
}
int main(int argc ,char **argv){
int s_fd;
int c_fd;
key_t key;
pid_t pid;
int sizec_addr;
struct Msg msg;
char readbuf[128] ={0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket创建套接字
s_fd = socket(AF_INET,SOCK_STREAM, 0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
//2.bind(为套接字绑定地址和端口号)
s_addr.sin_family = AF_INET; //IPV4 因特网
s_addr.sin_port = htons( atoi(argv[2]) );//大端变小端
inet_aton(argv[1],&s_addr.sin_addr) ;//字符串变网络形式
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3. listen()监听
listen(s_fd,10 );
printf("listen...\n");
while(1){ //不停的接受客户端请求
//4.accept()函数
sizec_addr = sizeof(struct sockaddr_in);
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &sizec_addr);
if(c_fd != -1){ //有客户端接入
char *p = inet_ntoa(c_addr.sin_addr);
printf("client %s connect\n",p);
pid = fork();
if(pid == 0){
while(1){
memset(msg.data,0,sizeof(msg.data));
memset(msg.secondBuf,0,sizeof(msg.secondBuf));
int n_read = read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("client %s out\n",p);
break;
}else if(n_read >0){
msghandler(c_fd,msg);
}
}
}
}
}
close(s_fd);
close(c_fd);
return 0;
}
- client.c
#include<stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include<linux/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>
#include"config.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/*struct sockaddr_in {
sa_family_t sin_family; //协议族
in_port_t sin_port; //端口号
struct in_addr sin_addr; //IP地址结构体
unsigned char sin_zero[8]; //填充,没有实际意义,只是为更sockaddr结构在内存中相互对齐,这样才能相互转换
}*/
/*struct in_addr {
__be32 s_addr;
};*/
int get_cmd_type(char *cmd){
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("lls",cmd)) return LLS;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("pwd",cmd)) return PWD;
if(strstr(cmd,"lcd")!=NULL) return LCD;
if(strstr(cmd,"cd")!=NULL) return CD;
if(strstr(cmd,"get")!=NULL) return GET;
if(strstr(cmd,"put")!=NULL) return PUT;
return -1;
}
char *getdir(char *cmd){
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");
return p;
}
int cmd_handler(struct Msg msg,int fd){
int ret;
char buf[32] = {0};
char *dir = NULL;
int filefd;
ret = get_cmd_type(msg.data);
switch(ret){
case LS:
case CD:
case PWD:
case GET:
write(fd,&msg,sizeof(msg));
break;
case PUT:
strcpy(buf,msg.data);
dir = getdir(buf);
if(access(dir,F_OK) == -1){
printf("%s is not exist\n",dir);
}else{
filefd = open(dir,O_RDWR);
read(filefd,msg.secondBuf,sizeof(msg.secondBuf));
close(filefd);
write(fd,&msg,sizeof(msg));
}
break;
case LLS:
write(fd,&msg,sizeof(msg));
system("ls");
break;
case LCD:
write(fd,&msg,sizeof(msg));
dir = getdir(msg.data);
if(access(dir,F_OK) == -1){
printf("dir is not exist\n");
}else{
chdir(dir);
}
break;
case QUIT:
strcpy(msg.secondBuf,"quit");
printf("client quit\n");
write(fd,&msg,sizeof(msg));
close(fd);
exit(-1);
break;
}
return ret;
}
void server_message_handler(struct Msg msg,int fd){
int n_read;
int newfilefd;
char *file = NULL;
memset(msg.data,0,sizeof(msg.data));
n_read = read(fd,&msg,sizeof(msg));
if(n_read == 0){
printf("server is out,quit\n");
exit(-1);
}
if(msg.type == DOFILE){
file = getdir(msg.data);
newfilefd = open(file,O_RDWR|O_CREAT,0600);
if(newfilefd == -1){
perror("newfilefd");
}
write(newfilefd,msg.secondBuf,strlen(msg.secondBuf));
close(newfilefd );
}else if(msg.type == 1){
printf("................................\n");
printf("\n%s\n",msg.secondBuf);
printf("................................\n");
fflush(stdout);
}else if(msg.type == 0){
return ;
}
}
int main(int argc ,char **argv){
int c_fd;//socket的返回值
int c_ret;//connect的返回值
int ret_cmd_handler;//cmd_handler()的返回值
int sizec_addr;
struct Msg msg;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket创建套接字
c_fd = socket(AF_INET,SOCK_STREAM, 0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
//2.connect(向服务器发送连接请求)
c_addr.sin_family = AF_INET; //IPV4 因特网
c_addr.sin_port = htons( atoi(argv[2]) );//大端变小端
inet_aton(argv[1],&c_addr.sin_addr) ;//字符串变网络形式
c_ret = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(c_ret == -1){
printf("connect fail\n");
exit(-1);
}
printf("connect %s\n",argv[1]);//显示当前连接服务端ip地址
while(1){ //不停的获取用户输入
memset(msg.data,0,sizeof(msg.data));//每次用户输入前,先把数据初始化
memset(msg.secondBuf,0,sizeof(msg.secondBuf));
msg.type = 0;
putchar('>');//每次输入前,打印‘>’,等待输入
fflush(stdout);
gets(msg.data);//获取输入命令,cmd,存入mag.data中
if(strlen(msg.data) == 0 ){//当输入为空
continue; //直接进入下一次循环,重新获取输入
}else{
ret_cmd_handler = cmd_handler(msg,c_fd);//根据输命令入,发送不同的数据给服务端
if(ret_cmd_handler > IFRET){ //IFRET为宏,大于它,表示server返回client的结果不需要处理
fflush(stdout);//冲刷缓冲区,是数据即时输出
continue;//进入下一次循环
}
if(ret_cmd_handler == -1){//非法指令
printf("command not found\n");
fflush(stdout);//冲刷缓冲区,是数据即时输出
continue;//进入下一次循环
}else{
server_message_handler(msg,c_fd);//服务端返回信息处理
}
}
}
return 0;
}