网络编程:用tcp实现文件传输

用tcp实现文件传输

  1. 文件发送端(tcp客户端)
    1.网络链接
    2.发送文件信息(文件名称, 文件大小)
    3.发送文件内容(分段发送)

  2. 文件接收端 (tcp服务器)
    1.网络初始化监听
    2.接收文件信息(创建一个空文件准备接收数据)
    3.接收文件内容(分段接收,计算接收得长度==文件大小)

完整代码:
发文件

/*
1. 文件发送端(tcp客户端)
1.网络链接
2.发送文件信息(文件名称, 文件大小)
3.发送文件内容(分段发送)
*/

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

//创建文件信息的结构体
struct fileinfo{
    char name[128];
    unsigned int size;
};

int main(int argc, char **argv)
{
    //判断传过来的参数是不是这4个
    if(argc < 4)
    {
        printf("use:./sendfile ip port filepath\n");   // sendfile->发送文件程序 ip->ip地址 port->端口号 filepath->文件路径
        return -1;
    }
    //创建套接字
    int socketfd = socket(AF_INET,SOCK_STREAM, 0);//AF_INET ->IPV4  / AF_INET6->IPV6  /SOCK_STREAM(TCP协议)
    if(socketfd < 0)
    {
        perror("socket fail");
        return -1;
    }    

    //连接服务器
    struct sockaddr_in addr;
    //socklen_t len = sizeof(addr);
    memset(&addr, 0 ,sizeof(addr));//清空
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));//把字符串转成整性数
    addr.sin_addr.s_addr = inet_addr(argv[1]);   
    int ret  = connect(socketfd, (struct sockaddr *)&addr,sizeof(addr));//三次握手已经在这个connect函数内部完成了

    // 获取文件信息
    char *ptr = "/home/book/my.avi";
    struct stat sbuf;
    ret = stat(ptr,&sbuf);
    unsigned int size = sbuf.st_size;

    //创建存储文件信息的结构体
    struct fileinfo info;
    info.size = size;
    
    //提取文件名称->strrchr用来查询最后的/的路径
    char *p = NULL;
    if(p = strrchr(ptr, '/'))
    {
        strcpy(info.name, p+1);//如果p="/",就取/后面的名称
    }
    else
    {
        strcpy(info.name, p);//如果p!="/",就取/
    }

    // while(1)
    // {
        write(socketfd, &info, sizeof(info));
    // }

    close(socketfd);
    return 0;
}

收文件

/*
2. 文件接收端 (tcp服务器)
1.网络初始化监听
2.接收文件信息(创建一个空文件准备接收数据)
3.接收文件内容(分段接收,计算接收得长度==文件大小)
*/

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

//创建文件信息的结构体
struct fileinfo{
    char name[128];
    unsigned int size;
};

int main(int argc, char **argv)
{
    int socketfd = socket(AF_INET,SOCK_STREAM, 0);//AF_INET ->IPV4  / AF_INET6->IPV6  /SOCK_STREAM(TCP协议)
    if(socketfd < 0)
    {
        perror("socket fail");
        return 0;
    }

    //初始化
    //创建套接字
    struct sockaddr_in addr;
    //struct sockaddr_in *p = (struct sockaddr_in*)&addr;这个和上面的结构体是一样的,只是细化了
    memset(&addr, 0 ,sizeof(addr)) ;//清空
    addr.sin_family = AF_INET;//地址族
    addr.sin_port = htons(8989);//转化端口号
    addr.sin_addr.s_addr = INADDR_ANY;//结构体里的结构sin_addr里的s_addr;INADDR_ANY这个宏就是0
    
    //绑定->端口号只能绑定一次,被用过不能再用
    int ret = bind(socketfd, (struct sockaddr *)&addr, sizeof(addr));   //bind()是绑定函数
    if(ret < 0)
    {
        perror("绑定失败");
        return -1;
    }

    //监听
    ret = listen(socketfd, 5);//长度为5
    if(ret < 0)
    {
        perror("监听失败");
        return -1;
    }

    //接受连接,这个函数是阻塞的
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    
    int clientfd = accept(socketfd, (struct sockaddr*)&clientaddr, &len);
    if(clientfd < 0)
    {
        perror("连接失败");
        return -1;
    }

    //接受读取数据->读到的是http通信的数据
    struct fileinfo info;
    memset(&info, 0, sizeof(info));
    char recvbuffer[1024];
    while(1)
    {
        //接收文件头
        ssize_t rd = read(clientfd, &info, sizeof(info));
        if(rd <= 0)
        {
            printf("客户端掉线\n");
            break;
        }
        printf("读到的数据:%s----%u\n", info.name, info.size);//数据时浏览器发过来的
    }

    //关闭
    close(clientfd);
    close(socketfd);
    return 0;
}

执行结果
在这里插入图片描述


版权声明:本文为qq_42343682原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。