Linux应用编程,网络编程练习--------ftp云盘(详细版)


前言

本文章是前段时间学习linux编程的一个小总结,如果不总结的话我怕我学完就忘emmm(其实已经忘的差不多了),本文是跟着上官可编程老师与朱有鹏老师学习之后的练手作品,本人学疏才浅,暂时只能到这个地步


提示:以下是本篇文章正文内容

一、实现的功能

利用socket,建立起服务端与客户端的对接;(服务端能支持多台客户端的同时连接)

1.客户端

1、客户端输入ls指令,能获取服务端上文件列表。
2、客户端输入cd指令+路径,可以切换服务端的目录。
3、在程序运行的过程中,客户端输入lls指令 能够查看自己的文件列表
4、在程序运行的过程中,客户端输入lcd指令+路径 能够切换自己的目录。
5、客户端输入get指令+文件名,能将服务端上面的某个文件下载到客户端
6、客户端输入put指令+文件名,能将客户端上面的某个文件上传到服务端

2.服务端

1、不断监听客户端的指令(等待指令)。
2、在接收上面客户端的指令后,去执行指令。


二、实现思路

1.建立socket连接

(1)服务端建立socket连接:socket—>blind—>listen—>accept
(2)客户端建立socket连接:socket—>connect

2.客户端和服务端进行信息的交互

(1)定义指令
(2)客户端匹配用户输入的指令,客户端发送指令到服务端,服务端解析指令作出响应


三、具体流程分析(代码)

第一步:建立socket连接

服务端建立socket连接
流程:socket—>blind—>listen—>accept
代码如下(示例):

int main(int argc,char **argv)
{
        int c_fd;
        int s_fd;
        int n_read;
        char readBuf[128];

        struct Msg msg;
        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;
        
/*调试信息,这里我想要用argv,argc,进行手动指定端口号和IP地址,所以在用这两个参数之前需要先判断*/
        if(argc != 3){
                printf("parameter error!\n");
                exit(-1);
        }
        
/*用之前清空,防止数据产生混乱*/
        memset(&s_addr,0,sizeof(struct sockaddr_in));
        memset(&c_addr,0,sizeof(struct sockaddr_in));

        //1.socket
        /*s_fd:这里socket返回的是监听fd,是用来监听客户端的,不能用来和任何客户端进行读写*/
        /*AF_INET:IPv4,SOCK_STREAM:tcp协议,0:使用系统默认的方式*/
        s_fd = socket(AF_INET,SOCK_STREAM,0);
        /*如果返回值为-1说明错误,打印出错误号*/
        if(s_fd == -1){
                perror("socket");
                exit(-1);
        }
        
        //2.bind
        /*有IPv4和IPv6两种,这里我们选IPv4*/
        s_addr.sin_family = AF_INET;   // 设置地址族为IPv4
        /*htons:端口号大小端的转换(具体大小端要看对应的计算机)
		atoi:字符串转换成整型,使我们输入的数字端口号可以被识别
		*/
        s_addr.sin_port = htons(atoi(argv[2]));  // 设置地址的端口号信息
        /*inet_aton:将我们输入的地址字符串转换成网络可以识别的api*/
        inet_aton(argv[1],&s_addr.sin_addr);  // 设置IP地址
        /*(struct sockaddr *)&s_addr这之所以需要类型转换的原因是因为我们用的是IPV4的结构体,而给出的标准是IPV4和IPV6的通用结构体,所以我们一般用IPV4的结构体之后将其转换为系统给出的通用结构体,其实转不转都一样,就是会报警告而已,而这个警告是我们可以理解的警告,所以问题不大*/
        bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

        //3.listen
        /*设置最大监听10个*/
        listen(s_fd,10);
        
        //4.accept
        int clen = sizeof(struct sockaddr_in);
	/*while不停的循环接收客户端发来的信息,并且作出响应*/
        while(1){
        /*accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写。*/
                c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
                /*调试信息*/
                if(c_fd != -1){
                        perror("accept");
                }
/*成功打印出连接的地址*/
                printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
/*当我的accept每接收到一个信息就建立一个子进程*/
                if(fork() == 0){

                        while(1){
                        /*每次循环接收信息之前,清空之前的信息*/
                                memset(msg.cmd,0,sizeof(msg.cmd));
                                /*用accept建立的连接fd来读写信息*/
                                n_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);
                                }
                        }
           }
        }
/*关闭监听fd和连接fd*/
        close(c_fd);
        close(s_fd);
        return 0;
}

客户端建立socket连接
流程:socket—>connect
客户端建立socket与服务端建立socket大同小异这里就不过多赘述了
代码如下(示例):

int main(int argc,char **argv)
{
        int c_fd;
        struct Msg msg;
        struct sockaddr_in c_addr;

        if(argc != 3){
                printf("parameter error!\n");
                exit(-1);
        }

        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);
        }
        c_addr.sin_family = AF_INET;
        c_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1],&c_addr.sin_addr);

        //2.connect
        if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
                perror("connect");
                exit(-1);
        }
        printf("connect ...\n");
        return 0;

}

第二步:客户端和服务端进行信息的交互

定义指令
共有指令
代码如下(示例):


#define LS   0
#define PWD  1
#define GET  2

#define IFGO 3

#define CD   4
#define PUT  5
#define LLS  6
#define LCD  7
#define LPWD 8

#define QUIT 9
#define DOFILE 10

struct Msg
{
        int type;
        char cmd[1024];
        char buf[128];

};

服务端指令
代码如下(示例):

int get_cmd_type(char *cmd)  //检测命令并转为相应int
{
        if(!strcmp("ls",cmd))           return LS;//strcmp,比较的字符串相等时返回0
        if(!strcmp("pwd",cmd))          return PWD;
        if(!strcmp("quit",cmd))         return QUIT;

        if(strstr(cmd,"cd"))      return CD;//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串
        if(strstr(cmd,"get"))     return GET;
        if(strstr(cmd,"put"))     return PUT;

        return 10;

}

客户端指令
代码如下(示例):

int get_cmd_type(char *cmd)
{
        if(strstr(cmd,"lcd"))     return LCD;
        if(!strcmp("ls",cmd))     return LS;
        if(!strcmp("pwd",cmd))    return PWD;
        if(!strcmp("quit",cmd))   return QUIT;

        if(strstr(cmd,"cd"))      return CD;
        if(strstr(cmd,"get"))     return GET;
        if(strstr(cmd,"put"))     return PUT;

        if(!strcmp("lls",cmd))    return LLS;

        return -1;

}

客户端匹配用户输入的指令,客户端发送指令到服务端,服务端解析指令作出响应
客户端:
代码如下(示例):

/*这是一个分割命令的函数,为了实现cd指令+路径*/
char *getDir(char *cmd)
{
        char *p;
        p = strtok(cmd," ");
        p = strtok(NULL," ");
        return p;

}
/*当客处理用户输入指令函数*/
int cmd_handler(struct Msg msg,int fd)
{
        char *dir = NULL;
        char buf[32];
        int ret;
        int filefd;

        ret = get_cmd_type(msg.cmd); //cmd转为int类型
/*根据不同的指令,进行不同的操作*/
        switch(ret){
                case LS:
                case CD:
                case PWD:
                case GET:
                        msg.type = 0;
                        write(fd,&msg,sizeof(msg));
                        break;

                case PUT:
                        strcpy(buf,msg.cmd);
                        dir = getDir(buf);  //获取第二个参数
                        if(access(dir,F_OK) == -1){  //判断文件是否存在
                                printf("%s not exsit\n",dir);
                        }else{
                                filefd = open(dir,O_RDWR);
                                read(filefd,msg.buf,sizeof(msg.buf));  //读文件内容
                                close(filefd);

                                write(fd,&msg,sizeof(msg));
                        }
                        break;
		case LPWD:
                        printf("-------------------------------------\n\n");
                        system("pwd");
                        printf("\n-------------------------------------\n");
                        break;

                case LLS:
                	printf("-------------------------------------\n\n");
                        system("ls");
                        printf("\n-------------------------------------\n");
                        break;
                        
                case LCD:
                        dir = getDir(msg.cmd);  //获取第二个参数
                        int t = chdir(dir);  //cd
                        break;

                case QUIT:
                        strcpy(msg.cmd,"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 msgget;
        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.cmd);
                newfilefd = open(p,O_RDWR|O_CREAT,0600);
                write(newfilefd,msgget.cmd,strlen(msgget.cmd));
                putchar('>');
                fflush(stdout);
        }

        else{
                printf("-------------------------------------\n");
                printf("\n%s\n",msgget.cmd);
                printf("-------------------------------------\n");
                putchar('>');
                fflush(stdout);
        }

}

服务端
代码如下(示例):

/*解析客户端发来的指令,并对应作出响应*/
void msg_handler(struct Msg msg,int fd)
{
        char dataBuf[1024] = {0};
        char *file = NULL;
        int fdfile;
        printf("cmd: %s\n",msg.cmd);  //打印命令
        int ret = get_cmd_type(msg.cmd);  //将命令转为int类型

        switch(ret){
                case LS:
                case PWD:
                        msg.type = 0;
                        FILE *r = popen(msg.cmd,"r");  //执行命令,返回结果
                        fread(msg.cmd,sizeof(msg.cmd),1,r);  //将结果读到msg.cmd
                        write(fd,&msg,sizeof(msg));  //写入客户端
                        break;

                case CD:
                        msg.type = 1;
                        char *dir = getDesDir(msg.cmd);  //获取第二个参数
                        printf("dir:%s\n",dir);
                        chdir(dir);  //系统调用函数,同cd
                        break;

                case GET:
                        file = getDesDir(msg.cmd);

                        if(access(file,F_OK) == -1){  //判断文件是否存在
                                strcpy(msg.cmd,"Have Not File!");
                                write(fd,&msg,sizeof(msg));
                        }else{
                                msg.type = DOFILE;  //设置标志
                                fdfile = open(file,O_RDWR);
                                read(fdfile,dataBuf,sizeof(dataBuf));  //读文件内容
                                close(fdfile);

                                strcpy(msg.cmd,dataBuf);
                                write(fd,&msg,sizeof(msg));
                        }
                        break;
               case PUT:
                        fdfile = open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0600);
                        write(fdfile,msg.buf,strlen(msg.buf));
                        close(fdfile);
                        break;

                case QUIT:
                        printf("client quit!\n");
                        exit(-1);

        }

}

四、整体流程分析(代码)

1.客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include <unistd.h>

int get_cmd_type(char *cmd)
{
        if(strstr(cmd,"lcd"))     return LCD;
        if(!strcmp("ls",cmd))     return LS;
        if(!strcmp("pwd",cmd))    return PWD;
        if(!strcmp("quit",cmd))   return QUIT;

        if(strstr(cmd,"cd"))      return CD;
        if(strstr(cmd,"get"))     return GET;
        if(strstr(cmd,"put"))     return PUT;

        if(!strcmp("lls",cmd))    return LLS;

        return -1;

}
/*==============以上是定义指令=====================*/
char *getDir(char *cmd)
{
        char *p;
        p = strtok(cmd," ");
        p = strtok(NULL," ");
        return p;

}

int cmd_handler(struct Msg msg,int fd)
{
        char *dir = NULL;
        char buf[32];
        int ret;
        int filefd;

        ret = get_cmd_type(msg.cmd); //cmd转为int类型

        switch(ret){
                case LS:
                case CD:
                case PWD:
                case GET:
                        msg.type = 0;
                        write(fd,&msg,sizeof(msg));
                        break;

                case PUT:
                        strcpy(buf,msg.cmd);
                        dir = getDir(buf);  //获取第二个参数
                        if(access(dir,F_OK) == -1){  //判断文件是否存在
                                printf("%s not exsit\n",dir);
                        }else{
                                filefd = open(dir,O_RDWR);
                                read(filefd,msg.buf,sizeof(msg.buf));  //读文件内容
                                close(filefd);

                                write(fd,&msg,sizeof(msg));
                        }
                        break;
		case LPWD:
                        printf("-------------------------------------\n\n");
                        system("pwd");
                        printf("\n-------------------------------------\n");
                        break;

                case LLS:
                	printf("-------------------------------------\n\n");
                        system("ls");
                        printf("\n-------------------------------------\n");
                        break;
                        
                case LCD:
                        dir = getDir(msg.cmd);  //获取第二个参数
                        int t = chdir(dir);  //cd
                        break;

                case QUIT:
                        strcpy(msg.cmd,"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 msgget;
        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.cmd);
                newfilefd = open(p,O_RDWR|O_CREAT,0600);
                write(newfilefd,msgget.cmd,strlen(msgget.cmd));
                putchar('>');
                fflush(stdout);
        }

        else{
                printf("-------------------------------------\n");
                printf("\n%s\n",msgget.cmd);
                printf("-------------------------------------\n");
                putchar('>');
                fflush(stdout);
        }

}
/*==============以上是处理服务端发来的消息=====================*/
/*==============以下是建立socket连接=====================*/
int main(int argc,char **argv)
{
        int c_fd;
        struct Msg msg;
        struct sockaddr_in c_addr;

        if(argc != 3){
                printf("parameter error!\n");
                exit(-1);
        }

        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);
        }
        c_addr.sin_family = AF_INET;
        c_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1],&c_addr.sin_addr);

        //2.connect
        if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
                perror("connect");
                exit(-1);
        }
        printf("connect ...\n");
        int mark = 0;

        while(1){

                memset(msg.cmd,0,sizeof(msg.cmd));
                if(mark == 0) printf(">");

                gets(msg.cmd);

                if(strlen(msg.cmd) == 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;

}


2.服务端

代码如下(示例):

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include <unistd.h>

int get_cmd_type(char *cmd)  //检测命令并转为相应int
{
        if(!strcmp("ls",cmd))           return LS;//strcmp,比较的字符串相等时返回0
        if(!strcmp("pwd",cmd))          return PWD;
        if(!strcmp("quit",cmd))         return QUIT;

        if(strstr(cmd,"cd"))      return CD;//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串
        if(strstr(cmd,"get"))     return GET;
        if(strstr(cmd,"put"))     return PUT;

        return 10;

}
/*==============以上是定义指令=====================*/
char *getDesDir(char *cmsg)  //取空格分开的的第二个字符串
{
        char *p;
        p = strtok(cmsg," ");//char *strtok(char s[], const char *delim);分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
        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.cmd);  //打印命令
        int ret = get_cmd_type(msg.cmd);  //将命令转为int类型

        switch(ret){
                case LS:
                case PWD:
                        msg.type = 0;
                        FILE *r = popen(msg.cmd,"r");  //执行命令,返回结果
                        fread(msg.cmd,sizeof(msg.cmd),1,r);  //将结果读到msg.cmd
                        write(fd,&msg,sizeof(msg));  //写入客户端
                        break;

                case CD:
                        msg.type = 1;
                        char *dir = getDesDir(msg.cmd);  //获取第二个参数
                        printf("dir:%s\n",dir);
                        chdir(dir);  //系统调用函数,同cd
                        break;

                case GET:
                        file = getDesDir(msg.cmd);

                        if(access(file,F_OK) == -1){  //判断文件是否存在
                                strcpy(msg.cmd,"Have Not File!");
                                write(fd,&msg,sizeof(msg));
                        }else{
                                msg.type = DOFILE;  //设置标志
                                fdfile = open(file,O_RDWR);
                                read(fdfile,dataBuf,sizeof(dataBuf));  //读文件内容
                                close(fdfile);

                                strcpy(msg.cmd,dataBuf);
                                write(fd,&msg,sizeof(msg));
                        }
                        break;
               case PUT:
                        fdfile = open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0600);
                        write(fdfile,msg.buf,strlen(msg.buf));
                        close(fdfile);
                        break;

                case QUIT:
                        printf("client quit!\n");
                        exit(-1);

        }

}
/*==============以上是处理客户端发来的消息=====================*/
/*==============以下是建立socket连接=====================*/
int main(int argc,char **argv)
{
        int c_fd;
        int s_fd;
        int n_read;
        char readBuf[128];

        struct Msg msg;
        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        if(argc != 3){
                printf("parameter error!\n");
                exit(-1);
        }

        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);
        }
        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1],&s_addr.sin_addr);

        //2.bind
        bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

        //3.listen
        listen(s_fd,10);
        //4.accept
        int clen = sizeof(struct sockaddr_in);

        while(1){
                c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
                if(c_fd != -1){
                        perror("accept");
                }

                printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));

                if(fork() == 0){

                        while(1){
                                memset(msg.cmd,0,sizeof(msg.cmd));
                                n_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);
                                }
                        }
           }
        }

        close(c_fd);
        close(s_fd);
        return 0;
}

3.共有文件:

代码如下(示例):

#define LS   0
#define PWD  1
#define GET  2

#define IFGO 3

#define CD   4
#define PUT  5
#define LLS  6
#define LCD  7
#define LPWD 8

#define QUIT 9
#define DOFILE 10

struct Msg
{
        int type;
        char cmd[1024];
        char buf[128];

};


五、实现效果(图片)

请添加图片描述


六、反思与总结

1.socket返回的fd叫做监听fd,是用来监听客户端的,不能用来和任何客户端进行读写;accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写
2.(struct sockaddr *)&s_addr这之所以需要类型转换的原因是因为我们用的是IPV4的结构体,而给出的标准是IPV4和IPV6的通用结构体,所以我们一般用IPV4的结构体之后将其转换为系统给出的通用结构体
3. htons:端口号大小端的转换(具体大小端要看对应的计算机)
atoi:字符串转换成整型,使我们输入的数字端口号可以被识别
inet_aton:将我们输入的地址字符串转换成网络可以识别的api
除了上面用到的,我们常用到的一些函数
inet_addr、inet_ntoa、inet_aton
inet_pton、inet_ntop
4.记得在每次数据交换的时候都要清空数据,不然会有上一次的数据进行干扰

那么这个小练习到这里就结束了,里面用到了多进程,socket相关的一些知识,而且虽然是个小练习但是整个框架就是一个项目的缩影,对于刚接触linux编程的同学来说联系一下还是有很大的好处的

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值