socket传输大文件demo,附各函数原型

本文详细介绍了一种通过socket在客户端和服务端之间传输大文件的方法。首先,服务端会发送文件大小给客户端,然后逐块发送文件内容,客户端根据接收到的文件大小判断文件是否传输完成。文章还提供了完整的代码示例,包括socket的创建、绑定、监听、接收和发送数据的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

功能:

实际研发过程中,发现要从服务端发送大的数据文件给客户端,但socket一次发送的数据包大小是有限制的,需要循环发送;循环发送需要考虑到文件何时发送完毕,所以,这里服务端先发送文件大小给客户端,然后再发送文件,客户端根据接收文件大小进行判定。

 

/*
*struct sockaddr_in 结构体
*在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义
*/
struct sockaddr_in
{
    sa_family_t    sin_family;    //地址族
    uint16_t       sin_port;      //16位TCP/UDP端口号
    struct in_addr sin_addr;      //32位IP地址
    char           sin_zero[8];  //不使用 
};

/*
*struct sockaddr结构体
*/
struct sockaddr
{
    sa_family_t    sin_family;    //地址族
    char           sa_data[14];   //14字节,包含套接字中的目标地址和端口信息
}

sockaddr_in该结构体解决了sockaddr的缺陷,把port和addr分开存储中两个变量中。
二者长度一样,都是16字节,即占用的内存大小是一致的,二者可以相互转化。

sockaddr_in用于变量的赋值,sockaddr用于函数参数。

 

/*
*struct in_addr 存放32位IP地址
*/
struct in_addr
{
    In_addr_t s_addr;    //32位IPv4地址  
};
/*
*INADDR_ANY表示为0.0.0.0的地址
*/
/*
*htonl
*将一个32位数从主机字节顺序转换成网络字节顺序
*例:server_address.sin_addr.s_addr = htonl(INADDR_ANY);
*这边是服务端用于修改INADDR_ANY
*/
#include <arpa/inet.h>
uint32_t htonl(uinit32_t hostlong);
/*
*htons
*将整型变量从主机字节顺序转变成网络字节顺序
*hostshort:16位无符号整数
*例:server_address.sin_port = htons(PORT);
*/
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
/*
*linux socket函数
*af:地址族,也就是IP地址类型,常用AF_INET, AF_INET6
*type:数据传输方式/套接字类型,常用SOCK_STREAM(流格式套接字/面向连接的套接字)
*SOCK_DGRAM(数据报套接字/无连接的套接字)
*protocol:传输协议,常用IPPROTO_TCP,IPPTOTO_UDP,分别表示TCP协议、UDP协议,
IPPROTO_IP为0表示接收任何的IP数据包
例:server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
返回值:成功时返回一个非负整型socket描述符,用于绑定、监听等;失败返回-1
*/
#include <sys/socket.h>
int socket(int af, int type, int protocol);
/*
*socket bind函数
*作用:将address指向的sockaddr结构体中的描述的一些属性与socket套接字进行绑定
*socket:socket创建的套接字
*address:地址信息
*address_len:d地址信息长度
*例:bind(server_sockfd, (struct sockaddr*) & server_address, sizeof(server_address));
*返回值:绑定成功,返回0;失败返回-1
*/
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
/*
*socket listen函数
*作用:将主动连接套接口变为被动连接套接口,使得一个进程可以接受其他进程的请求。
*sockfd:表示要设置的服务端套接字文件描述符
*backlog:表示要设置服务端套接字连接队列的大小(已完成队列和未完成队列中的连接)
返回值:成功返回0;失败返回-1
*/
#include <sys/socket.h>
int listen(int sockfd, int backlog);
/*
*socket accept函数
*作用:提取所监听套接字的等待连接队列中的第一个连接请求,创建一个新的套接字,
*并返回指向该套接字的文件描述符
*sockfd:socket()函数创建的套接字
*addr:struct sockaddr结构体指针,一般存客户端地址信息
*addrlen:这边填struct sockaddr_in结构体的长度
例:client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size)
返回值:成功时,返回接收到的套接字的描述符;出错时,返回-1
*/
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
socket connect函数
作用:完成面向连接的协议连接过程
sockfd:套接字
server_addr:struct sockaddr结构体指针,存放要连接的地址信息
addrlen:指定server_addr结构体长度
例:connect(sockfd, (struct sockaddr*) & server_addr, sizeof(server_addr));
返回值:成功返回0,失败返回-1
*/
#include <sys/socket.h>
#include <sys/types.h>
int connect(int sockfd, const struct sockaddr *server_addr, socklen_t addrlen);
/*
*socket send函数
作用:发送消息到套接字中
sockfd:客户端/服务端套接字
buf:存放发送数据的缓冲区
len:实际要发送的数据的字节数
flags:调用执行方式,一般为0
例:send(fd, data_length, strlen(data_length), 0);
返回值:成功则返回发送数据的大小(发送0字节也成功),失败则返回-1
*/
#include <sys/type.h>
#include <sys/socket.h>
int send(int sockfd, const void *buf, int len, int flags);
/*
*socked recv函数
*作用:从TCP连接的另一端接收数据
sockfd:客户端/服务端套接字
buf:存放recv函数接收数据的缓冲区
len:指明接收数据的大小
flags:调用执行方式,一般为0
例:recv(fd, recv_buf, 1024, 0);
返回值:recv实际返回copy的字节数;成功执行时,返回接收到的字节数(send发送0,则recv接收也为0 );
另一端关闭返回0;失败返回-1
*/
#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, int len, int flags);

DEMO:

/*server*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>

#define MIDLEN 1024
#define RET_ERROR 0
#define RET_OK 1
#define PORT 8888
#define LINTEN_QUEUE 5

/****************************************************************
 * * function name 		: send_big_data
 * * functional description	: 服务端读文件发送数据
 * * input parameter		:fd:客户端套接字
 * * output parameter	: None
 * * return value: None
 * * history	:
 * *****************************************************************/
void send_big_data(int fd)
{
    char recv_buf[MIDLEN] = "";
    recv(fd, recv_buf, MIDLEN, 0); 

    /*接收到的客户端消息,为get_data则进行传输*/
    if(strcmp(recv_buf, "get_data") != 0)
    {
        return;
    }
    /*8250为要传输的文件的大小,这里为方便测试,直接写的固定值*/
    /*先发送要传输文件的大小,然后再发送整个文件*/
    char data_length[MIDLEN] = "8250";
    send(fd, data_length, strlen(data_length), 0); 
    printf("data_length:%s\n", data_length);
    sleep(3);

    /*读取要发送的文件*/
    FILE *fp = fopen("/tmp/print_hello", "rb");
    if(fp == NULL)
    {
        return;
    }
    
    char send_buff[MIDLEN] = "";
    int read_size;
    while ( (read_size = fread(send_buff, sizeof(char), MIDLEN, fp)) > 0)
    {
        if (send(fd, send_buff, read_size, 0) < 0)
        {
            break;
        }
        memset(send_buff, '\0', MIDLEN);
    }
    fclose(fp);
    return;
}
/****************************************************************
 * * function name 		: socket_bind_listen
 * * functional description	: socket绑定、监听
 * * input parameter		:None
 * * output parameter	:None
 * * return value: server_sockfd:创建的套接字
 * * history	:
 * *****************************************************************/
int socket_bind_listen()
{
    struct sockaddr_in server_address;
    int server_sockfd;
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(PORT);

    bind(server_sockfd, (struct sockaddr*) & server_address, sizeof(server_address));
    listen(server_sockfd, LINTEN_QUEUE);
    
    return server_sockfd;
}

/****************************************************************
 * * function name 		: socket_select
 * * functional description	: select模型监控套接字数据
 * * input parameter		:套接字
 * * output parameter	:None
 * * return value: None
 * * history	:
 * *****************************************************************/
void socket_select(int server_sockfd)
{
    fd_set readfds, testfds;
    int client_sockfd;
    struct sockaddr_in client_address;
    int client_len;
    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);

    struct timeval tv;
    while(1)
    {
        tv.tv_sec = 5;
        tv.tv_usec = 0;
    
        int fd;
        int nread;
        testfds = readfds;
     
        int result = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, NULL); 
        if (result < 0)
        {
            perror("server select error");
            return;
        }
        else if (result == 0)
        {
            printf("time out\n");
            //continue;
        }

        for (fd = 0; fd < FD_SETSIZE; fd++)
        {
            if (FD_ISSET(fd, &testfds))
            {
                if (fd == server_sockfd)
                {
                    client_len = sizeof(client_address);
                    client_sockfd = accept(server_sockfd, (struct sockaddr*) & client_address, &client_len);

                    FD_SET(client_sockfd, &readfds);
                }
                else
                {
                    ioctl(fd, FIONREAD, &nread);
                    if (nread == 0)
                    {
                        close(fd);
                        FD_CLR(fd, &readfds);
                        printf("close client_sockfd:%d\n", fd);
                    }
                    else
                    {
                        send_big_data(fd); 
                    }
                }
            }
        }
    }

}

int main()
{
    int server_sockfd;

    server_sockfd = socket_bind_listen();
    socket_select(server_sockfd);

    return 0;
}
/*client.c*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MIDLEN 1024
#define RET_ERROR 0
#define RET_OK 1
#define PORT 8888
#define SERVERIP "127.0.0.1"

/****************************************************************
 * * function name 		: client_socket_init
 * * functional description	: 客户端进行socket连接
 * * input parameter		: None
 * * output parameter	:None
 * * return value: 客户端连接的套接字
 * * history	:
 * *****************************************************************/
int client_socket_init()
{
    int sockfd;
    struct sockaddr_in server_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return RET_ERROR;
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr(SERVERIP);

    if (connect(sockfd, (struct sockaddr*) & server_addr, sizeof(server_addr)) == -1)
    {
        printf("connect error\n");
        return RET_ERROR;
    }
    return sockfd;
}
/****************************************************************
 * * function name 		: recv_big_data
 * * functional description	: 客户端接收大文件数据
 * * input parameter		: None
 * * output parameter	:None
 * * return value: None
 * * history	:
 * *****************************************************************/
void recv_big_data()
{
    int sockfd = client_socket_init();
    if(sockfd == RET_ERROR)
    {
        printf("client connect socket error\n");
        return;
    }

    /*先发送get_data给服务端,进行数据接收*/
    char client_send[MIDLEN] = "get_data";
    int res = send(sockfd, client_send, strlen(client_send), 0);
    if (res == -1)
    {
        printf("send error\n");
        return;
    }
    
    /*先接收服务端发送过的文件大小,再去循环接收文件*/
    char buf_head_length[MIDLEN] = "";
    if(recv(sockfd, buf_head_length, MIDLEN,0) <= 0)
    {
        close(sockfd);
        return;
    }
    printf("buf_head_length:%s\n", buf_head_length);    
 
    /*接收文件写入到print_hello_client文件中*/
    FILE *fp = fopen("/tmp/print_hello_client", "wb");
    if(fp == NULL)
    {
        printf("file connot be open\n");
    } 
    else
    {
        char recv_buf[MIDLEN] = "";
        int recv_size = 0;
        int total_length = 0;
        while (1)
        {
            recv_size = recv(sockfd, recv_buf, MIDLEN, 0);
            if (recv_size <= 0)
            {
                printf("recv error\n");
                break;
            }
            total_length += recv_size;
            int write_buf = fwrite(recv_buf, sizeof(char), recv_size, fp);
            if (write_buf < recv_size)
            {
                printf("file write error\n");
            }
            /*接收完整个数据包之后,退出*/
            if (total_length >= (atoi)(buf_head_length)) break;
            memset(recv_buf, '\0', MIDLEN);
        }
        fclose(fp);
    }
    close(sockfd);

    return;
}

int main()
{
    recv_big_data();

    return 0;
}

参考:

https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html

 

有错误或者逻辑不清的地方,欢迎指出!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值