linux sendfile 性能,LinuxC OSX sendfile()

今天,来小结一下纠结我几个小时的linux C。需求是这样的,用c实现tcp的文件上传与下载

一开始,很容易想到的上传思路是,直接在内存中开一块buffer,得到一个file description后,进行一读一发。char buffer[1024];

int fd = open(path, O_RDONLY);

int n = read(fd, buffer, 1024);

if(n<0) {

perror("read");

}

buffer[n] = 0;

write(socket, buffer, n);

//...

然而,其实在linux内核中已经实现了一种更为高效的方法,sendfile不需要频繁的调用read/write,也不需要开辟buffer,减少了内核函数的调用,提高性能。

函数说明定义int sendfile(int fd, int s, off_t offset, off_t *len, struct sf_hdtr *hdtr, int flags);

解释argument nameexplantionfd需要发送的文件的fd(file description)

ssocket的fd

offset文件从那开始发,NULL表示为0

len输出参数,输出一共发送了多少byte,包括后面的hdtr

hdtr额外发送的头和尾

flags设置为0即可

关于flags, man page原文如下:The flags parameter is reserved for future expansion and must be set to 0. Any other value will cause sendfile() to return EINVAL.

意思是,flags是为了后面备用的,现在还没实现,现在传入0即可。

下面着重解释len与hdtr参数 结构体sf_hdtr, 成员如下struct sf_hdtr {

struct iovec *headers; /* pointer to header iovecs */

int hdr_cnt; /* number of header iovecs */

struct iovec *trailers; /* pointer to trailer iovecs */

int trl_cnt; /* number of trailer iovecs */

};

而,结构体iovecstruct iovec {

void * iov_base;/* [XSI] Base address of I/O memory region */

size_t iov_len;/* [XSI] Size of region iov_base points to */

};

可以看到,iovec数据就是表示一段iov_len长度的数据区,而sf_hdtr则是2个iov_len数组(指针)。

headers就是发送文件数据前发送的数据段,trailers则是跟在文件数据EOF之后的。

解释完该方法后,其实上传文件,只需要调用该方法即可,而headers和trailers可以用来界定文件数据,ngnix osx版本中,便有使用该方法。

为了简化文件数据划分的逻辑,我未采用,http协议中类似Content-Length字段来表示文件的大小,从而拼接出完整的文件内容,而是简单的在文件数据头尾加上了自定义的字符串。

代码发送文件bool _sendFile(int out_fd, const char* file) {

int fd = open(file, O_RDONLY);

char* tmp = strrchr(file, '/');

const char* filename = tmp!=NULL? tmp+1: file;

if(fd==-1) {

char b[1024];

sprintf(b, "open failed %s", file);

perror(b);

return false;

}

struct stat state;

fstat(fd, &state);

printf("sending File %s ...n", file);

off_t offset = 0;

off_t len = 0; // 必须初始化0, 不然下次重入时,会被旧值覆盖

char head[1024], sizehd[1024];

sprintf(head, "---file: %srn", filename); // 拼装头部字符串

// sprintf(sizehd, "---size: %lldrnrn", state.st_size);

struct sf_hdtr hdtr = NULL;

iovec headers = NULL, trailers = NULL;

headers.iov_base = head;

headers.iov_len = strlen(head);

// trailers.iov_base = (void *)"file---rn"; //todo: don't recv sometimes ??

// trailers.iov_len = 9;

hdtr.headers = &headers;

hdtr.hdr_cnt = 1;

hdtr.trailers = NULL;

hdtr.trl_cnt = 0;

if(0 == sendfile(fd, out_fd, offset, &len, &hdtr, 0)) {

close(fd);

write(out_fd, "file---rn", 9); // 未使用trailers,因为有时候上传大文件,trailers会丢失。

printf("sendFile %s success, return len: %lld.n", file, len);

return true;

} else {

close(fd);

write(out_fd, "file---rn", 9);

perror("sendfile");

return false;

}

}接收文件bool _receFile(FILE* &pfsile, char* buffer, ssize_t n, bool& receiveing, char* rfilename, int size) {

bool run = false;

char* last = NULL;

if(!receiveing && isfileHead(buffer)) {

memset(rfilename, 0, 50);

strcpy(rfilename, "data/");

if (stat(rfilename, NULL) == -1) {

mkdir(rfilename, 0700);

}

char name[40];

sscanf(buffer, "---file: %srn", name);

int othlen = 11+strlen(name);

int addonlen = n-othlen;

strcat(rfilename, name);

pfile = fopen(rfilename, "wb+"); //!! 以二进制打开文件

receiveing = true;

printf("Downloading %s ...nhead addon: %snn",

rfilename, buffer+othlen);

if(addonlen > 0) {

fwrite(buffer+othlen, addonlen, 1, pfile);

}

run= true;

}

if(receiveing && (last = fileTail(buffer, n))!=NULL) {

receiveing = false;

fwrite(buffer, last-buffer, 1, pfile);

fclose(pfile);

printf("Downloaded %s. and savedn", rfilename);

run= true;

} else if(receiveing && !run) {

printf("download chunk, size: %ldn", n);

if(n

receiveing = false;

fclose(pfile);

}

fwrite(buffer, n, 1, pfile);

run= true;

}

return run;

}

最后

其实还是会有bug的,比如---file: a.pngrn ... ---filern的数据,接收方buffer设置较小,不能容纳完整的---file标志,可能就不会被认为是file;或者结尾截断了。而对于上诉情况,应用层只能通过更复杂的代码逻辑来控制了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值