在Linux下设计一组简单的ftp服务器。
主要功能如下:
1、一台服务器,可同时连接多台客户端实现相同功能操作。
2、客户端可访问服务器的所含文件,并从服务端下载所需文件;同时客户端可以将自身所含文件发送到服务器。
3、客户端可以查看自身所含文件。
//config.h
#define LS 0
#define PWD 1
#define GET 2
#define IFGO 3
#define CD 4
#define QUIT 5
#define LLS 6
#define LCD 7
#define PUT 8
#define DOFILE 9
struct Msg
{
int type;
char data[1024];
char secondBuf[128];//该字符串空间主要用于备用存储
};
~
//客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include "config.h"
int get_msg_type(char *cmd)
{
if(!strcmp(cmd,"ls")) return LS;
if(!strcmp(cmd,"pwd")) return PWD;
if(!strcmp(cmd,"quit")) return QUIT;
if(!strcmp(cmd,"lls")) return LLS;
if(strstr(cmd,"lcd") != NULL) return LCD;
if(strstr(cmd,"cd") != NULL) return CD;
if(strstr(cmd,"put") != NULL ) return PUT;
if(strstr(cmd,"get") != NULL) return GET;
return -1;
}
char* getDesDir(char* msg) //该函数调用后将破坏原msg数据的格式
{
char *dir;
dir = strtok(msg," ");
dir = strtok(NULL," ");
return dir;
}
int handler_msg(struct Msg msg,int fd)
{
char *dir = NULL;
char readBuf[1024] = {0};
char buf[32];
int filefd;
int ret = get_msg_type(msg.data);
switch(ret){
case CD:
case LS:
case PWD:
case GET:
write(fd,&msg,sizeof(msg));
break;
case PUT:
strcpy(buf,msg.data); //在此需要调用strcpy函数对data备份;否则put指令无法执行
dir = getDesDir(buf);
if(access(dir,F_OK) == -1){
printf("This file is not exist\n");
break;
}else{
filefd = open(dir,O_RDWR);
read(filefd,msg.secondBuf,sizeof(msg.secondBuf));
close(filefd);
write(fd,&msg,sizeof(msg));
break;
}
case LLS:
system("ls");
break;
case LCD:
dir = getDesDir(msg.data);
printf("dir= %s\n",dir);
chdir(dir);
break;
case QUIT:
printf("quit\n");
strcpy(msg.data,"quit");
write(fd,&msg,sizeof(msg));
exit(-1);
}
return ret;
}
void server_msg(int fd,struct Msg msg)
{
int n_read;
int newfilefd;
struct Msg msgget;
n_read = read(fd,&msgget,sizeof(msgget));
if(n_read == 0){
printf("server is quit\n");
exit(-1);
}
if(msgget.type == DOFILE){
char* p = getDesDir(msg.data);
newfilefd = open(p,O_RDWR|O_CREAT|0600);
write(newfilefd,msgget.data,strlen(msgget.data));
close(newfilefd);
fflush(stdout);
}else{
printf("=======================\n");
printf("%s\n",msgget.data);
printf("=======================\n");
fflush(stdout);
}
}
int main(int argc,char** argv)
{
if(argc != 3){
printf("The parmas is not good\n");
exit(-1);
} //输入参数判断(服务器端同)
int c_fd;
int ret;
struct sockaddr_in c_addr;
struct Msg msg;
memset(&c_addr,0,sizeof(struct sockaddr_in));
memset(&msg,0,sizeof(struct Msg));
if((c_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("socket\n");
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(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
perror("connect\n");
exit(-1);
}
printf("connect...\n");
while(1){
memset(&msg.data,0,sizeof(msg.data));
gets(msg.data);
ret = handler_msg(msg,c_fd);
if(ret == -1){
printf("This command is not exist\n");
fflush(stdout);
continue;
}else if(ret > IFGO){
fflush(stdout);
continue;
}
server_msg(c_fd,msg);
}
return 0;
}
//服务端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
//#include <unistd.h>
#include "config.h"
int get_msg_type(char* data)
{
if(!strcmp(data,"ls")) return LS;
if(!strcmp(data,"pwd")) return PWD;
if(!strcmp(data,"quit")) return QUIT;
if(strstr(data,"cd") != NULL) return CD;
if(strstr(data,"get") != NULL) return GET;
if(strstr(data,"put") != NULL) return PUT;
return -1;
}
char* getdir(char* msg)
{
char* dir;
dir = strtok(msg," ");
dir = strtok(NULL," ");
return dir;
}
void handler_msg(int fd,struct Msg msg)
{
char* File = NULL;
FILE* r;
int filefd;
char readBuf[1024] = {0};
char* dir = NULL;
printf("cmd: %s\n",msg.data);
int ret = get_msg_type(msg.data);
switch(ret){
case LS:
case PWD:
r = popen(msg.data,"r");
fread(msg.data,sizeof(msg.data),1,r);
write(fd,&msg,sizeof(msg));
break;
case CD:
dir = getdir(msg.data);
printf("dir:%s\n",dir);
chdir(dir);
break;
case GET:
File = getdir(msg.data);
if(access(File,F_OK) == -1){
strcpy(msg.data,"This file is not exist\n");
write(fd,&msg,sizeof(msg));
}else{
msg.type = DOFILE;
filefd = open(File,O_RDWR);
read(filefd,readBuf,sizeof(readBuf));
close(filefd);
strcpy(msg.data,readBuf);
write(fd,&msg,sizeof(msg));
}
break;
case PUT:
filefd = open(getdir(msg.data),O_RDWR|O_CREAT,0600);
write(filefd,msg.secondBuf,strlen(msg.secondBuf));
close(filefd);
break;
case QUIT:
printf("quit\n");
exit(-1);
}
}
int main(int argc,char** argv)
{
if(argc != 3){
printf("The params is not good\n");
exit(-1);
}
struct Msg msg;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
int sockfd;
int c_fd;
int size = sizeof(struct sockaddr_in);
int n_read;
int ret;
pid_t pid;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
memset(&msg,0,sizeof(struct Msg));
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("socket\n");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
if(bind(sockfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1){
perror("bind\n");
exit(-1);
}
listen(sockfd,10);
while(1){
if((c_fd = accept(sockfd,(struct sockaddr *)&c_addr,&size)) == -1){
perror("accept\n");
exit(-1);
}
printf("%s:connect success\n",inet_ntoa(c_addr.sin_addr));
pid = fork();
if(pid == -1){
perror("fork\n");
}else if(pid == 0){
while(1){
memset(&msg.data,0,sizeof(msg.data));
n_read = read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("quit\n");
break;
}
else if(n_read > 0){
handler_msg(c_fd,msg);
}
}
}
}
return 0;
}
服务器与客户端的互动需要各个指令的相互配合:
一、服务器可以连接多个客户端,因此只有在客户端成功接入时,创建子进程对客户端进行处理。
二、有多个指令的处理形式是相同的因此可以对它们进行归纳处理:
1、ls pwd指令:对于客户端来说,只需要调用popen函数将其实现的结果发送给客户端打印即可。
2、lls lcd都属于客户端自身的调用,与服务器无关。
3、quit指令,对于服务器来说需要知晓该客户端的退出,需要退出为该客户端服务的子进程;对于客户端则直接退出即可。
4、cd指令,需要通过解析cd指令的具体操作目标,因此需要getDesDir函数对传送的指令进行解析,做出具体操作。在客户端使用该函数的时候,需要特别注意,在对put指令解析时:会对原msg.data产生分割,所以当在此把msg.data中的内容发送至服务器的时候,服务器将无法判断。
5、get、put指令是客户端与服务器/服务器与客户端之间的文件收发操作。特别注意:在操作相应的文件时,解析后务必要使用access()函数对待操作文件的存在与否进行判断。
补充:get命令从服务器获取文件时,发现在客户端打开该文件内容为空,是由于本地客户端缺少对该文件的访问权限,使用chmod命令修改权限即可显示源文件内容。