前言:这是对linux系统编程学习的总结,具体操作过程在代码里有注释。
//config.h
#define LS 0
#define PWD 1
#define GET 2
#define IFGO 3
#define LLS 4
#define LCD 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
#define MAX_LISTEN 20 //同时进行最大监听数
struct Msg {
int type;
char data[1024]; //文件数据
char cmd[1024]; //存放命令
char contentBuf[1024]; //存放文件内容
};
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include "config.h"
#include
#include
/* 获取命令*/
int get_cmd_type(char *cmd)
{
/* strcmp(s1,s2)比较s1和s2
* 若s1=s2,返回0*/
/* strstr(s1,s2)在字符串s1中
* 查找第一次出现字符串s2的位置,未找到返回NULL*/
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(strstr(cmd,"cd") != NULL) return CD;
if(strstr(cmd,"get") != NULL) return GET;
if(strstr(cmd,"put") != NULL) return PUT;
return 100;
}
/* 分隔字符串,获取需要操作的文件名或路径*/
char *get_cmd_dir(char *cmd)
{
/* char *strtok(char s[], const char *delim);
* s为要分解的字符串,delim为分隔符字符串*/
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");//从保存好的位置开始继续往后寻找
return p;
}
/* 服务端获取命令后进行处理*/
void msg_cmd_handler(struct Msg msg,int fd)
{
char dataBuf[1024] = {0};
char *filename;
int fdfile;
FILE *fp;
char *path = NULL;
char *newfile;
//1.获取命令msg.cmd,并将数据转换为int整数类型
printf("cmd:%s\n",msg.cmd);
int ret = get_cmd_type(msg.cmd);
//2.调用switch处理命令
switch(ret){
case LS:
case PWD:
/* 使用popen调用命令,将输出的结果存放到fp中;
* 再使用fread,读取fp中的内容
* 再使用write,写回到客户端client_fd*/
fp = popen(msg.cmd,"r");
fread(msg.data,sizeof(msg.data),1,fp);
pclose(fp);
write(fd, &msg, sizeof(msg));
break;
case GET:
/* 1.获取命令需要操作的文件名*/
filename = get_cmd_dir(msg.cmd);
/* 2.判断文件是否存在
* int _access( const char *path, int mode );
* F_OK 只判断是否存在*/
if(access(filename,F_OK) == -1){//不存在
strcpy(msg.data,"this file not exit!\n");//写入不存在信息
write(fd, &msg, sizeof(msg));//将信息返回给客户端client_fd
}
/* 3.open文件,read文件, write文件内容到client_fd当中*/
else{
msg.type = DOFILE;
fdfile = open(filename,O_RDWR);
read(fdfile, dataBuf, sizeof(dataBuf));
strcpy(msg.data,dataBuf);
write(fd, &msg, sizeof(msg));
close(fdfile);
}
break;
case CD:
/* 1.获取命令需要操作的路径*/
path = get_cmd_dir(msg.cmd);
/* int chdir(const char *path);
* 2.改变当前工作目录(同cd)*/
chdir(path);
break;
case PUT:
/* 1.创建目标文件*/
fdfile = open(msg.data,O_RDWR|O_CREAT,0666);//创建相同文件名
/* 2.将内容写进目标文件*/
write(fdfile, msg.contentBuf, sizeof(msg.contentBuf));
close(fdfile);
break;
case QUIT:
printf("Client quit!\n");
exit(1);
break;
default:
strcpy(dataBuf,"cmd not exit!");
write(fd,msg.data,sizeof(msg.data));
break;
}
}
int main(int argc,char **argv)
{
int sockfd;
int client_fd;
int n_read;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
struct Msg msg;
if(argc != 3)
{
printf("Input params error!\n");
exit(1);
}
/*void *memset(void *s, int c, size_t n);
memset() 函数常用于内存空间初始化*/
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.int socket(int domain, int type, int protocol);创建网络套接字
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1){
perror("socket");
exit(1);
}
s_addr.sin_family = AF_INET;//网络协议设置为IPV4
s_addr.sin_port = htons(atoi(argv[2]));//uint16_t htons(uint16_t hostshort);传递端口号, htons:从主机(小端)到网络(大端)
inet_aton(argv[1],&s_addr.sin_addr); //int inet_aton(const char *cp, struct in_addr *inp);传递IP地址
//2.int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);设置IP地址和端口号
if(bind(sockfd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1){
perror("bind");
exit(1);
}
//3.int listen(int sockfd, int backlog);监听被绑定的端口
if(listen(sockfd,MAX_LISTEN) == -1){
perror("listen");
exit(1);
}
//4.int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);接受连接请求
int c_len = sizeof(struct sockaddr_in);
while(1){
client_fd = accept(sockfd,(struct sockaddr *)&c_addr,&c_len);
if(client_fd == -1){
perror("accept");
exit(1);
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));//char *inet_ntoa(struct in_addr in);网络字节写转换为字符串形式,打印出IP地址
//创建子进程
if(fork() == 0){
while(1){
//5.read
memset(msg.cmd,0,sizeof(msg.cmd));//对较大的结构体或数组进行清零操作
//01.读取client_fd当中的数据到msg
n_read = read(client_fd,&msg,sizeof(msg));
if(n_read == -1){
perror("read");
exit(1);
}else if(n_read == 0){
printf("Client out!\n");
exit(1);
}else if(n_read > 0){
//02.将调用msg_cmd_handler函数处理msg当中的命令数据
msg_cmd_handler(msg,client_fd);
}
}
}
}
close(client_fd);
close(sockfd);
return 0;
}
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include "config.h"
#include
#include
/* 分隔字符串,获取需要操作的文件名或路径*/
char *get_cmd_dir(char *cmd)
{
/* char *strtok(char s[], const char *delim);
* s为要分解的字符串,delim为分隔符字符串*/
char *p;
p = strtok(cmd," ");
p = strtok(NULL," ");//从保存好的位置开始继续往后寻找
return p;
}
/* 获取命令*/
int get_cmd_type(char *cmd)
{
if(!strcmp("ls",cmd)) return LS;
if(!strcmp("pwd",cmd)) return PWD;
if(!strcmp("quit",cmd)) return QUIT;
if(!strcmp("lls",cmd)) return LLS;
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;
}
int cmd_handler(struct Msg msg,int fd)
{
char *filename = NULL;
int fdfile;
char databuf[1024] = {0};
/* 1.将字符串指令,转换为int整数类型*/
int ret = get_cmd_type(msg.cmd);
/* 2.调用switch处理ret*/
switch(ret){
case LS:
case CD:
case PWD:
write(fd, &msg, sizeof(msg));
break;
case GET:
write(fd, &msg, sizeof(msg));
break;
case PUT:
/* 1.提取目标文件名*/
filename = get_cmd_dir(msg.cmd);
strcpy(msg.data,filename);//将文件名存入data
/* 2.判断目标文件是否存在*/
if(access(filename, F_OK) == -1){
printf("this file not exit!\n");
}else{
/* 3.传输文件内容,open, read, write,close*/
fdfile = open(filename, O_RDWR);
read(fdfile,msg.contentBuf,sizeof(msg.contentBuf));
write(fd, &msg, sizeof(msg));
close(fdfile);
}
break;
case LLS:
system("ls");
break;
case LCD:
/* 1.提取目标文件名*/
filename = get_cmd_dir(msg.cmd);
if(access(filename, F_OK) == -1){
printf("this file not exit!\n");
}else{
/* 2.chdir*/
chdir(filename);
}
break;
case QUIT:
write(fd, &msg, sizeof(msg));
close(fd);
exit(1);
break;
}
return ret;
}
void cmd_server_message(struct Msg msg, int fd)
{
int n_read;
char *filename;
int newfdfile;
struct Msg msgget;
/* 将管道中的数据读到结构体msgget中*/
n_read = read(fd, &msgget, sizeof(msgget));
if(n_read == -1) {
perror("read");
exit(1);
}else if(n_read == 0){
printf("server is out,quit!\n");
exit(1);
}
if(msgget.type == DOFILE){
/* 1.获取文件名*/
filename = get_cmd_dir(msg.cmd);
/* 2.创建文件*/
newfdfile = open(filename,O_RDWR|O_CREAT,0600);
/* 3.将内容写入文件*/
write(newfdfile, msgget.data, strlen(msgget.data));
printf("ftp>");
close(newfdfile);
fflush(stdout);
}else{
/* 输出管道中的数据*/
printf("------------------------\n");
printf("%s",msgget.data);
printf("------------------------\n");
printf("ftp>");
fflush(stdout);
}
}
int main(int argc,char **argv)
{
int client_fd;
struct sockaddr_in c_addr;
struct Msg msg;
if(argc != 3)
{
printf("Input params error!\n");
exit(1);
}
/* void *memset(void *s, int c, size_t n);
* memset() 函数常用于内存空间初始化*/
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.int socket(int domain, int type, int protocol);创建网络套接字
client_fd = socket(AF_INET,SOCK_STREAM,0);
if(client_fd == -1){
perror("socket");
exit(1);
}
c_addr.sin_family = AF_INET;//网络协议设置为IPV4
c_addr.sin_port = htons(atoi(argv[2]));//uint16_t htons(uint16_t hostshort);传递端口号, htons:从主机(小端)到网络(大端)
inet_aton(argv[1],&c_addr.sin_addr); //int inet_aton(const char *cp, struct in_addr *inp);传递IP地址
//2.int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);发送连接请求
if(connect(client_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(1);
}
printf("connect ... [IP:%s]\n",inet_ntoa(c_addr.sin_addr));//char *inet_ntoa(struct in_addr in);
int mark = 0;
while(1){
/* 1.刷新msg.cmd*/
memset(&msg.cmd, 0, sizeof(msg.cmd));
if(mark == 0){printf("ftp>");}
/* 2.获取输入指令,保存到msg.cmd*/
gets(msg.cmd);
if(strlen(msg.cmd) == 0){
if(mark == 1){
printf("ftp>");
}
continue;
}
mark = 1;
/* 3.调用cmd_handler函数,将指令传入到client_fd中*/
int ret = cmd_handler(msg, client_fd);
printf("mainret:%d\n",ret);
/* 4.过滤指令*/
if(ret > IFGO){
printf("ftp>");
fflush(stdout);//清空标准输出缓冲区,刷新输出缓冲区,即将缓冲区的东西输出到屏幕上
continue; //跳出循环,不继续执行下面代码
}
if(ret == -1){
printf("cmd not exit! Please enter again!\n");
printf("ftp>");
fflush(stdout);
continue;
}
/* 5.输出指令产生的内容*/
cmd_server_message(msg, client_fd);
}
return 0;
}
服务器运行,可以有多个客户端接入,如下:

Server:

Client:

最后谢谢阅读,笔者乃小白,如有错误之处还请指正。