一个子进程负责一条连接通道
一、项目要求:实现以下内容
客户端支持的指令:
远程控制:
1、获取服务器当前路径的文件:get xxx2、 展示服务器有哪些文件:ls3、进入服务器某文件夹 :cd xxx4、上传文件到服务器: put xxx
本地控制:
1、查看本地客户端文件 :lls2、查看进入客户端文件夹 :lcd
二、步骤:
服务器:解析指令(创建子进程、对接客户端)
1、创建套接字socket2、绑定bind ip+端口号3、listen监听while(1)调用accept4、accept接受客户端请求,返回值小于0报错①、有客户端接入调用fork创建子进程,返回值小于 0 报错;等于 0 对客户端进行交互。②、对(buf)读取出来的数据做判断(ls、get、cd、pwd)①)get:open打开文件,read文件,send文件到客户端②)ls:popen文件,fread读取文件,send发送文件到客户端
// system()调用副服务器:
在子进程中:1、接收指令2、把指令写入共享内存 + get pid3、读取结果共享内存4、读到的结果返回客户端
在副服务器中:1、读取共享内存(指令共享内存)2、发现两次指令不一样,执行结果存入结果共享内存
客户端:发指令
1、socket创建套接字2、connect连接服务器3、连接上以后获取用户的输入,对输入的信息进行处理
三、代码实现
客户端client代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <config.h>
#include <sys/stat.h>
#include <fcntl.h>char * getdir(char *cmd)
{char *p;p = strtok(cmsg," ");//分割字符串,用空格分隔p = strtok(NULL," ");return p;
}int get_cmd_type(char *cmd)
{if(strstr(cmd,"lcd")!=NULL) return LCD;if(!strcmp("lls",cmd)) return LLS;if(!strcmp("ls",cmd)) return LS; //宏定义if(!strcmp("quit",cmd)) return QUIT;if(!strcmp("pwd",cmd)) return PWD;if(strstr(cmd,"cd")!=NULL) return CD;if(strstr(cmd,"get")!=NULL) return GET;if(strstr(cmd,"put")!=NULL) return PUT;return -1;
}int cmd_handler(struct Msg msg,int fd)
{char *dir = NULL;char buf[32];int ret;int filefd;ret = get_cmd_type(msg.data);switch(ret){case LS:case CD:case PWD:msg.type = 0;write(fd,&msg,sizeof(msg));break;case GET:msg.type = 2;write(fd,&msg,sizeof(msg));break;case PUT:strcpy(buf,msg.data);dir = getdir(buf);if(access(dir,F_OK == -1)){printf("%s No exsit !",dir);}else{fdfile = 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 = getdir(msg.data);chdir(dir);break;case QUIT:strcpy(msg.data,"quit");write(fd,&msg,sizeof(msg));close(fd);exit(-1);}return ret;
}void handler_server_message(int c_fd,struct Msg msg)
{int n_read;struct Msg msg;int newfilefd;n_read = read(c_fd,&msgget,sizeof(msgget));if(n_read == 0){printf("server is out,quit \n");exit(-1);}else if(msgget.type == DOFILE){char *p = getdir(msg.data);newfilefd = open(p,O_RDWR|O_CREAT,0666);write(newfilefd,msgget,strlen(msgget));putchar(">");fflush(stdout);}else{printf("......................\n");printf("\n %s \n",msgget.data);printf("......................\n");putchar(">");fflush(stdout);}
}int main(int argc,char **argv)
{int c_fd;//定义结构体struct sockaddr_in c_addr;struct Msg msg;//数据清空memset(&c_addr,0,sizeof(struct sockaddr_in));if(argc != 3){printf("param is not good"); //参数不好exit(-1);}//1、创建套接字 socketc_fd = socket(AF_INET,SOCK_STREAM,0);//用到IPV4,TCP协议,0自动配合起来if(c_fd == -1){ //返回-1,错误perror("socket");exit(-1);}c_addr.sin_family = AF_INET; //协议族c_addr.sin_port = htons(atoi(argv[2])); //端口号,一般为5000--9000//电脑为x86是小端字节序,网络字节序为大端字节序。所以要进行转换用htonsinet_aton(argv[1],&c_addr.sin_addr);//把字符串形式的127.0.0.1转换成网络能识别的格式。用 到inet_aton//2、connect 连接客户端的listenif(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){perror("connect");exit(-1);}printf("connect ....\n");//printf("get connect : %s\n",inet_ntoa(c_addr.sin_addr));//把网络格式的IP转换为字符格式int mark = 0;while(1){memset(msg.data,0,sizeof(msg.data));//初始化if(mark == 0) printf(">");gets(msg.data);if(strlen(msg.data) == 0){if(mark == 1){printf(">");}continue;}mark = 1;int ret = cmd_handler(msg,c_fd);if(ret > IFGO){putchar(">");fflush(stdout);continue;}if(ret == -1){printf("command not \n");printf(">");fflush(stdout);continue;}handler_server_message(c_fd,msg);}return 0;
}
服务器代码server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <config.h>
#include <sys/stat.h>
#include <fcntl.h>int get_cmd_type(char *cmd)
{if(!strcmp("ls",cmd)) return LS; //宏定义if(!strcmp("quit",cmd)) return QUIT;if(!strcmp("pwd",cmd)) return PWD;if(strstr(cmd,"cd")!=NULL) return CD;if(strstr(cmd,"get")!=NULL) return GET;if(strstr(cmd,"put")!=NULL) return PUT;return 100;
}char *getDesDir(char *cmsg)
{char *p;p = strtok(cmsg," ");//分割字符串,用空格分隔p = strtok(NULL," ");return p;
}void msg_handler(struct Msg msg,int fd)
{char dataBuf[1024] = {0};char *file = NULL;int fdfile;//解析指令,把指令转换成想要的整型数printf("cmd : %s\n",msg.data);int ret = get_cmd_type(msg.data);switch(ret){case LS: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 = 0;char *dir = getDesDir(msg.data);//分割字符串printf("dir : %s\n",dir);chdir(dir);break;case GET:file = getDesDir(msg.data);if(access(file,F_OK == -1)){strcpy(msg.data,"No this file !");write(fd,&msg,sizeof(msg));}else{msg.type = DOFILE;fdfile = open(file,O_RDWR);read(fdfile,dataBuf,sizeof(dataBuf));close(fdfile);strcpy(msg.data,dataBuf);write(fd,&msg,sizeof(msg));}break;case PUT:fdfile = open(getDesDir(msg.data),O_RDWR|O_CREAT,0666);write(fdfile,msg.secondBuf,strlen(msg.secondBuf));//buf放内容,msg.data放指令close(fdfile);break;case QUIT:printf("client quit ! \n");exit(-1);}
}int main(int argc,char **argv)//配置端口
{int s_fd;int c_fd;int n_read;char redBuf[128];struct sockaddr_in s_addr; //定义结构体struct sockaddr_in c_addr;struct Msg msg;if(argc != 3){printf("param is not good\n"); //参数不好exit(-1);}//数据清空memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1、创建套接字 sockets_fd = socket(AF_INET,SOCK_STREAM,0);//用到IPV4,TCP协议,0自动配合起来if(s_fd == -1){ //返回-1,错误perror("socket");exit(-1);}//2、添加地址 bind s_addr.sin_family = AF_INET; //协议族s_addr.sin_port = htons(atoi(argv[2])); //端口号,一般为5000--9000//电脑为x86是小端字节序,网络字节序为大端字节序。所以要进行转换用htonsinet_aton(argv[1],&s_addr.sin_addr);//把字符串形式的127.0.0.1转换成网络能识别的格式。用到inet_atonbind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct socket_in));//3、监听listenlisten(s_fd,10);//监听10个//4、连接 acceptint clen = sizeof(struct socket_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr ,&clen);//连接到客户端之后,后续的操作用返回值c_fd来操作if(c_fd == -1){perror("accept");}printf("get connect : %s\n",inet_ntoa(c_addr.sin_addr));//把网络格式的IP转换为字符格式//新的客户端接入,创建子进程,一个子进程负责一条连接通道if(fork() == 0){ //fock返回值=0进入子进程,>0进入父进程while(1){ memset(retmessage,0,sizeof(retmessage));//初始化
//5、readn_read = read(c_fd,&msg,sizeof(msg));if(n_read == 0){printf("client out !\n");break;}else if(n_read > 0){msg_handler(msg,c_fd);}
//6、write}}}close(c_fd);close(s_fd);return 0;
}
定义
//msg结构体#define LS 0
#define GET 1
#define PWD 2#define IFGO 3#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7#define QUIT 8
#define DOFILE 9struct Msg
{int type;char data[1024];char secondBuf[128];
};