C语言:实现Linux系统下利用TCP协议传输文件到阿里云服务器

2 篇文章 0 订阅

最近学习了Linux下的TCP网络编程,想实践一下,于是写了利用TCP协议、传输文件到阿里云服务器的客户端程序服务端程序

首先来看看实现的效果。

1. 实现一次传输

虚拟机Ubuntu/桌面/TCP_File 目录下,有如下文件:

将其发送到自己的阿里云服务器的/home/www/website/目录下。

首先在云服务器端运行server

Ubuntu系统运行client,并输入相应的参数(云服务器地址、文件目录/文件名):

按下回车后,各界面上的响应:

  • 客户端:

  • 服务端

查看服务器端,文件已传输完成:

2. 程序流程

  • 客户端

  • 服务端

3. 文件传输源代码

1. 客户端client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>

// 端口号6666
#define PORT 6666
// 定义buffer大小
#define BUF_SIZE 1024
// #define LISTEN_LENGTH 20

int main(int argc, char *argv[])
{
    printf("客户端启动...\n");

    // 简单判断用户输入的命令行参数,若不符合规范则给出提示
    if (argc != 3)
    {
        // argv[0]是输入的第一个参数
        printf("请输入正确的参数!\n");
        printf("格式: %s 服务器主机地址 目录/文件名 \n", argv[0]);
        printf("举例: %s 47.110.144.145 XXX.txt \n", argv[0]);
        // exit(1)表示异常退出
        exit(1);
    }

    int sockfd, fp;
    // int fp;
    int recvBytes, sendBytes, readBytes;
    // 定义buffer
    unsigned char buffer[BUF_SIZE];
    // 定义sockaddr_in结构体
    struct sockaddr_in serv_addr;
    struct hostent *host;

    // 打开要发送的文件
    // argv[2]是输入的第三个参数
    // 打开文件方式为O_RDONLY只读方式
    if ((fp = open(argv[2], O_RDONLY)) == -1)
    {
        printf("打开%s文件失败!\n", argv[2]);
        exit(1);
    }

    printf("打开%s文件成功!\n", argv[2]);

    // 解析hostname
    // argv[1]是输入的第二个参数
    if ((host = gethostbyname(argv[1])) == NULL)
    {
        printf("hostname错误!\n");
        exit(1);
    }

    // 创建socket流式套接字
    // AF_INET允许与远程主机通信
    // SOCK_STREAM代表流式套接字,采用TCP协议
    // 失败返回-1
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("创建socket失败!\n");
        exit(1);
    }
    printf("创建socket成功!\n");

    // 请求连接服务器
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
    // 将sin_zero清零
    bzero(&(serv_addr.sin_zero), 8);
    // 调用connect(),失败返回-1
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
    {
        printf("连接服务器失败!\n");
        exit(1);
    }
    printf("连接服务器成功!\n");

    // ------------------通信阶段------------------

    // 1.发送文件名
    // 将buffer清零
    memset(buffer, 0, sizeof(buffer));
    // argv[2]是输入的第三个参数,文件名
    strcpy(buffer, argv[2]);
    // 调用send()
    send(sockfd, buffer, strlen(buffer) + 1, 0);
    printf("发送文件名完成!\n");

    // 将buffer清零
    memset(buffer, 0, sizeof(buffer));
    // 调用recv()
    recvBytes = recv(sockfd, buffer, sizeof(buffer), 0);

    // 2.发送数据
    // 将buffer清零
    memset(buffer, 0, sizeof(buffer));
    printf("开始发送数据...\n");
    // 读文件,读取的字节数为buffer的长度
    // 成功返回当前读取的字节数
    // 如果返回值大于0,说明可能还没读取完毕,则继续执行一次
    while ((readBytes = read(fp, buffer, BUF_SIZE)) > 0)
    {
        // 调用send(),将这次读取的数据发送
        // readBytes 为这次读取的字节数
        sendBytes = send(sockfd, buffer, readBytes, 0);
        // 如果出错,返回-1,给出提示并退出
        if (sendBytes < 0)
        {
            printf("× 发送文件失败!\n");
            exit(1);
        }
        // 将buffer清零
        memset(buffer, 0, sizeof(buffer));
    }
    printf("√ 发送文件成功!\n");

    // 关闭文件
    close(fp);
    // 关闭套接字
    close(sockfd);
    printf("客户端退出...\n");
}

2. 服务端server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

// 端口号6666
#define PORT 6666
// 定义buffer大小
#define BUF_SIZE 1024
#define Name 100
// 定义listen中请求队列的大小
#define LISTEN_LENGTH 20

void main()
{
    // 定义sockaddr_in结构体
    struct sockaddr_in server_sockaddr, client_sockaddr;
    int size, recvBytes, writeBytes, fp;
    int sockfd, listenfd;
    int i = 0;

    unsigned char filePath[Name], filePath1[Name], filePath2[Name];
    // unsigned char filePath1[Name], filePath2[Name];
    // 定义buffer
    unsigned char buffer[BUF_SIZE];

    printf("服务端启动...\n");

    // 创建socket流式套接字,失败返回-1
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("socket创建失败!\n");
        exit(1);
    }
    printf("socket创建成功!listenfd=%d\n", listenfd);

    // 绑定地址及端口
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(PORT);
    server_sockaddr.sin_addr.s_addr = INADDR_ANY;
    // 将sin_zero清零
    bzero(&(server_sockaddr.sin_zero), 8);
    // 调用bind(),失败返回-1
    if ((bind(listenfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))) == -1)
    {
        printf("绑定失败!\n");
        exit(1);
    }
    printf("绑定成功!\n");

    // 监听客户端请求
    // 调用listen(),失败返回-1
    if (listen(listenfd, LISTEN_LENGTH) == -1)
    {
        printf("监听失败!\n");
        exit(1);
    }
    printf("监听连接请求...\n");

    // 接收连接
    size = sizeof(client_sockaddr);
    // 调用accept(),接收请求
    sockfd = accept(listenfd, (struct sockaddr *)&client_sockaddr, &size);
    if (sockfd == -1)
    {
        printf("连接失败!\n");
        exit(1);
    }
    printf("连接成功!\n");

    // ------------------通信阶段------------------
    // 1.传输文件名
    // 将buffer清零
    bzero(buffer, BUF_SIZE);
    // 调用recv(),接受数据保存在buffer
    recvBytes = recv(sockfd, buffer, BUF_SIZE, 0);
    // 把buffer中的数据复制到filePath
    strcpy(filePath, buffer);

    // 将buffer清零
    memset(buffer, 0, sizeof(buffer));
    // 将反馈信息放入buffer
    strcpy(buffer, "文件名接收成功!\n");
    // 应答
    send(sockfd, buffer, strlen(buffer) + 1, 0);

    // 2.编辑文件名编辑,去除带'/'的路径
    // 判断是否输入了目录
    if (strchr(filePath, '/') != NULL)
    {
        // 输入了目录,作处理,去掉'/'
        strcpy(filePath1, strrchr(filePath, '/'));
        for (i = 1; filePath1[i] != '\0'; i++)
        {
            filePath2[i - 1] = filePath1[i];
            filePath2[i] = '\0';
        }
    }
    else
        // 没有输入目录,直接赋值
        strcpy(filePath2, filePath);

    // 3.服务端创建文件
    // 创建文件,并以读写方式打开
    // 0777表示所有者、组成员和其他用户都有读、写和执行权限
    fp = open(filePath2, O_CREAT | O_RDWR, 0777);
    // 创建失败返回-1
    if (fp == -1)
    {
        printf("%s文件创建失败!\n", filePath2);
        exit(1);
    }
    printf("%s文件创建成功!\n", filePath2);

    // 4.传输文件的数据
    // 将buffer清零
    bzero(buffer, BUF_SIZE);
    printf("开始接收数据...\n");

    // 调用recv(),成功时返回成功接受的字节个数
    // 如果返回值大于0,则可能还没有接收完毕,继续重复执行
    // 接受的数据放入buffer
    while (recvBytes = recv(sockfd, buffer, BUF_SIZE, 0))
    {
        // 接收过程出现错误,给出提示
        if (recvBytes < 0)
        {
            printf("接收文件失败!\n");
            break;
        }
        // 将接受的数据写入文件中,写的个数为接受的字节数
        // 成功时返回实际写入的数据
        writeBytes = write(fp, buffer, recvBytes);

        // 实际写入数据 < 接受的数据,则传输错误
        if (writeBytes < recvBytes)
        {
            printf("传输数据失败!\n");
            break;
        }
        // 将buffer清零
        bzero(buffer, BUF_SIZE);
    }
    printf("√ 接收文件成功!\n");

    // 关闭文件
    close(fp);
    // 关闭连接套接字
    close(sockfd);
    // 关闭监听套接字
    close(listenfd);
    printf("服务端退出...\n");
}

📘📘欢迎在我的博客上访问:
https://lzxjack.top/

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

火星飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值