利用网络传输文件是常用的功能,本文使用tcp 方式实现文件的传输。
其文件树结构为:
.
├── base
│ ├── data_info.h
│ └── file_operation.h
├── client
│ ├── 1.jpg
│ ├── client_tcp.h
│ └── file_client.c
└── server
├── file_server.c
└── sever_tcp.h
1、base目录下为文件传输的数据结构以及文件操作接口,其源码如下:
data_info.h 包含传输的数据结构:
#ifndef __FILE_DATA_INFO_HEADER__
#define __FILE_DATA_INFO_HEADER__
#include<string.h>
#define BUFFER_SIZE 1024
enum info_type
{
file_info,
file_data,
consult_result,
};
struct data_base
{
short i_type;
data_base()
{
i_type = info_type::file_info;
}
};
struct st_file_info : public data_base
{
char name[32];
int size;
st_file_info()
{
memset((void*)&name, 0, sizeof(name));
size = 0;
i_type = info_type::file_info;
}
};
struct st_file_data : public data_base
{
int size;
char data[BUFFER_SIZE];
st_file_data()
{
size = 0;
memset(&data, 0, sizeof(data));
i_type = info_type::file_data;
}
};
struct st_consult_result : public data_base
{
int result;
st_consult_result()
{
result = -1;
i_type = info_type::consult_result;
}
};
#endif //__FILE_DATA_INFO_HEADER__
file_operation.h 为对文件的读写操作:
#ifndef __FILE_OPERATION_HEADER__
#define __FILE_OPERATION_HEADER__
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include"data_info.h"
int write_to_file(int fd,const char* data,int size)
{
write(fd,data,size);
}
int read_from_file(int fd,const int offset,char* readbuf,int size)
{
if(nullptr == readbuf || fd <= -1)
{
return -1;
}
lseek(fd,offset,SEEK_SET);
int ret = read(fd,readbuf,size);
return ret;
}
int open_file(const char* file_path,const char* file_name,int flag)
{
char file[64];
memset((void*)&file,0,sizeof(file));
sprintf(file,"%s/%s",file_path,file_name);
int fd = open(file,flag,0666);
if( -1 == fd)
{
return -1;
}
return fd;
}
int close_file(const int fd)
{
close(fd);
}
int get_file_info(const char* file_path,const char* file_name,data_base* file_data)
{
if( nullptr == file_data ||
nullptr == file_path ||
file_data->i_type != info_type::file_info )
{
return -1;
}
struct stat file_info;
char file[64];
memset((void*)&file,0,sizeof(file));
sprintf(file,"%s/%s",file_path,file_name);
if(-1 == stat(file,&file_info))
{
return -2;
}
st_file_info* info = (st_file_info*) file_data;
info->size = file_info.st_size;
file_data->i_type = info_type::file_info;
memcpy((void*)info->name ,file_name,sizeof(info->name));
return 0;
}
#endif //__FILE_OPERATION_HEADER__
2、client 端 问文件按发送方,分了两个文件,一个是client_tcp.h,一个是file_client.c。其中client_tcp.h为tcp 网络连接操作,file_client.c为文件发送操作。
client_tcp.h
#ifndef __CLIENT_TCP_HEADER__
#define __CLIENT_TCP_HEADER__
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int create_tcp_client_ipv4(const char* ip)
{
if(nullptr == ip)
{
return -2;
}
int socket_fd = -1;
socket_fd = socket(AF_INET,SOCK_STREAM,0);
if(socket_fd<= -1)
{
return -1;
}
struct sockaddr_in serv_addr;
memset((void*)&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_port = htons(8888);
serv_addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &serv_addr.sin_addr);
int ret = connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(0 != ret)
{
return -3;
}
return socket_fd;
}
int connect_cient_close(int socket_fd)
{
close(socket_fd);
}
int connect_client_recv_data(int socket_fd,void* recvbuf,int size)
{
if(nullptr == recvbuf || size < 0)
{
printf("error!!! clinet recv buf is null or empty,can't to recv data!!!");
return -1;
}
int recvlen = recv(socket_fd, recvbuf, size, 0);
return recvlen;
}
int connect_client_send_data(int socket_fd,void* sendbuf, int size)
{
if(size < 0 || nullptr == sendbuf )
{
printf("error!!! client data is empty ,no need to send!!");
return -1;
}
int sendsize = write(socket_fd,sendbuf,size);
return sendsize;
}
#endif //__CLIENT_TCP_HEADER__
file_client.c
#include <stdio.h>
#include "client_tcp.h"
#include "../base/data_info.h"
#include "../base/file_operation.h"
int send_consult_file_infomation(const int socket_fd,const st_file_info* file_info)
{
if(nullptr == file_info || socket_fd <= -1)
{
return -1;
}
int ret = connect_client_send_data(socket_fd,(void*)file_info,sizeof(st_file_info));
if(ret <= -1)
{
return -2;
}
return ret;
}
int send_consult_file_end(const int socket_fd,const st_consult_result* file_result)
{
if(nullptr == file_result || socket_fd <= -1)
{
return -1;
}
int ret = connect_client_send_data(socket_fd,(void*)file_result,sizeof(st_consult_result));
if(ret <= -1)
{
return -2;
}
return ret;
}
int main(int argc,char** argv)
{
int socket_fd;
socket_fd = create_tcp_client_ipv4("127.0.0.1");
if(-1 >= socket_fd)
{
printf("create client tcp to server error! code is %d !!!",socket_fd);
return 0;
}
st_file_info file_info;
int ret = get_file_info(".","1.jpg",&file_info);
if(ret < 0)
{
printf("get file information error! code is %d !!!",ret);
return 0;
}
int fd = open_file(".","1.jpg",O_RDONLY);
if(fd <= -1)
{
printf(" open file error! code is %d !!!",fd);
return 0;
}
send_consult_file_infomation(socket_fd,&file_info);
int recv_ret = -1;
char buffer[BUFFER_SIZE];
memset((void*)buffer,0,sizeof(buffer));
int read_size = 0;
int total_offset = 0;
char send_buffer[BUFFER_SIZE];
memset((void*)send_buffer,0,sizeof(send_buffer));
bool send_flag = true;
while(send_flag)
{
recv_ret = connect_client_recv_data(socket_fd,buffer,sizeof(buffer));
if(recv_ret > 0 )
{
data_base* recv = (data_base*) buffer;
if(recv->i_type == info_type::consult_result)
{
st_consult_result* result = (st_consult_result*)recv;
if(result->result == 1)
{
while(true)
{
memset((void*)send_buffer,0,sizeof(send_buffer));
read_size = read_from_file(fd,total_offset,send_buffer,sizeof(send_buffer));
if(read_size > 0 )
{
st_file_data data;
memset((void*)&data,0,sizeof(data));
memcpy(data.data,send_buffer,sizeof(data.data));
data.size = read_size;
data.i_type = info_type::file_data;
connect_client_send_data(socket_fd,(void*)&data,sizeof(st_file_data));
total_offset+= read_size;
read_size = -1;
}else if (read_size == 0){
st_consult_result result;
memset((void*)&result,0,sizeof(result));
result.result = 1;
result.i_type = info_type::consult_result;
send_consult_file_end(socket_fd,&result);
send_flag = false;
break;
}else{
close_file(fd);
break;
}
}
}
}else{
printf("wait for consult inforamtion");
}
}
}
close_file(fd);
connect_cient_close(socket_fd);
return 0;
}
3、server 端为文件接收方,同样分为2个文件,一个 sever_tcp.h,一个file_server.c。
sever_tcp.h
#ifndef __SERVER_TCP_HEADER__
#define __SERVER_TCP_HEADER__
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<stdio.h>
int create_socket_ipv4()
{
int socketfd = -1;
socketfd = socket(AF_INET,SOCK_STREAM,0);
if (socketfd <= -1)
{
return -1;
}
struct sockaddr_in serv_addr;
memset((void*)&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_port = htons(8888);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_family = AF_INET;
if (-1 == bind(socketfd, (sockaddr*)&serv_addr, sizeof(struct sockaddr_in)))
{
close(socketfd);
return -1;
}
if (-1 == listen(socketfd, 5))
{
close(socketfd);
return -1;
}
return socketfd;
}
int connect_accept(int socket_fd)
{
struct sockaddr_in cli_addr;
int cli_fd = -1;
memset((void*)&cli_addr, 0, sizeof(struct sockaddr_in));
socklen_t len = sizeof(struct sockaddr);
cli_fd = accept(socket_fd, (sockaddr*)&cli_addr, &len);
if (-1 >= cli_fd)
{
return -1;
}
char client_IP[32];
memset((void*)&client_IP, 0, sizeof(client_IP));
printf("client ip:%s port:%d\n",
inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr, client_IP, sizeof(client_IP)),
ntohs(cli_addr.sin_port));
return cli_fd;
}
int connect_close(int socket_fd)
{
close(socket_fd);
return 0;
}
int connect_send_data(int socket_fd,char* sendbuf,int size)
{
if(size<0 || nullptr == sendbuf )
{
printf("error!!! data is empty ,no need to send!!");
return -1;
}
int sendsize = write(socket_fd,sendbuf,size);
return sendsize;
}
int connect_recv_data(int socket_fd,char* recvbuf,int size)
{
if(nullptr== recvbuf || size <= 0)
{
printf("error! recv buf is null or empty,can't to recv data!!!");
return -1;
}
int recvlen = recv(socket_fd,recvbuf,size,0);
return recvlen;
}
#endif //__SERVER_TCP_HEADER__
file_server.c
#include<stdio.h>
#include"../base/data_info.h"
#include"../base/file_operation.h"
#include"sever_tcp.h"
int consult_file_infomation(int cli_fd,int res)
{
struct st_consult_result result;
result.result = res;
connect_send_data(cli_fd,(char*)&result,sizeof(result));
}
int main(int argc,char** argv)
{
int socket_fd = -1;
socket_fd = create_socket_ipv4();
if(socket_fd <= -1 )
{
printf("create server error!, code is %d!!",socket_fd);
return 0;
}
int total_recv = 0;
int file_total_size = 0;
int fd = -1;
do
{
int cli_socket_fd = connect_accept(socket_fd);
if(cli_socket_fd<= -1)
{
continue;
}
while(true)
{
data_base data = {};
int recv_size = connect_recv_data(cli_socket_fd,(char*)&data,sizeof(data));
if(recv_size > 0 )
{
if(data.i_type == info_type::file_info)
{
st_file_info info = {};
connect_recv_data(cli_socket_fd,(char*)&info+sizeof(data_base),sizeof(info)-sizeof(data_base));
file_total_size = info.size;
fd = open_file(".",info.name,O_WRONLY|O_CREAT|O_APPEND|O_TRUNC);
if(fd <=-1)
{
printf("create file error! code is %d !!",fd);
break;
}
consult_file_infomation(cli_socket_fd,1);
}else if(data.i_type == info_type::file_data){
st_file_data data = {};
connect_recv_data(cli_socket_fd,(char*)&data+sizeof(data_base),sizeof(data)-sizeof(data_base));
write_to_file(fd,data.data,data.size);
total_recv+= data.size;
recv_size = -1;
}else if(data.i_type == info_type::consult_result){
st_consult_result result = {};
connect_recv_data(cli_socket_fd,(char*)&result+sizeof(data_base),sizeof(result)-sizeof(data_base));
if(result.result == 1)
{
printf(" file total size is %d,and recv total size is %d\n",file_total_size,total_recv);
}
close_file(fd);
break;
}
}
}
}while(false);
connect_close(socket_fd);
return 0;
}
编译运行结果:
.
├── base
│ ├── data_info.h
│ └── file_operation.h
├── client
│ ├── 1.jpg
│ ├── a.out
│ ├── client_tcp.h
│ └── file_client.c
└── server
├── 1.jpg
├── a.out
├── file_server.c
└── sever_tcp.h
client 端1.jpg 图片完整的传输到了server端。