前言文件传输是聊天室的最后一个功能,这个博客仍然站在聊天室的项目背景上。
既然两个客户端可以通过套接字进行通信,那么传输文件的难点在于如何接收和离线发送文件。
文件发送主要有以下两种模式。
客户端-客户端(实时文件发送)客户端-服务器-客户端(脱机文件发送)首先,确定用于发送文件的函数。 sendfile ) )。
搜索sendfile函数时,大多数博客似乎都介绍了sendfile零拷贝的优点,但找不到我最需要的。 那个怎么用?
SYNOPSIS
#include sys/sendfile.h
size_tsendfile(intout_FD,int in_fd,off_t *offset,size_t count );
说得再多也比不上例行公事的直接。 以下是第一版代码,服务器和客户端之间的连接步骤很繁琐,代码可以放在文末,必要时直接往下拉复制。 这里只显示传输文件部分的代码。
客户端-服务器
客户端(发送方) :
# define bufsize 1024 char * file _ name; char buf[BUFSIZE]; char file_path[BUFSIZE]; printf ('请输入完整的路径名: '; Sanf('%s ',file_path ); //确定路径名是否正确的if(stat(file_path,buffer )=-1 ) printf(----不正确的路径名---n ' ); 继续; //获取文件名file_name=basename(file_path ); //将文件名发送到接收方strcpy(buf,file_name )的write(CFD,buf,BUFSIZE ); //这里采用定长协议发送文件intFP=open(file_path,O_CREAT|O_RDONLY,S_IRUSR|S_IWUSR )。 //文件开始发送printf (---- -文件:%s---n (,buf ); sendfile(CFD,fp,0,buffer.st_size ); printf('---文件%s传输成功---n ',buf; //关闭文件描述符和套接字关闭(FP )关闭(CFD; 服务器端(接收端)
# define bufsize 1024 char file _ name [ bufsize ]; char file_path[BUFSIZE]; char buf[BUFSIZE]; //接收文件名read(CFD,file_name,BUFSIZE ); 如果希望在特定目录下接收//文件,可以添加这样的步骤//默认值保存到当前文件夹//当前文件夹下的file_buf/目录中包含sprintf(file_path,文件路径) 文件//文件file * FP=创建fopen (file _ path,' wb ' )的if(FP==null ) Perror(can'topenfile ); 退出(1; //将数据写入文件while ((n=读取) CFD,buf,BUFSIZE ) ) fwrite ) buf,sizeof(char ),n,fp; //关闭文件描述符和套接字fclose(FP )的close(CFD; 至此,我们第一个发送文件的任务完成了。
但是在接收方read返回值0时停止循环while。注意阻止并等待数据,而不是在套接字中读取数据后返回0。
从发送方套接字关闭到发送FIN数据包为止,read将返回0并退出循环。
这里的内容详见以下。
3359 ZF L9.github.io/c-socket.html
但是,这样显然不足以达到我们的需要。 作为聊天室,如果插座断开,应该如何通信? 因此,read ) )应该设计一个判断点来判断是否传输了文件,而不是阻止并等待FIN包。
在发送端把文件的长度提前发送至接收端
很简单。 将文件的长度和文件名保存到结构中
struct len_name{unsigned int len; char name[NAME_MAX]; (; 但是,write (),read ) )只能发送和接收字符串类型的分组,所以这里有几个类型转换
客户端(发送方)
struct len_name ln; ln.len=buffer.buffer.st_size; strcpy(ln.name,file_name ); //类型转换//按字节复制结构的内存在buf中char buf[BUFSIZE]; memcpy(buf,ln,sizeof ) ln ); 发送//write (CFD、buf、BUFSIZE ); 服务器端(接收端)
struct len_name ln; char buf[BUFSIZE]; 收到//read(CFD、buf、BUFSIZE ); //类型转换memcpy(ln,buf,sizeof ) ln ); 然后,只需稍微修改接收方的接收文件部分,即可解决上述问题。
服务器端(接收端)
char buf[BUFSIZE]; int len; 未指定int sum=0; file * FP=fopen (时间,' wb ' ); while((n=read ) CFD,buf,BUFSIZE ) )0) fwrite ) buf,sizeof(char ),n,fp ); sum =n; 在收到if(sum=ln.len )//足够长的时间点,文件的读取就完成了{break; }完整的代码请参阅此处:
3359 github.com/hiyoyolumi/chat room/tree/master/file _ test
通过将发送方写入服务器,将接收方写入客户端,可以实现服务器------客户端
对于客户端的实时传输文件模式,在掌握上述文件传输技术后也能容易实现,但本文对此不写代码。
基本思路与客户端-服务器基本一致。 服务器获取两个客户端的套接字,一个发送方接收,服务器充当一个中继即可。