用什么软件可以做网站整站优化关键词排名
readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数则将多块分散的内存数据一并写人文件描述符中,即集中写。这两个函数定义在 <sys/uio.h>
头文件中。
ssize_t readv(int fd, const struct iovec *iov, int iovcnt)
fd
:文件描述符,指定从哪个文件、套接字或设备读取数据。iov
:指向iovec
结构体数组的指针。iovec
结构体定义如下:struct iovec {void *iov_base; /* 缓冲区起始地址 */size_t iov_len; /* 缓冲区长度 */ };
iovcnt
:iovec
结构体数组中的元素个数,即要使用的缓冲区数量。
- 成功时,返回读取的总字节数。这个值可能小于请求的字节数(即所有
iov
结构体中iov_len
的总和),例如在到达文件末尾或遇到信号中断时。 - 失败时,返回
-1
,并设置errno
以指示错误原因。常见的错误包括EBADF
(无效的文件描述符)、EINTR
(系统调用被信号中断)、EAGAIN
或EWOULDBLOCK
(在非阻塞 I/O 中,没有数据可读)等。
readv示例代码
在这个示例中,程序打开一个文件 input.txt
,使用 readv
函数将文件内容分散读取到 buffer1
和 buffer2
两个缓冲区中。
input.txt内容为:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>#define BUFFER1_SIZE 10
#define BUFFER2_SIZE 20int main() {char buffer1[BUFFER1_SIZE];char buffer2[BUFFER2_SIZE];struct iovec iov[2];iov[0].iov_base = buffer1;iov[0].iov_len = sizeof(buffer1);iov[1].iov_base = buffer2;iov[1].iov_len = sizeof(buffer2);int fd = open("input.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}ssize_t bytes_read = readv(fd, iov, 2);if (bytes_read == -1) {perror("readv");close(fd);return 1;}printf("Read %zd bytes.\n", bytes_read);// 这里需要说明一下input.txt中内容总共30字节// readv会先将前10个字节也就是"0123456789"读到buffer1中// 这里仅仅为了测试演示方便buffer1的最后一个字节被设置为了'\0'// 于是相当于丢失了9,大家理解其逻辑即可,实际中肯定不会为了后续输出方便就丢失最后一个数据呀// 也就是说大家要明白:readv会先将前10个字节也就是"0123456789"读到buffer1中。即可// 最后就是:readv会先将后20个字节也就是"01234567890123456789"读到buffer2中buffer1[bytes_read < sizeof(buffer1)? bytes_read : sizeof(buffer1) - 1] = '\0';buffer2[bytes_read < sizeof(buffer1) + sizeof(buffer2)? bytes_read - sizeof(buffer1) : sizeof(buffer2) - 1] = '\0';printf("Buffer 1: %s\n", buffer1);printf("Buffer 2: %s\n", buffer2);close(fd);return 0;
}
-
这里需要说明一下input.txt中内容总共30字节
-
readv会先将前10个字节也就是"0123456789"读到buffer1中
-
这里仅仅为了测试演示方便buffer1的最后一个字节被设置为了'\0'
-
于是相当于丢失了9,大家理解其逻辑即可,实际中肯定不会为了后续输出方便就丢失最后一个数据呀
-
也就是说大家要明白:readv会先将前10个字节也就是"0123456789"读到buffer1中。即可
-
最后就是:readv会先将后20个字节也就是"01234567890123456789"读到buffer2中
ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
fd
:文件描述符,指定数据要写入的文件、套接字或设备。iov
:同样是指向iovec
结构体数组的指针,用于定义要写入的多个缓冲区。iovcnt
:iovec
结构体数组中的元素个数,即要使用的缓冲区数量。- 成功时,返回写入的总字节数。这可能小于请求写入的字节数(所有
iov
结构体中iov_len
的总和),例如在遇到磁盘空间不足或信号中断时。 - 失败时,返回
-1
,并设置errno
以指示错误原因。常见错误包括EBADF
(无效的文件描述符)、EINTR
(系统调用被信号中断)、ENOSPC
(设备上没有足够的空间)等。
writev示例代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>#define BUFFER1_SIZE 10
#define BUFFER2_SIZE 20int main() {char buffer1[BUFFER1_SIZE] = "Hello, ";char buffer2[BUFFER2_SIZE] = "world!\n";struct iovec iov[2];iov[0].iov_base = buffer1;iov[0].iov_len = sizeof(buffer1);iov[1].iov_base = buffer2;iov[1].iov_len = sizeof(buffer2);int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (fd == -1) {perror("open");return 1;}ssize_t bytes_written = writev(fd, iov, 2);if (bytes_written == -1) {perror("writev");close(fd);return 1;}printf("Written %zd bytes.\n", bytes_written);close(fd);return 0;
}
- buffer1有效数据共7个,其余三个被填充为'\0',这10个字节全部被填充到输出文件中
- buffer2有效数据共7个,其余13个被填充为'\0',这20个字节全部被填充到输出文件中
在web服务器上集中写
当Web服务器解析完一个HTTP请求之后,如果目标文档存在且客户具有读取该文档的权限,那么它就需要发送一个HTTP应答来传输该文档。这个HTTP应答包含1个状态行、多个头部字段、1个空行和文档的内容。其中,前3部分的内容可能被Web服务器放置在一块内存中,而文档的内容则通常被读入到另外一块单独的内存中(通过read函数或mmap函数)。我们并不需要把这两部分内容拼接到一起再发送,而是可以使用writev函数将它们同时写出。
场景是这样的:
- 服务器不考虑客户请求之类的事,接收到客户连接,就给客户发送应答文件
- 应答文件是input.txt,内容是:我是应答文件哦
- 该服务器运行后,另一台服务器用telnet去获取这个文件内容
- 代码中我添加了大量注释去帮助大家理清楚其中的逻辑
server.cpp
/* http应答格式:状态行头部段头部段...(头部段)空行文件*/#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>#define BUFFER_SIZE 1024
// status_line用于存储http应答的第一行:状态行
// 如果状态正常:"200 OK"
// 如果状态异常:"500 Internal server error"
static const char* status_line[2] = { "200 OK", "500 Internal server error" };int main( int argc, char* argv[] )
{if( argc <= 2 ){printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );return 1;}// const char* ip = argv[1];int port = atoi( argv[1] );const char* file_name = argv[2];struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;// inet_pton( AF_INET, ip, &address.sin_addr );address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons( port );int sock = socket( PF_INET, SOCK_STREAM, 0 );assert( sock >= 0 );int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( sock, 5 );assert( ret != -1 );struct sockaddr_in client;socklen_t client_addrlength = sizeof( client );int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );if ( connfd < 0 ){printf( "errno is: %d\n", errno );}else{// header_buf用于存放:状态行+头部段+空行// header_buf在栈区char header_buf[ BUFFER_SIZE ];memset( header_buf, '\0', BUFFER_SIZE );// memset函数将header_buf全部置为0// file_buf是一个指向堆区的指针// 后续会把文件内容复制到这个堆区char* file_buf;struct stat file_stat;// 把文件属性输出到struct stat file_stat(后文放的有讲解struct stat的链接)// valid用于标记,是否具备将文件应答给客户端的各个条件bool valid = true;int len = 0;if( stat( file_name, &file_stat ) < 0 )// stat用于把文件file_name的属性输出给struct stat file_stat{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 ];// 在堆区开辟内存,用于存放应答文件内容,将file_buf指向该区域memset( file_buf, '\0', file_stat.st_size + 1 );if ( read( fd, file_buf, file_stat.st_size ) < 0 )// 将应答文件file_name的内容读到file_buf指向的堆区中{valid = false;}}else{valid = false;}}if( valid ){ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[0] );//将状态行加入header_buflen += ret;ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "Content-Length: %d\r\n", file_stat.st_size );// 将头部段Content-Length加入header_buflen += ret;ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" ); // 将空行加入header_buf// 借助iovec进行集中写。第一个位置存放首部长度,第二个位置存放文件内容struct iovec iv[2];iv[ 0 ].iov_base = header_buf;iv[ 0 ].iov_len = strlen( header_buf );iv[ 1 ].iov_base = file_buf;iv[ 1 ].iov_len = file_stat.st_size;ret = writev( connfd, iv, 2 );// 集中写}// 客户端不具备得到该文件的条件。服务器向其返回错误else{ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[1] );len += ret;ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );send( connfd, header_buf, strlen( header_buf ), 0 );}close( connfd );delete [] file_buf;}close( sock );return 0;
}
服务器开始运行:
客户端开始运行: