linux-高性能服务器编程-06
测试使用readv和writev的使用
书上有服务器端的实现,自己尝试写了简单客服端的实现。
直接贴上代码
服务器端
书上的直接贴过来的
#include "head.h"
#define BUFFER_SIZE 1024
static const char* status_line[2] = {"200 OK", "500 Internal server error"};
void rwv_test_ser(const char* ip, const int &port, const char *file_name){
int ret;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address.sin_addr);
int sockfd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC , 0);
if (sockfd < 0){
perror("sockfd");
exit(1);
}
if (bind(sockfd, (struct sockaddr*)& address, sizeof(address)) < 0){
perror("bind");
exit(1);
}
if (listen(sockfd, 16) < 0){
perror("listen");
exit(1);
}
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int acfd = accept(sockfd, (struct sockaddr*)& client_addr, &client_addr_len);
if (acfd < 0){
perror("acfd");
}else {
printf("addr:%s, port:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 用于保存HTTP应答的在这里插入代码片状态行、 头部字段和一个空行的缓存区
char header_buf[BUFFER_SIZE];
memset(header_buf, '\0', BUFFER_SIZE);
// 用于存放目标文件内容的应用程序缓存
char *file_buf;
// 用于获取目标文件的属性, 比如是否为目录, 文件大小等
struct stat file_stat;
// 记录目标文件是否是有效文件
bool valid = true;
// 缓存区header_buf 目前已经使用了多少字节的空间
int len = 0;
if (stat(file_name, &file_stat) < 0){ // 目标文件不存在
perror("stat");
valid = false;
}else {
if (S_ISDIR(file_stat.st_mode)){ // 目标文件是一个目录
perror("file is a dir");
valid = false;
}else if (file_stat.st_mode & S_IROTH){ // 当前用户有读取目标文件的权限
/* 动态分配缓冲区 file_buf, 并指定其大小为目标文件的大小
* file_stat.st_size 加1, 然后讲目标文件读入缓存区file_buf中*/
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) < 0){
perror("read");
valid = false;
}
}
}
// 如果目标文件有效,则发送正常的HTTP应答
if (valid){
/*
* 下面这部分内容将HTTP应答的状态行、“Content-Length” 头部字段 和一个空
* 行一次加入head_buf中
*/
ret = snprintf(header_buf, BUFFER_SIZE - 1, "%s %s\r\n",
"HTTP/1.1", status_line[0]);
len += ret;
ret = snprintf(header_buf + len, BUFFER_SIZE - 1 - len, "Content-Length: %d\r\n", file_stat.st_size);
len += ret;
ret = snprintf(header_buf + len, BUFFER_SIZE - 1 - len, "%s", "\r\n");
/*
* 利用writev 将header_buf 和 file_buf的内容一并写出
*/
printf("head_buf: %s\n",header_buf);
printf("file_buf:%s\n",file_buf);
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(acfd, iv, 2);
if (ret < 0){
perror("writev:\n");
}
}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(acfd, header_buf, strlen(header_buf), 0);
}
close(acfd);
delete [] file_buf; 自己随便写的,目的就是一个文件的传输.
}
close(sockfd) ;
}
客户端
自己随便写的,目的就是一个文件的传输.
void readv_client_test(const char *ip. const int port){
struct sockaddr_in cli_addr;
bzero(&cli_addr, sizeof(cli_addr));
char *pathname = "client_test";
int fd = open(pathname, O_RDWR | O_TRUNC | O_CREAT,0777);
if (fd < 0){
perror("open file:\n");
exit(1);
}
int clisockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (clisockfd < 0){
perror("clisockfd");
exit(1);
}
cli_addr.sin_family = PF_INET;
cli_addr.sin_port = htons(port);
inet_pton(PF_INET, ip, &cli_addr.sin_addr);
if (connect(clisockfd, (struct sockaddr*)&cli_addr, sizeof (cli_addr)) < 0){
perror("connect");
exit(1);
}
int pipefd[2];
int ret = pipe(pipefd);
/*
* 使用splice函数实现文件内容的传输,splice函数至少有一个描述符是管道描述符
*/
ret = splice(clisockfd, NULL, pipefd[1], NULL, 32768,
SPLICE_F_MOVE);
if (ret < 0){
perror("splice read:\n");
exit(1);
}
ret = splice(pipefd[0], NULL, fd, NULL, 32768, SPLICE_F_MOVE);
if (ret < 0){
perror("splice write:\n");
exit(1);
}
close(fd);
close(clisockfd);
}
服务器端代码转载于:《linux高性能服务器编程》–游双