原型:
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
说明:
第 1 个参数 out_fd,在 2.6 内核里,必须指向一个 socket 。
第 2 个参数 in_fd,是一个要拷贝文件的文件描述服。
第 3 个参数 offset, 是一个偏移量,它在不断的 sendfile 中,这个偏移量会随着偏移增加,直到文件发送完为止,当然在程序中需要用如 while() 这样的语句来控制。
第 4 个参数 count,表示要传送的字节数(在以下示例中,是 1G 文件的大小,即 buf.st_size)。
应用举例:
下面使用 sendfile() 拷贝一个 1G 大小的文件,程序分为客户端和服务端。
服务端代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main (int argc, char **argv)
{
struct sockaddr_un sin1;
int server_sockfd, client_sockfd;
int server_len, client_len;
ssize_t bytes, res=0;
ssize_t rtotal = 0;
FILE *stream;
int in_fd;
struct stat buf;
off_t off = 0;
unlink ("server_socket");
unlink ("src_sendfile_save");
stream = fopen ("src_sendfile_save", "w");
if (!stream) {
perror ("fopen");
exit (EXIT_FAILURE);
}
fclose (stream);
if ((in_fd = open ("src", O_RDONLY)) < 0) {
printf ("Can't open 'src' file");
exit (EXIT_FAILURE);
}
if (fstat (in_fd, &buf) == -1) {
printf ("Can't stat 'src' file\n");
exit (EXIT_FAILURE);
}
printf ("Get file size are %u bytes\n", buf.st_size);
server_sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
if (server_sockfd < 0) {
perror ("socket");
exit (EXIT_FAILURE);
}
sin1.sun_family = AF_UNIX;
strcpy (sin1.sun_path, "server_socket");
server_len = sizeof (sin1);
if (bind (server_sockfd, (struct sockaddr *)&sin1, server_len) < 0) {
perror ("bind");
exit (EXIT_FAILURE);
}
if (listen (server_sockfd, 5) < 0) {
perror ("listen");
exit (EXIT_FAILURE);
}
printf ("The server is waiting for client connect...\n");
client_sockfd = accept (server_sockfd, (struct sockaddr *)&sin1, (socklen_t *)&client_len);
if (client_sockfd == -1 ) {
perror ("accept");
exit (EXIT_FAILURE);
}
while (off < buf.st_size) {
if ((res = sendfile (client_sockfd, in_fd, &off, buf.st_size)) < 0 ) {
printf ("sendfile failed\n");
exit (EXIT_FAILURE);
} else {
rtotal += res;
}
}
printf ("server sendfile total %u bytes\n", rtotal);
close (client_sockfd);
unlink ("server_socket");
return (0);
}
客户端代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
struct sockaddr_un address;
int sockfd;
int len, result;
int i, bytes;
struct stat buf;
off_t off;
ssize_t res, total = 0;
int wfd;
char rwbuf[4096];
wfd = open ("src_sendfile_save", O_WRONLY);
if (wfd < 0) {
perror ("open");
exit (EXIT_FAILURE);
}
/*..socket,AF_UNIX....,SOCK_STREAM....*/
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror ("socket");
exit (EXIT_FAILURE);
}
address.sun_family = AF_UNIX;
strcpy (address.sun_path, "server_socket");
len = sizeof (address);
/*..........*/
result = connect (sockfd, (struct sockaddr *)&address, len);
if (result == -1) {
printf ("ensure the server is up\n");
perror ("connect");
exit (EXIT_FAILURE);
}
while ((res = read (sockfd, rwbuf, 4096)) > 0) {
total += res;
write (wfd, rwbuf, 4096);
}
printf ("total %u bytes received from server snedfile\n", total);
close (sockfd);
close (wfd);
return (0);
}
注:在我自己的测试中,对于拷贝 1G 大小的文件,使用 sendfile() 和 普通的 write() 差不多,没有明显区别。
转自:http://www.groad.net/bbs/read.php?tid-2308.html
分享到:
相关推荐
3. **读取文件并发送**:读取本地文件内容,并通过Socket的`Send`方法分块发送到服务器。 ```csharp byte[] fileBuffer = File.ReadAllBytes("待发送的文件路径"); int sentBytes; while (sentBytes ) { sentBytes...
零拷贝的关键在于使用特定的系统调用,如Linux下的sendfile或readv/writev。这些调用可以直接将文件描述符和socket句柄传入,让内核在读取文件后,直接将PageCache中的内容拷贝到Socket缓冲区,然后由网卡通过DMA...
2. **sendfile()**: 这个函数在发送文件时可以实现零拷贝。它直接将文件描述符和套接字描述符作为参数,由操作系统负责从文件到网络的传输,省去了用户空间的拷贝步骤。 3. **splice()** 和 **vmsplice()**: 这两个...
`sendFile`系统调用进一步优化了数据传输,数据从磁盘直接通过DMA引擎读入内核缓冲区,然后直接写入Socket缓冲区,避免了经过用户空间,减少了1次上下文切换,总共进行3次拷贝。在Linux 2.4及以后的版本,通过改进...
- **工作流程**:sendfile()系统调用更进一步优化了数据传输,如图1所示,它直接从文件的内核缓冲区拷贝到socket的内核缓冲区,然后由DMA将数据送至协议引擎,省去了用户空间的参与。即使在数据传输过程中文件被...
sendfile 是一种零拷贝技术的实现方式,它可以代替 read 和 write 系统调用,通过使用 DMA 技术以及传递文件描述符,实现了零拷贝。mmap 是另一种零拷贝技术的实现方式,它可以代替 read 系统调用,将内核空间的Page...
3. 利用sendfile系统调用直接将文件内容从一个文件描述符传输到另一个文件描述符(通常是从文件到socket),减少了复制操作。 4. 文件租赁(Lease)机制,确保文件在操作过程中不被其他进程修改,从而避免了错误的...
sendfile系统调用在Unix-like操作系统(如Linux)中广泛使用,它允许内核直接将一个文件的数据拷贝到另一个文件描述符,而无需通过用户空间进行数据拷贝。这一特性减少了上下文切换和内存拷贝的开销,对于大文件传输...
在传统的数据传输过程中,sendfile() 技术需要将数据从文件中读出,并将其拷贝到 socket 缓冲区中去,然后再将其传输到网络中去。这种方法需要进行多次数据拷贝操作,占用了大量的系统资源和 CPU 时间。DMA 收集拷贝...
- **sendfile()**: 在Linux上,sendfile函数可以将文件直接从内核空间发送到网络,避免用户态和内核态之间的数据拷贝。 - **splice()和vmsplice()**: 这两个系统调用允许在内核空间内进行数据传输,如从文件到...
1. 缓存管理:使用sendfile系统调用直接将文件内容传递给网络,减少内存拷贝。 2. TCP缓冲区调整:通过内核参数调整TCP接收和发送缓冲区大小,提高吞吐量。 十、其他协议 1. UDP套接字:适用于一次性、不可靠的...
sendfile()将文件描述符和socket描述符作为参数,使得数据从内核缓冲区直接到网络栈,减少了一次拷贝。但在某些场景下,如需要处理数据时,sendfile()可能不够灵活。 3. **splice()和vmsplice()**:splice()和...
它使用操作系统底层的`sendFile()`系统调用来完成数据传输,直接将文件内容从磁盘读取到内核缓冲区,然后将缓冲区内容发送到socket缓冲区,减少了中间的数据复制步骤。虽然`transferTo()`在一定程度上减少了上下文...
在Java中,可以通过`FileChannel`的`transferTo()`方法实现零拷贝,这个方法直接将文件通道中的数据传输到可写字节通道,如Socket通道。在Linux系统中,`transferTo()`方法会映射到`sendfile()`系统调用。 `send...
在sendfile中,数据从磁盘通过DMA拷贝到内核缓冲区,然后直接通过DMA拷贝到协议引擎,减少了CPU参与的数据复制。从Java的角度看,FileChannel.transferTo方法实现了类似的功能。 另外,Linux内核2.4版本之后,对...
这个过程可能涉及到`sendfile()`系统调用,它可以高效地将内核缓冲区中的数据直接写入文件,减少了用户空间和内核空间之间的数据拷贝。 5. **错误处理**:在整个通信过程中,都需要良好的错误处理机制。例如,网络...
因为只需要上传下载文件,所以不想使用curl这样重量级的库,所以,基于以上原因自己编写封装一个类,利用socket完成对文件的上传和下载,还可以使用内核函数sendfile实现文件上传时的“零拷贝”。在本例中,同样也给...
sendfile()是另一种实现零拷贝的方法,它允许直接将文件内容从一个文件描述符(通常是磁盘文件)传输到另一个文件描述符(如socket)。sendfile()避免了数据在用户空间和内核空间之间的拷贝,但仍然利用DMA将数据从...
零拷贝在Netty中的实现是通过内存映射文件(Memory Mapped Files)或sendfile系统调用等方式,让数据直接从磁盘读取到网络发送出去,或者从网络接收后直接写入到磁盘,减少了数据在不同内存区域间的拷贝。...