zoukankan      html  css  js  c++  java
  • readv()和write()sendfile()

    readv函数将文件描述符读取到分散的内存存快中,分散读,writev把分散的数据一并写入文件描述符中,集中写。

    ssize_t readv(int fd,const struct iovec* vector,int count);

    ssize_t writev(int fd,const struct iovec* vector,int count);

    fd参数是被操作的目标文件描述符。vector参数的类型是iovec结构数组,该结构体描述一块内存区。count参数是vector数组的长度,就是有所少块内存数据需要从fd中读出或者写到fd。readv和writev成功时返回读出/写入的fd的字节数,失败返回-1,并设置errno,简化版的recvmsg和sendmsg。

    web服务器上的集中写:

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    #define buflen 1024
    
    static const char* status_line[2] = {"200 OK","500 Internal server error"};
    
    int main(int argc,char* argv[])
    {
    	const char* ip = argv[1];
    	int port = atoi(argv[2]);
    	const char* file_name = argv[3];
    	
    	struct sockaddr_in address;
    	address.sin_family = AF_INET;
    	address.sin_port =htons(port) ;
    	inet_pton(AF_INET,ip,&address.sin_addr);
    	
    	int sock = socket(AF_INET,SOCK_STREAM,0);
    	int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    	ret = listen(sock,5);
    	
    	struct sockaddr_in client;
    	socklen_t client_addrlength = sizeof(client);
    	
    	int connfd = accept(sock,(struct sockaddr* )&client,&client_addrlength);
    	
    	char header_buf[buflen];
    	char * file_buf;
    	struct stat file_stat;
    	bool valid = true;
    	int len  =0;
    	
    	if(stat(file_name,&file_stat)<0){valid = false;}
    	else{
    	if(S_ISDIR(file_stat.st_mode)){valid = false;}
    	else if(file_stat.st_mode& S_IROTH){
    	int fd = open(file_name,O_RDONLY);
    	file_buf = new char[file_stat.st_size+1];
    	memset(file_buf,'0',file_stat.st_size+1);
    	if(read(fd,file_buf,file_stat.st_size+1)<0){valid = false;}
    	}
    	else{
    	valid = false;
    	}
    	
    	if(valid){
    	ret = snprintf(header_buf,buflen-1,"%s %s 
    ","HTTP/1.1",status_line[1]);
    	len += ret;
    	ret = snprintf(header_buf+len,buflen-1-len,"Content-Length:%d
    ",file_stat.st_size);
    	len+=ret;
    	ret = snprintf(header_buf+len,buflen-1-len,"%s","
    ");
    	struct iovec iv[2];
    	iv[0].iov_base = header_buf;
    	iv[1].iov_len = strlen(header_buf);
    	iv[2].iov_base = file_buf;
    	iv[3].iov_len = file_stat.st_size;
    	ret = writev(connfd,iv,2);
    	}
    	else{
    	ret = snprintf(header_buf,buflen-1,"%s %s 
    ","http/1.1",status_line[1]);
    	len += ret ;
    	ret = snprintf(header_buf+len, buflen-1-len,"%s","
    ");	
    	send(connfd,header_buf,strlen(header_buf),0);	
    	}
    	close(connfd);
    	delete[] file_buf;
    }
    	close(sock);
    	return 0;
    
    }
    

      sendfile在俩个文件描述符之间传递数据(在内核中操作),避免了内核缓冲区和用户缓冲区之间的拷贝,效率很高,零拷贝。

    #include<sys/sendfile.h>

    ssize_t sendfile(int out_fd,int in_fd,off_t offset,size_t count);

    in_fd参数是待读出的文件描述符,out_fd是待写入的文件描述符。offset参数指定读入文件流的那个位置开始读,为空,默认起始位置开始读。count参数指定文件描述符之间的传输的字节数。sendfile成功时候,返回传输的字节数,失败返回-1并设置errno。另外in_fd必须指向真实的文件,而不能是socket或是管道。而out_fd必须是一个socket。由此可见sendfile是专门为网络传输文件而设计的。

    例子:

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/sendfile.h>
    #include <sys/stat.h>
    
    
    int main(int argc,char* argv[])
    {
    	const char* ip = argv[1];
    	int port  = atoi(argv[2]);
    	const char* file_name = argv[3];
    	
    	int filefd = open(file_name,O_RDONLY);
    	struct stat stat_buf;
    	fstat(filefd,&stat_buf);
    	
    	struct sockaddr_in address;
    	address.sin_family = AF_INET;
    	address.sin_port = htons(port);
    	inet_pton(AF_INET,ip,&address.sin_addr);
    	
    	int sock = socket(PF_INET,SOCK_STREAM,0);
    	
    
    	int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    
    	ret = listen(sock,5);
    
    	struct sockaddr_in client;
    	socklen_t client_addrlength = sizeof(client);
    	
    	int connfd = accept(sock,(struct sockaddr*)&client,&client_addrlength);
    	
    	sendfile(connfd,filefd,NULL,stat_buf.st_size);
    	
    	close(sock);
    	return 0;
    }
    

    6、splice函数。

    用于在俩个文件描述符之间移动数据,也是零拷贝操作。splice函数的定义如下:

    #include<fcntl.h>

    ssize_t splice (int fd_in,loff_t * off_in,int fd_out,loff_t * off_out,size_t len,unsigned int flags);

    fd_in 是待输入数据的文件描述符。如果fd_in是一个管道文件描述符,那么off_in参数必须被奢姿为NULL。如果fd_in不似一个管道文件描述符(socket),那么off_in表示从输入数据流的何处开始读取数据。此时,若off_in被设置为null,则表示从输入数据流的当前偏移位置读入;若off_in不为null,则他将指出具体的偏移位置。fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。冷参数指定移动数据的长度;

      使用splice()时候,fd_in和fd_out必须至少有一个管道文件描述符。splice调用成功时,返回移动字节的数量。

    案例:我们使用splice函数实现一个零拷贝的回射服务器,将客户端发送的数据原样返回给客户端。

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    #include <unistd.h>
    #include <iostream>
    using namespace std;
    
    int main(int argc,char* argv[]){
    	
    	const char* ip =argv[1];
    	int port = atoi(argv[2]);
    	
    	struct sockaddr_in address;
    	address.sin_family = AF_INET;
    	address.sin_port = htons(port);
    	inet_pton(AF_INET,ip,&address.sin_addr);
    	
    	int sock= socket(AF_INET,SOCK_STREAM,0);
    	
    	int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    	
    	ret = listen(sock,5);
    	
    	struct sockaddr_in client ;
    	socklen_t client_addrlength = sizeof(client);
    	
    	int conn = accept(sock,(struct sockaddr*)&client,&client_addrlength);
    	
    	int pipefd[2];
    	ret = pipe(pipefd);
    
    	ret = splice(pipefd[0],NULL,conn,NULL,32768,SPLICE_F_MORE | SPLICE_F_MOVE);
    	close(conn);
    	return 0;
    }
    

    7、tee()函数。

    tee函数用于在俩个管道文件描述符之间复制数据,也是零拷贝操作。不消耗数据,因此源文件描述符上的数据任然可以用于后续的读操作。tee函数的原型如下;

    #include<fcntl.h>

    ssize_t tee(int fd_in , int fd_out , size_t len , unsigned int flags);

    该函数的参数的含义与splice相同(但fd_in , fd_out 都必须是管道文件描述符)。tee函数成功是返回再俩个文件描述符之间复制的数据流量(字节数)。返回0表示没有复制任何数据。tee失败返回-1,并设施errno。

    8、fcntl()函数。

    ioctl)比fcntl()更能够执行更多的控制。

    #include<fcntl.h>

    int fcntl(int fd, int cmd,...);

    fd参数是被操作的文件描述符,cmd执行各种类型的操作。根据操作类型的不同,该函数可能还需要第三个可选参数arg。

    另外,SIGIO和SIGURG这俩个信号与其他的信号不同,他们必须与某个文件描述符相关联方可使用;

  • 相关阅读:
    android activity状态的保存
    java android 序列号serializable和parcelable
    java 中的 自定义viewUtils框架
    CSS隐藏元素的几种妙法
    WCF、WebAPI、WCFREST、WebService之间的区别
    前端大全
    最全前端资源汇集
    Unicode和汉字编码小知识
    关于写保护
    js实现密码加密
  • 原文地址:https://www.cnblogs.com/yjds/p/8994048.html
Copyright © 2011-2022 走看看