TCP实现文件传输以及下载

目录

1.上传文件思路

2.下载文件思路

3.上传文件代码

4.下载文件代码

5.运行格式


1.上传文件思路

上传文件就相当于客户端发送文件

步骤:

  1. 创建套接字
  2. 连接服务器
  3. 获取文件大小
  4. 循环少量多次发送
  5. 关闭文件和套接字

2.下载文件思路

下载文件就相当于服务器端接收文件

步骤:

  1. 创建套接字
  2. 绑定服务器信息
  3. 监听
  4. 接收数据
  5. 关闭文件描述符

3.上传文件代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#define MSG_LEN 4096

// 相当于客户端上传文件
// a.out 服务器端IP 服务器端口 要上传文件路径
int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        printf("参数有误!\n");
        return -1;
    }
    // 先检查文件存不存在
    if (access(argv[3], F_OK) == -1)
    {
        printf("文件不存在\n");
        return -1;
    }
    // 创建套接字
    int send_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (send_fd == -1)
    {
        perror("socker error...");
        return -1;
    }
    struct sockaddr_in send_inf;
    send_inf.sin_family = AF_INET;
    send_inf.sin_addr.s_addr = inet_addr(argv[1]);
    send_inf.sin_port = htons(atoi(argv[2]));
    // 连接
    if (connect(send_fd, (struct sockaddr *)&send_inf, sizeof(send_inf)) == -1)
    {
        perror("connnect error...");
        return -1;
    }
    else
    {
        // 发送数据
        // 先获取文件大小
        struct stat sb;
        memset(&sb, 0, sizeof(sb));
        stat(argv[3], &sb);
        long file_size = sb.st_size;
        printf("文件大小是:%ld\n", file_size);
        int file_fd = open(argv[3], O_RDONLY);
        if (file_fd == -1)
        {
            perror("open error...");
            return -1;
        }
        char msg[MSG_LEN] = "\0";
        int i;
        // 循环发送数据
        for (i = 0; i < (file_size / MSG_LEN); i++)
        {
            memset(msg, 0, sizeof(char) * MSG_LEN);
            // 先读
            int read_ret = read(file_fd, msg, MSG_LEN);
            if (read_ret == -1)
            {
                perror("read error...");
                printf("%d\n", __LINE__);
                return -1;
            }
            // 再写
            int write_ret = write(send_fd, msg, read_ret);
            if (write_ret == -1)
            {
                perror("write error...");
                return -1;
            }
            usleep(2000);
        }
        if (file_size % MSG_LEN != 0)
        {
            // 最后一次
            memset(msg, 0, sizeof(char) * MSG_LEN);
            int read_ret = read(file_fd, msg, MSG_LEN); // 因为read是安全的,所以可以直接读MSG_LEN,即使数据不够也不会多读
            if (read_ret == -1)
            {
                perror("read error...");
                return -1;
            }
            // 再写
            int write_ret = write(send_fd, msg, read_ret);
            if (write_ret == -1)
            {
                perror("write error...");
                return -1;
            }
        }
        printf("发送成功!\n");
        // 关闭文件描述符
        close(send_fd);
        close(file_fd);
    }
    return 0;
}

4.下载文件代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#define MSG_LEN 4096

// 相当于服务器接收文件
// a.out 服务器端口 存放文件路径
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("参数有误!\n");
        return -1;
    }
    // 创建套接字
    int rec_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (rec_fd == -1)
    {
        perror("socker error...");
        return -1;
    }
    struct sockaddr_in rec_inf;
    rec_inf.sin_family = AF_INET;
    rec_inf.sin_addr.s_addr = htonl(INADDR_ANY);
    rec_inf.sin_port = htons(atoi(argv[1]));
    // 绑定
    if (bind(rec_fd, (struct sockaddr *)&rec_inf, sizeof(rec_inf)) == -1)
    {
        perror("bind error...");
        return -1;
    }
    // 监听
    if (listen(rec_fd, 20) == -1)
    {
        perror("listen error...");
        return -1;
    }
    int send_fd = accept(rec_fd, NULL, NULL);
    if (send_fd == -1)
    {
        perror("accpet error...");
        return -1;
    }
    else
    {
        // 接收数据
        char msg[MSG_LEN] = "\0";
        umask(0000);
        int file_fd = open(argv[2], O_CREAT | O_WRONLY, 0777);
        if (file_fd == -1)
        {
            perror("open error...");
            return -1;
        }
        while (1)
        {
            memset(msg, 0, sizeof(char) * MSG_LEN);
            int read_ret = read(send_fd, msg, MSG_LEN);
            if (read_ret == -1)
            {
                perror("read error...");
                return -1;
            }
            else if (read_ret == 0)
            {
                break;
            }
            else
            {
                if (write(file_fd, msg, strlen(msg)) == -1)
                {
                    perror("write error...");
                    return -1;
                }
            }
        }
        close(file_fd);
        close(rec_fd);
    }
    return 0;
}

5.运行格式

一、发送端(客户端)

a.out 服务器端IP 服务器端口 要上传文件路径

二、接收端(服务器端)

a.out 服务器端口 存放文件路径

(注意:同一台主机端口号不能相同)

相关推荐
番茄啊1 小时前
linux 开启xftp连接
linux·运维·服务器
AI 编程助手GPT2 小时前
curl 返回成功就能收文件?用本地 HTTP 验证错误页与超时
网络·网络协议·http
Julien20043 小时前
管理机密(一)
运维·服务器·开发语言·学习方法
流浪0014 小时前
计算机网络篇16:传输层——TCP 报头属性字段:16 位窗口大小撑起流量控制,六个标志位标识报文类型
网络协议·tcp/ip·计算机网络·urg紧急指针·tcp报头
从琳开始9895 小时前
网络原理入门(二)------OSI七层模型?TCP/IP五层(四层)模型原来是这样的
java·网络·tcp/ip·java-ee·php
Jongkhurun7 小时前
以太网的最小帧长为什么是 64 字节:从 CSMA/CD 冲突窗口推导 64 Byte
网络协议
AOI小白新手上路7 小时前
深度学习算力购置方案分析:本地 RTX 4090 服务器 vs 云端租赁
服务器·人工智能·深度学习
千桐科技8 小时前
qKnow智能体构建平台专业版 v3.1.5 发布:扩容工作流编排调用能力,上线Text2SQL插件,拓宽数据类智能应用构建边界
服务器·数据库·oracle·qknow
雪落漂泊8 小时前
Linux进程(四)概念:命令行参数和环境变量篇
linux·运维·服务器
NextTech_8 小时前
不想学 vim,怎么改服务器上的配置文件?
服务器·编辑器·vim