【TCP实现文件传输 --文件上传和下载】

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SEARCH 1  //查目录
#define DOWN 2   //下载
#define FAIL 3    //失败
#define SUCCESS 4 //成功
#define END 5     //查询目录结束
#define UP 6      //上传

struct msg
{
    int type; //要执行的操作
    int flag; //记录成功或者失败
    char buff[128];
    char fname[50];
};
//查询目录
int search_serverdir(int sockfd, struct msg info)
{
    info.type = SEARCH;
    if(send(sockfd, &info, sizeof(info), 0)<0)
    {
        perror("send");
        return -1;
    }
    while (1)
    {
        int res=recv(sockfd, &info, sizeof(info), 0);
        if(res<0)
        {
            perror("recv");
            return -1;
        }
        if(0==res)
        {
            printf("服务器断开连接\n");
            break;
        }
        if (info.flag == END)
        {
            break;            
        }
        printf("%s \n", info.buff);
    }
    printf("\n");
    return 0;
}
//下载文件
int ftp_download(int sockfd, struct msg info)
{
    int res;
    info.type = DOWN;
    printf("请输入要下载的文件名:");
    fgets(info.fname, sizeof(info.fname), stdin);
    info.fname[strlen(info.fname) - 1] = '\0';

    send(sockfd, &info, sizeof(info), 0);
    res=recv(sockfd, &info, sizeof(info), 0);
    if(res<0)
    {
        perror("recv");
        return -1;
    }
    if(0==res)
    {
        printf("服务器断开连接\n");
        return -1;
    }
    if (info.flag == FAIL)
    {
        printf("服务器打开文件失败\n");
        return -1;
    }
    if (info.flag == SUCCESS)
        printf("正在下载.....\n");

    FILE *fp;
    fp = fopen(info.fname, "w");
    if (fp == NULL)
    {
        perror("fopen");
        return -1;
    }


    while (1)
    {
        bzero(info.buff,sizeof(info.buff));
        res = recv(sockfd, info.buff, sizeof(info.buff), 0);
        if (res < 0)
        {
            perror("recv");
            return -1;
        }
        fwrite(info.buff, 1, res, fp);
        if (res < sizeof(info.buff))
            break;
    }
    printf("下载成功\n");
    fclose(fp);
    return 0;
}
//上传文件
int upload_ftp(int sockfd, struct msg info)
{
    int res;
    info.type = UP;
START:
    printf("请输入要上传的文件名:");
    fgets(info.fname, sizeof(info.fname), stdin);
    info.fname[strlen(info.fname) - 1] = '\0';
    if(access(info.fname,F_OK)!=0)
    {
        printf("文件不存在\n");
        goto START;
    }
    send(sockfd, &info, sizeof(info), 0);
    res=recv(sockfd, &info, sizeof(info), 0);
    if(res<0)
    {
        perror("recv");
        return -1;
    }
    if(0==res)
    {
        printf("服务器断开连接\n");
        return -1;
    }
    if (info.flag == FAIL)
    {
        printf("服务器出现文件上传错误!\n");
        return -1;
    }
    if (info.flag == SUCCESS)
        printf("正在上传中....\n");

    FILE *fp;
    char name[100];
    sprintf(name, "./%s", info.fname);
    fp = fopen(name, "r");
    if (fp == NULL)
    {
        perror("fopen:");
        return -1;
    }
    while (1)
    {
        bzero(info.buff,sizeof(info.buff));
        res = fread(info.buff, 1, sizeof(info.buff), fp);
        if (send(sockfd, info.buff, res, 0) < 0)
        {
            perror("send");
            return -1;
        }

        if (res < sizeof(info.buff) && feof(fp) != 0) //文件结尾
            break;
    }
    printf("上传成功\n");
    fclose(fp);
}

int main(int argc, const char *argv[])
{
    //创建套接字
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    //绑定信息
    struct sockaddr_in sin;

    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    sin.sin_addr.s_addr = inet_addr("192.168.31.90");
    // connect
    if (connect(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("connect");
        return -1;
    }

    char c = 0;
    struct msg info;
    while (1)
    {
        system("clear");
        printf("=======================TCP文件传输客户端============================\n");
        printf("-----------------------------功能菜单------------------------------\n");
        printf("\t\t\t1.查询文件\n");
        printf("\t\t\t2.下载文件\n");
        printf("\t\t\t3.上传文件\n");
        printf("\t\t\t0.退出系统\n");
        printf("-------------------------------------------------------------------\n");
        printf("请选择你要执行的操作:");
        c = getchar();
        while (getchar() != 10)
            ;

        switch (c)
        {
        case '1':
            search_serverdir(sockfd, info);
            break;
        case '2':
            ftp_download(sockfd, info);
            break;
        case '3':
            upload_ftp(sockfd, info);
            break;
        case '0':
            close(sockfd);
            return 0;
        default:
            printf("输入错误,请重新输入\n");
        }

        printf("点击任意处清屏:");
        while (getchar() != 10)
            ;
    }
    return 0;
}

服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#define SEARCH 1
#define DOWN 2
#define FAIL 3
#define SUCCESS 4
#define END 5
#define UP 6
struct msg
{
    int type;
    int flag;
    char buff[128];
    char fname[50];
};

//回收僵尸进程
typedef void (*sighandler_t)(int);
void hander(int sig)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}
//将指定目录中的所有文件名发给客户端
int search_serverdir(int acceptfd, struct msg info)
{
    DIR *dp = opendir("./download/");
    if (NULL == dp)
    {
        perror("open dir");
        return -1;
    }
    struct dirent *dir = NULL;

    while (1)
    {
        dir = readdir(dp);
        if (NULL == dir)
        {
            if (0 == errno)
            {
                //printf("读取完毕\n");
                break;
            }
            else
            {
                perror("readdir error");
            }
        }
        if (dir->d_name[0] != '.') //去掉隐藏文件
        {
            bzero(info.buff,sizeof(info.buff));
            strcpy(info.buff, dir->d_name);
            if (send(acceptfd, &info, sizeof(info), 0) < 0)
            {
                perror("send");
                return -1;
            }
        }
    }
    info.flag = END;                                // readdir完成
    if (send(acceptfd, &info, sizeof(info), 0) < 0) //把标志位发出
    {
        perror("send");
        return -1;
    }

}
//将某文件中的内容传给客户端
int ftp_download(int acceptfd, struct msg info)
{
    FILE *fp;

    char name[100];
    sprintf(name, "./download/%s", info.fname);
    fp = fopen(name, "r");
    if (fp == NULL)
    {
        info.flag = FAIL; //服务器打开文件失败
        if (send(acceptfd, &info, sizeof(info), 0) < 0)
        {
            perror("send");
            return -1;
        }
        return -1;
    }
    else
    {
        info.flag = SUCCESS; //服务器打开文件成功
        send(acceptfd, &info, sizeof(info), 0);
    }

    int res;
    while (1)
    {
        bzero(info.buff, sizeof(info.buff));
        res = fread(info.buff, 1, sizeof(info.buff), fp);
        if (send(acceptfd, info.buff, res, 0) < 0)
        {
            perror("send");
            return -1;
        }

        if (res < sizeof(info.buff) && feof(fp) != 0) //读到文件结尾
            break;
    }

    fclose(fp);
    return 0;
}
//接收客户端上传的文件内容
int upload_ftp(int connfd, struct msg info)
{
    FILE *fp;
    char name[100];
    sprintf(name, "./download/%s", info.fname);
    //新建文件
    fp = fopen(name, "w");
    if (fp == NULL)
    {
        info.flag = FAIL; //打开文件失败
        if (send(connfd, &info, sizeof(info), 0) < 0)
        {
            perror("send");
            return -1;
        }
        return -1;
    }
    else
    {
        info.flag = SUCCESS;
        if (send(connfd, &info, sizeof(info), 0) < 0)
        {
            perror("send");
            return -1;
        }
    }
    //接收上传文件的内容
    int res;
    while (1)
    {
        bzero(info.buff, sizeof(info.buff));
        res = recv(connfd, info.buff, sizeof(info.buff), 0);
        if (res < 0)
        {
            perror("recv");
            return -1;
        }
        fwrite(info.buff, 1, res, fp);
        if (res < sizeof(info.buff)) //写完
            break;
    }
    fclose(fp);

    return 0;
}

int main(int argc, const char *argv[])
{
    sighandler_t s = signal(SIGCHLD, hander);
    if (SIG_ERR == s)
    {
        perror("signal");
        return -1;
    }
    int sockfd;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    //允许端口快速重用
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        perror("setsockopt");
        return -1;
    }
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    sin.sin_addr.s_addr = inet_addr("192.168.31.90");

    if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("bind");
        return -1;
    }

    if (listen(sockfd, 10) == -1)
    {
        perror("listen");
        return -1;
    }

    int acceptfd;
    struct sockaddr_in cin;
    socklen_t len = sizeof(cin);
    pid_t pid;
    struct msg info;
    int res;
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&cin, &len);
        if (acceptfd < 0)
        {
            perror("accept");
            return -1;
        }

        pid = fork();
        if (pid < 0)
        {
            perror("fork");
            return -1;
        }
        else if (0 == pid)
        {
            close(sockfd);
            while (1)
            {
                res = recv(acceptfd, &info, sizeof(info), 0);
                if (res < 0)
                {
                    perror("recv");
                    return -1;
                }
                if (0 == res)
                {
                    printf("客户端退出\n");
                    goto end;
                }
                switch (info.type)
                {
                case SEARCH:
                    search_serverdir(acceptfd, info);
                    break;
                case DOWN:
                    ftp_download(acceptfd, info);
                    break;
                case UP:
                    upload_ftp(acceptfd, info);
                    break;
                }
            }
        }
        else
        {
            close(acceptfd);
        }
    }
end:
    return 0;
}

测试

查看服务器目录文件

在这里插入图片描述

下载文件

在这里插入图片描述

上传文件

在这里插入图片描述

小结

在实现的过程中,遇到了一些问题,最让人头大的还是TCP的粘包现象,在传输过程中,有很大的问题,我先前定义一个较大的数组,把结构体的信息放在数组中发送,接收时再把数组内容强转为结构体。也可以避免粘包现象,但是下载和上传功能实现不是太好。最终我修改了结构体的大小,也没有发生粘包现象,各功能均可以实现。但是总感觉不是很好。

  • 2
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
TCP文件上传下载实现需要使用Socket编程。以下是一个简单的Python实现文件上传: 1. 客户端连接服务器,并发送文件名和文件大小。 2. 服务器接收到文件名和文件大小后,创建一个新的文件,并等待客户端发送文件内容。 3. 客户端读取文件内容,并将其发送到服务器。 4. 服务器接收到文件内容后,将其写入文件中。 代码实现: 客户端: ```python import socket def send_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, port)) filesize = os.path.getsize(filename) s.sendall(f"{filename}:{filesize}".encode()) with open(filename, "rb") as f: while True: data = f.read(1024) if not data: break s.sendall(data) print("File sent successfully.") if __name__ == "__main__": send_file("test.txt", "localhost", 8000) ``` 服务器: ```python import socket def receive_file(host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((host, port)) s.listen() conn, addr = s.accept() with conn: print(f"Connected by {addr}") data = conn.recv(1024).decode() filename, filesize = data.split(":") filesize = int(filesize) with open(filename, "wb") as f: while True: data = conn.recv(1024) if not data: break f.write(data) print("File received successfully.") if __name__ == "__main__": receive_file("localhost", 8000) ``` 文件下载: 1. 客户端连接服务器,并发送要下载文件名。 2. 服务器接收到文件名后,打开文件并将其内容发送给客户端。 3. 客户端接收到文件内容后,将其写入文件中。 代码实现: 客户端: ```python import socket def receive_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((host, port)) s.sendall(filename.encode()) with open(filename, "wb") as f: while True: data = s.recv(1024) if not data: break f.write(data) print("File received successfully.") if __name__ == "__main__": receive_file("test.txt", "localhost", 8000) ``` 服务器: ```python import socket def send_file(filename, host, port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind((host, port)) s.listen() conn, addr = s.accept() with conn: print(f"Connected by {addr}") with open(filename, "rb") as f: while True: data = f.read(1024) if not data: break conn.sendall(data) print("File sent successfully.") if __name__ == "__main__": send_file("test.txt", "localhost", 8000) ``` 以上代码只是一个简单的实现,实际应用中还需要考虑文件传输过程中的错误处理、断点续等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Holy meat

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值