一、函数说明
sendfie函数在两个文件描述符之间直接传递数据,其中的操作完全在内核中执行,从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,大大提高了效率,被称为零拷贝。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:指定从读入文件流哪个位置开始,NULL则从起始位置
- count:指定传输的字节数
sendfile成功返回传输的字节数,失败返回-1并设置errno。
注意 —— in_fd文件描述符必须指向一个真正存在的文件,不能是socket或是管道;out_fd则必须指向socket。
二、使用sendfile
//使用C实现简单的HTTP服务
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/sendfile.h>
char * SendHead(int c, int size, char *pathname) /* 发送头部信息 */
{
char buff[1024] = {0};
strcpy(buff, "HTTP/1.1 200 OK\n\r");
strcat(buff, "Server: myhttpd/1.0\n\r");
strcat(buff,"Content-Length: ");
sprintf(buff+strlen(buff),"%d",size);//将size的%d值写入到buff+strlen(buff)处
strcat(buff, "\n\r");
strcat(buff, "Content-Type: text/html;charset=utf-8\n\r");
// strcat(buff, "Set-Cookie: \n\r");
// strcat(buff, "Via: \n\r");
strcat(buff,"\n\r\n\r");
send(c, buff, strlen(buff), 0);
}
void SendError(int c) /* 错误信息 */
{
char buff[1024] = {0};
strcpy(buff, "HTTP/1.1 404 NOTFOUND\n\r");
send(c, buff, strlen(buff), 0);
}
void AnayRequst(char *recvbuf, char *pathname) /* 分析客户端请求 */
{
//GET /index.heml HTTP/1.1
char *p = strtok(recvbuf, " ");
p = strtok(NULL, " ");
strcpy(pathname, "/var/www/html");
strcat(pathname, p);
}
void SendData(char *pathname, int c) /* 回馈客户端 */
{
struct stat st;
stat(pathname, &st);
SendHead(c, st.st_size, pathname);
int fd = open(pathname, O_RDONLY);
assert(fd != -1);
sendfile(c, fd, NULL, st.st_size);
close(c);
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in ser,cli;
memset(&ser, 0, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(80);
ser.sin_addr.s_addr = inet_addr("192.168.31.36");
int res = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
assert(res != -1);
listen(sockfd, 5);
while(1)
{
int len = sizeof(cli);
int c= accept(sockfd, (struct sockaddr *)&cli, &len);
if(c < 0)
{
continue;
}
while(1)
{
char recvbuf[1024] = {0};
int n = recv(c, recvbuf, 1023, 0);
if(n <= 0)
{
SendError(c);
close(c);
break;
}
printf("%s\n",recvbuf);
char pathname[128] = {0};
AnayRequst(recvbuf, pathname);
SendData(pathname, c);
close(c);
}
}
}
结果: