zoukankan      html  css  js  c++  java
  • Linux网络编程综合运用之MiniFtp实现(十)

    上次已经实现了将当前目录打印出来的效果,这次则实现这些列表信息显示在FTP客户端中,先将测试代码注释掉:

    在实现之前,还是先来看下vsftpd的效果:

    这里先实现POST主动模式,上面就将目录列表显示给了FTP客户端,在显示目录列表之前,首先需要创建一个数据连接。主动模式是先发送一个PORT命令,紧接着是LIST的命令:

    下面来照着实现,在实现之前,先来看下目前miniftpd的效果,先将站点配置成PORT主动模式:

    再来连接:

    【函数说明】:sscanf() - 从一个字符串中读进与指定格式相符的数据。

    然后将这个地址信息先存起来,所以先在Session结构体中增加一个变量:

    然后对其进行初始化:

    接着将这个地址信息实例化:

    接着来给客户端响应200:

    这时do_port方法就实现完了,编译运行一下:

     下面先来回顾一下PORT主动模式的步骤:

      接着按着第三步来实现LIST命令,该函数的实现可以分为以下几步:

    接着来实现创建数据连接的方法,第一步需要进行判断:

    下面来实现port_active的判断:

    接下来回到get_transfer_fd()函数:

    其中服务器POST主动模式需要主动绑定20端口号,但是这里会存在一个问题,调用tcp_client(20)的是在FTP服务器进程,当一个用户,比如说webor2006登录时,会将这个进程的用户ID和组ID改变为webor2006所对应的UID和GID,这时它是没有权限绑定20端口号的,之前也描述过,一个客户端过来,应该采取两个进程的方式:nobody进程用于协助数据连接的创建,这里先以一个进程的方式实现,之后会改用两人进程,所以这里先不传端口号:

    这时将这个数据套接字绑定到session当中,所以需要再新建一个变量:

    接下来回到do_list()这个函数继续实现:

    接下来需要传输列表:

    实现也得修加修改:

    int list_common(session_t *sess)
    {
        //打开当前目录
        DIR *dir = opendir(".");
        if (dir == NULL)
        {
            return 0;
        }
    
        //读取目录并进行遍历
        struct dirent *dt;
        struct stat sbuf;
        while ((dt = readdir(dir)) != NULL)
        {    //获取文件的状态
            if (lstat(dt->d_name, &sbuf) < 0)
            {
                continue;
            }
    
            if (dt->d_name[0] == '.')
                continue;
    
            char perms[] = "----------";
            perms[0] = '?';
            //获取文件类型
            mode_t mode = sbuf.st_mode;
            switch (mode & S_IFMT)
            {
            case S_IFREG://普通文件
                perms[0] = '-';
                break;
            case S_IFDIR://目录文件
                perms[0] = 'd';
                break;
            case S_IFLNK://链接文件
                perms[0] = 'l';
                break;
            case S_IFIFO://管道文件
                perms[0] = 'p';
                break;
            case S_IFSOCK://套接字文件
                perms[0] = 's';
                break;
            case S_IFCHR://字符设备文件
                perms[0] = 'c';
                break;
            case S_IFBLK://块设备文件
                perms[0] = 'b';
                break;
            }
    
            //获取文件9个权限位
            if (mode & S_IRUSR)
            {
                perms[1] = 'r';
            }
            if (mode & S_IWUSR)
            {
                perms[2] = 'w';
            }
            if (mode & S_IXUSR)
            {
                perms[3] = 'x';
            }
            if (mode & S_IRGRP)
            {
                perms[4] = 'r';
            }
            if (mode & S_IWGRP)
            {
                perms[5] = 'w';
            }
            if (mode & S_IXGRP)
            {
                perms[6] = 'x';
            }
            if (mode & S_IROTH)
            {
                perms[7] = 'r';
            }
            if (mode & S_IWOTH)
            {
                perms[8] = 'w';
            }
            if (mode & S_IXOTH)
            {
                perms[9] = 'x';
            }
            //获取特珠权限位
            if (mode & S_ISUID)
            {
                perms[3] = (perms[3] == 'x') ? 's' : 'S';
            }
            if (mode & S_ISGID)
            {
                perms[6] = (perms[6] == 'x') ? 's' : 'S';
            }
            if (mode & S_ISVTX)
            {
                perms[9] = (perms[9] == 'x') ? 't' : 'T';
            }
            
            //格式化信息
            char buf[1024] = {0};
            int off = 0;
            off += sprintf(buf, "%s ", perms);//连接权限位
            off += sprintf(buf + off, " %3d %-8d %-8d ", sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid);//连接连接数、uid、gid
            off += sprintf(buf + off, "%8lu ", (unsigned long)sbuf.st_size);//连接文件大小,以8位的长度展现
    
            const char *p_date_format = "%b %e %H:%M";
            struct timeval tv;
            gettimeofday(&tv, NULL);
            time_t local_time = tv.tv_sec;
            if (sbuf.st_mtime > local_time || (local_time - sbuf.st_mtime) > 60*60*24*182)
            {
                p_date_format = "%b %e  %Y";
            }
    
            char datebuf[64] = {0};
            struct tm* p_tm = localtime(&local_time);
            strftime(datebuf, sizeof(datebuf), p_date_format, p_tm);
            off += sprintf(buf + off, "%s ", datebuf);
            if (S_ISLNK(sbuf.st_mode))
            {
                char tmp[1024] = {0};
                readlink(dt->d_name, tmp, sizeof(tmp));
                off += sprintf(buf + off, "%s -> %s
    ", dt->d_name, tmp);
            }
            else
            {
                off += sprintf(buf + off, "%s
    ", dt->d_name);
            }
            //printf("%s", buf);
            writen(sess->data_fd, buf, strlen(buf));//由原来的打印在屏幕中,改为输出到数据套接字中
        }
    
        return 1;
    }

    另外在关闭数据套接字之后,将数据还原成默认值:

    另外在get_transfer_fd函数中,有个内存需要释放一下:

    至此PORT目录列表显示的代码已经写完,下面编译运行看下效果:

    这里有几个是需要注意的:

    ①、如果没有关闭套接字,目录列表是没法显示滴:

    所以需要注意,将代码还原。

    ②、目前无法绑定20端:

    编译运行:

    好了,这次先学到这,下次继续~~

  • 相关阅读:
    移植Fatfs文件系统到工程中
    利用官方的ucosiii包中测试板的工程移植到属于自己的开发板(stmf103ZE)上
    华为Liteos移植到stm32F03ZE
    如何使用postman模拟https的post和get请求
    ubuntu 16 搭建只能上传不可下载删除ftp服务
    使用STM32F103ZET霸道主板实现LCD显示屏显示
    资源任务调度算法实现(大数据云计算作业来的)
    STM32F10xx(高容量)WiFi模块的初始化和使用
    使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)
    单片机期末实训---- 密码锁和交通灯
  • 原文地址:https://www.cnblogs.com/webor2006/p/4640886.html
Copyright © 2011-2022 走看看