自己实现一个简单的ftp软件

linux应用编程 专栏收录该内容
11 篇文章 0 订阅

近期面试有个机试题是写个ftp软件,自己写完不想就扔了,就放到这里大家参考一下,也帮我优化优化,没什么太难的也就不需要什么注释了

消息格式:

cmd | size | data

服务端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <dirent.h>
#include <stdlib.h>

#define COMPARE(a, b, c) (strcmp(a, c) b 0) 
#define HEAD_SIZE 4

char *packet(int size, const char *data);

int main(int argc, const char *argv[])
{
    int sockfd, acceptfd;
    struct sockaddr_in myaddr;
    memset(&myaddr, 0, sizeof(myaddr));
    socklen_t addrlen = sizeof(struct sockaddr_in);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) return -1; 

    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(6060);
    myaddr.sin_addr.s_addr = inet_addr("192.168.255.128");

    int optval = 1;
    socklen_t optlen = sizeof(optval);
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen); //设置端口重用

    if (bind(sockfd, (struct sockaddr *)&myaddr, addrlen) == -1) {
        printf("bind faild\n");
        return -1;
    }
    if (listen(sockfd, 10) == -1) {
        printf("listen faild\n");
        return -1;
    }
    if ((acceptfd=accept(sockfd, (struct sockaddr *)&myaddr, &addrlen)) == -1) {
        printf("acceptfd faild\n");
        return -1;
    }

    ssize_t recvlen, sendlen;
    char buf[256];
    DIR *dirfd;
    while (1) { //注意服务端使用的消息格式是size|data
        recv(acceptfd, buf, 4, 0); //先接收开始的四个字节cmd
        if (COMPARE(buf, ==, "get")) {
            memset(buf, 0, 0);
            recv(acceptfd, buf, 4, 0);
            int size = *(int *)buf;
            recv(acceptfd, buf, size, 0);
            printf("%s\n", buf);
            FILE *fp = fopen(buf, "rb");
            if (fp == NULL) {
                printf("file open failed\n");
                break;
            }
            memset(buf,  0, 0); //使用buf前先清空一下,防止出现其它不想要的字符
            size_t readlen = fread(buf, 1, sizeof(buf), fp);
            char *tmp = packet(readlen, buf);
            send(acceptfd, tmp, HEAD_SIZE+readlen, 0);
            free(tmp);
            fclose(fp);
        } else if (COMPARE(buf, ==, "ls")) {
            printf("%s\n", buf);
            if ((dirfd = opendir("./")) == NULL) {
                char *tmp = packet(8, "no file");
                send(sockfd, tmp, HEAD_SIZE + 8, 0);
                free(tmp);
                printf("file open failed\n");
            } else {
                struct dirent *pdirent;
                while ((pdirent = readdir(dirfd)) != NULL) {
                    if (pdirent->d_name[0] == '.')
                        continue;
                    char *tmp = packet(strlen(pdirent->d_name)+1, pdirent->d_name);
                    sendlen = send(acceptfd, tmp, HEAD_SIZE + strlen(pdirent->d_name)+1, 0);
                    if (sendlen == 0) break;
                    free(tmp);
                }
                char *tmp = packet(4, "ok");
                send(acceptfd, tmp, HEAD_SIZE + 3, 0);
            }
        } else if (COMPARE(buf, ==, "put")) {
            printf("%s\n", buf);
            recv(acceptfd, buf, 4, 0);
            int size = *(int *)buf;
            memset(buf, 0, 0);
            size_t recvlen = recv(acceptfd, buf, size, 0);
            FILE *fp = fopen("./update.txt", "wb");
            if (fp == NULL) {
                printf("file open failed\n");
                break;
            }
            fwrite(buf, 1, recvlen, fp);
            strcpy(buf, "ok");
            send(acceptfd, buf, 3, 0);
            fclose(fp);
        }
    }

    close(sockfd);
    close(acceptfd);
    return 0;
}

char *packet(int size, const char *data) {
    char *ret = malloc(sizeof(int) + size);

    *(int *)ret = size;
    memcpy(ret + HEAD_SIZE, data, size);

    return ret;
}

客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>

#define COMPARE(a, b, c) (strcmp(a, c) b 0)
#define HEAD_SIZE 8

char *packet(const char *cmd, int size, const char *data);

int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in myaddr;
    memset(&myaddr, 0, sizeof(myaddr));
    socklen_t addrlen = sizeof(struct sockaddr_in);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) return -1; 

    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(6060);
    myaddr.sin_addr.s_addr = inet_addr("192.168.255.128");

    if (connect(sockfd, (struct sockaddr *)&myaddr, addrlen) == -1) {
        printf("connect faild\n");
        return -1;
    }

    ssize_t recvlen, sendlen;
    char buff[256];
    char send_buff[256];
    char recv_buff[256];
    char *token = NULL;
    char *delim = " ";
    while (1) {

        printf("ftp > ");

        fgets(buff, sizeof(buff), stdin);
        buff[strlen(buff) - 1] = 0;
        char *tmp = buff;
        if (*tmp == 0) continue;
        token = strtok(tmp, delim);
#if 1
        if (COMPARE(token, ==, "ls")) {
            char *send_data = packet("ls", 0, NULL);
            send(sockfd, send_data, HEAD_SIZE, 0);
            free(send_data);
            while (1) {
                recvlen = recv(sockfd, recv_buff, 4, 0);
                if (recvlen <= 0) break;
                int size = *(int *)recv_buff;
                memset(recv_buff, 0, 0);
                recv(sockfd, recv_buff, size, 0);
                if (COMPARE(recv_buff, ==, "ok")) break;
                printf("%s  ", recv_buff);
                fflush(stdout);
            }
            printf("\n");
        } else if (COMPARE(token, ==, "get")) {
            char *data = strtok(NULL, delim);
            char *send_data = packet("get", strlen(data) + 1, data);
            send(sockfd, send_data, HEAD_SIZE+strlen(data)+1, 0);
            free(send_data);

            recvlen = recv(sockfd, recv_buff, 4, 0);
            if (recvlen <= 0) break;
            int size = *(int *)recv_buff;
            memset(recv_buff, 0, 0);
            recvlen = recv(sockfd, recv_buff, size, 0);
            FILE *fp = fopen(data, "wb");
            size_t len = fwrite(recv_buff, 1, recvlen, fp);
            printf("file download\n");
            fclose(fp);
        } else if (COMPARE(token, ==, "put")) {
            char *data = strtok(NULL, delim);
            FILE *fp = fopen(data, "rb");
            if (fp == NULL) {
                printf("file open failed\n");
                continue;
            }
            size_t readlen = fread(send_buff, 1, sizeof(send_buff), fp);
            char *tmp = packet("put", readlen, send_buff);
            send(sockfd, tmp, HEAD_SIZE+readlen, 0);
            free(tmp);
            fclose(fp);

            memset(recv_buff, 0, 0);
            recv(sockfd, recv_buff, 3, 0);
            if (COMPARE(recv_buff, ==, "ok")) 
                printf("put file finish\n");
        } else if (COMPARE(token, ==, "quit")) {
            close(sockfd);
            return 0;
        }
#endif
    }

    close(sockfd);
    return 0;
}

char *packet(const char *cmd, int size, const char *data) {
    char *ret = malloc(sizeof(char *) + sizeof(int) + size);

    memcpy(ret, cmd, 4);
    *(int *)(ret + 4) = size;
    if (size != 0) 
        memcpy(ret + HEAD_SIZE, data, size);

    return ret;
}

显示结果:
这里写图片描述

一个简单的ftp软件就实现了,不过有太多的缺陷和问题,等以后修改了再更新

  • 2
    点赞
  • 0
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
自己写的ftp服务端程序代码,支持{"USER", do_user }, {"PASS", do_pass }, {"CWD", do_cwd }, {"XCWD", do_cwd }, {"CDUP", do_cdup }, {"REIN", do_rein },//重新初始化,此命令终止USER,重置所有参数,控制连接仍然打开,用户可以再次使用USER命令 {"QUIT", do_quit }, /*------------传输参数命令------------*/ {"PORT", do_port },//数据端口,主要向服务器发送客户数据连接的端口 //格式为PORT h1,h2,h3,h4,p1,p2,其中32位的IP地址用h1,h2,h3,h4表示,16位的TCP端口号用p1,p2表示 {"PASV", do_pasv },//此命令要求服务器数据传输进程在指定的数据端口侦听,进入被动接收请求的状态 {"TYPE", do_type },//文件类型,可指定ASCII码、EBCDIC码、Image、本地类型文件等参数 /*------------服务命令----------------*/ {"RETR", do_retr },//下载文件 {"STOR", do_stor },//上传 {"APPE", do_appe },//上传,如文件已存在,数据附加到尾部 {"REST", do_rest },//重新开始 {"RNFR", do_rnfr }, {"RNTO", do_rnto },//重命名文件或目录 {"ABOR", do_abor },//异常终止 {"DELE", do_dele },//删除文件 {"RMD", do_rmd },//删除目录 {"XRMD", do_rmd }, {"MKD", do_mkd },//新建目录 {"XMKD", do_mkd }, {"PWD", do_pwd },//打印当前目录 {"XPWD", do_pwd }, {"LIST", do_list },//列目录详细清单 {"NLST", do_nlst },//列目录短清单 {"SYST", do_syst },//获取系统信息 {"STAT", do_stat },//返回服务器状态 {"SIZE", do_size },//获得文件大小 {"HELP", do_help }, {"NOOP", do_noop }, {"SITE", do_site }, }等命令
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

吾辈中人

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值