关于HTTP服务器的实现,其实如果明白了HTTP协议,这个很容易实现,就是在上节课epoll+reactor
的基础上,接收浏览器发送来的HTTP请求,之后组装一些数据发送出去即可。之前有记录过本地简易HTTP服务器
值得注意的是king老师课上讲的这个获取行内容很有用:
int readline(char *allbuf, int idx, char *linebuf) { int len = strlen(allbuf); for (; idx < len; idx++) { if (allbuf[idx] == '\r' && allbuf[idx + 1] == '\n') { return idx + 2; } else { *(linebuf++) = allbuf[idx]; } } return -1; }
还有响应的填充:
printf("resources: %s\n", conn->resource); int filefd = open(conn->resource, O_RDONLY); if (filefd == -1) { printf("errno: %d\n", errno); return -1; } //fseek(, SEEK_END) struct stat stat_buf; fstat(filefd, &stat_buf); int len = sprintf(conn->wbuffer, "HTTP/1.1 200 OK\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %ld\r\n" "Content-Type: text/html\r\n" "Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size); len += read(filefd, conn->wbuffer+len, BUFFER_LENGTH-len); conn->wc = len; close(filefd);
使用sendfile
零拷贝技术:
int filefd = open(conn->resource, O_RDONLY); if (filefd == -1) { printf("errno: %d\n", errno); return -1; } struct stat stat_buf; fstat(filefd, &stat_buf); int ret = sendfile(fd, filefd, NULL, stat_buf.st_size); // sendbody if (ret == -1) { printf("errno: %d\n", errno); } close(filefd);
在访问图片资源时,需要修改响应协议:
"Content-Type: image/png\r\n"
最后讲到了HTTP协议头用key-value格式存储,便于解析数据。
从最初的reactor到现在的HTTP协议的解析与封装,其实都是一个思想,每一个链接所对应的资源或上下文都单独管理,比如一个链接对应一个结构体,这里面包含它的读写缓存,sockfd,如果考虑HTTP协议的话还可以加上对应的协议头的key-value形式的存储,之后,凡是涉及这个sockfd相关的资源,都可以在这个结构里面找到,这样一来就将业务和网络底层隔离开来,我们只需要关注读写buffer里面的内容。更进一步,当有多个链接时,如何更合理的管理这些链接(结构),链表的形式?还是什么样的数据结构,而目的就是为了能更快更方便的索引,找到对应的资源!其实我感觉这些才是这几节课要表达的核心!(这里说资源可能不太合适,但我想你应该懂我意思,就是与该sockfd相关的东西)
关于更多http服务器相关的实现,可以看我的之前实现的一个webserver项目:https://gitee.com/gao-yuelong/web-server
文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:https://ke.qq.com/course/417774?flowToken=1020253