Linux下基于TCP的文件传输
相关函数及结构体:
int open(char *path,int flag,/*mode_t mode*/);
open建立了一条到文件的访问路径,如果调用成功则会返回一个能够被read,write和其它系统调用的文件描述符,它不会与任何其它运行中的进程共享,flag(O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写)),open在成功时返回一个新的文件描述符,失败时返回-1,mode用来传递文件的属主权限此处忽略int read(int fd,void *buff,size_t size);按字节读取文件内容 size读取字节大小,缓冲区大小
int write(int fd,void *buff,size_t size);按字节给文件中写入数据 buff文件的起始位置 size:数据的长度int fstat(int fd,struct stat *st);获取已在文件描述符ds上打开文件的有关信息
void sprint(char *s,const char *format,.....);把自己的输出和一个结尾的空字符写入参数字符串s
void sscanf(const char *s,const char *format,.....)把自己读入参数字符串sint recv(int fd,char* buf,int len,int flags);
int send(int fd,const char* buf,int len,int flags);
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。off-t lseek(int fd, off_t offset, int wherece); 用来实现断点续传
wherece:SEEK_SET, 将文件的偏移设置为据文件开始出的offset字节,为正
SEEK_CUE,将文件的偏移设置为其当前加offset,offset可正可负
SEEK_END,将文件的偏移设置为文件长度加offset,offset可正可负基于linux2.6.37 源码的stat 结构体(包含着文件的详细属性信息)
struct kstat {
u64 ino;
dev_t dev;
umode_t mode;
unsigned int nlink;
uid_t uid;
gid_t gid;
dev_t rdev;
loff_t size;
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
unsigned long blksize;
unsigned long long blocks;
};
主体代码如下:
void FileTrans::RecvFile(char *filename, int sockfd, int flag); 发送文件函数
void FileTrans::SendFile(char *filename, int sockfd, int flag); 接受文件函数
void mysend(int fd, int hsize, int totalsize); 发送文件数据函数的封装
void my_recv(int fd,int hsize,int totalsize); 接受文件数据函数的封装void mysend(int fd, int hsize, int totalsize) //发送数据
{
int size = hsize;
while(1)
{
char data[128] = {0}; //定义一个buff大小为128字节并初始化
int n = read(fd, data, 127); //read fd打开的文件的内容
if(n == 0) //如果读取字节大小为零
{
if(flag) //标记为true 表示文件内容以全部读完 则发送完成
{
printf("已完成传送率: 100.00%%\n");
}
break;
}
size += n; //已经发送的文件大小
sleep(1);
if(flag) //用来获取传送的进度,
{
printf("已完成传送率: ");
printf("%5.2f %%", size * 100.0 / totalsize);
fflush(stdout);
printf("\033[100D");
printf("\033[K");
}send(sockfd, data, n, 0); //发送文件
}
}void my_recv(int fd,int hsize,int totalsize) //接受数据
{
int size = hsize;
while(1)
{
char data[128] = {0};
int n = recv(sockfd, data, 127, 0);
if(n <= 0)
{
break;
}
write(fd, data, n);
size +=n;
sleep(1);if(flag)
{
printf("已完成传送率: ");
printf("%5.2f %%", size * 100.0 / totalsize); //每隔一秒显示传输的百分比
fflush(stdout);
printf("\033[100D");
printf("\033[K");
}if(size == totalsize)
{
if(flag)
{
printf("已完成传送率: 100.00%%\n");
}
break;
}
}
}void FileTrans::SendFile(char *filename, int sockfd, int flag)
{
int fd = open(filename, O_RDONLY); //以读的方式打开文件,成功返回文件描述符,失败返回-1
if(fd == -1) //判断是否打开失败,打开失败发送错误提示信息
{
send(sockfd, "File Not Found\n", strlen("File Not Found\n"), 0);
return;
}struct stat st; //当定义一个stat类型的st用来获取文件信息
fstat(fd, &st); //fatst 获取fd打开的文件的信息
char buff[128] = {0}; //定义一个buff字符串数组,全部初始化为0
sprintf(buff, "%d", st.st_size); //将fd打开文件的大小放入buff,
send(sockfd, buff, strlen(buff), 0); //发送文件的大小memset(buff, 0, 128); //buff重新初始化为0
recv(sockfd, buff, 127, 0); //接收服务器返回的数据,
if(strncmp(buff, "ERR", 3) == 0) //如果服务器返回失败则关闭fd
{
close(fd);
return;
}memset(buff, 0, 128);
recv(sockfd, buff, 127, 0); //是否存在已发送大小 是否需要断点续传
if(buff != NULL)
{
send(sockfd, "Can select the resume\n", strlen("Can select the resume\n"), 0);
}int hsize = 0;
sscanf(buff, "%d", &st.st_size); //记录已传输的字节大小,断点续传开始的字节位置
if( hsize!= 0) //选择是否断点续传
{
memset(buff, 0, 128);
recv(sockfd, buff, 127, 0);
if(strncmp(buff, "YES", 3) == 0) //断点续传
{
int now = lseek(fd, hsize, SEEK_SET); //设置文件的新的起始位置
mysend(sockfd,hisze,st.st_size);
}
else
{
return;
}
}
else //整体传输
{
mysend(sockfd,0,st.st_size);
}
close(fd);
}void FileTrans::RecvFile(char *filename, int sockfd, int flag)
{
char buff[128] = {0};
int n = recv(sockfd, buff, 128, 0);
if(strcmp(buff, "File Not Found\n") == 0)
{
return;
}int fileSize = 0; //用来记录将要接收的文件的大小
sscanf(buff, "%d", &fileSize);int fd = open(filename, O_CREAT|O_WRONLY, 0664); //创建或写的方式打开文件,0664:权限
if(fd == -1) //创建文件失败 给客户端发送一个ERR,结束
{
send(sockfd, "ERR", 3, 0);
return;
}send(sockfd, "OK", 2, 0); //创建或打开成功,给客户端发送一个OK
int hsize = lseek(fd, 0, SEEK_END); //计算已有文件大小 断点续传的其实位置
memset(buff, 0, 128);
sprintf(buff,"OK#%d", hsize);send(sockfd, buff, strlen(buff), 0); //发送已有文件大小
if(hsize != 0) //选择是否断点续传
{
f = hsize*100.0/fileSize;
printf("当前已下载%.2f%%\n", f);
printf("Select the resume(YES OR NO):\n");
char qbuff[128] = {0};
fgets(qbuff, 127, stdin);
qbuff[strlen(buff)-1] = 0;
send(sockfd, qbuff, 127, 0);if( strncmp(qbuff, "YES", 3) == 0 ) //断点续传
{
int now = lseek(fd, hsize, SEEK_SET);
my_recv(fd,hsize,fileSize)
}
else
{
return;
}else //整体传输文件
{
my_recv(fd,0,fileSize)
}
close(fd);
}