34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
讲述:冯永吉
时长09:19大小8.52M
buffer 对象
套接字接收数据处理
套接字发送数据处理
HTTP 协议实现
完整的 HTTP 服务器例子
总结
思考题
赞 6
提建议
精选留言(28)
- chs2019-11-15老师不明白缓冲区为什么要这样设计。用两块内存当做缓冲区,一个用于接收数据,另一个用于发送数据。这两种方式的优缺点能说一下吗?
作者回复: 这里你的理解有点问题,确实是两个buffer对象,一个input_buffer用来接收数据,这个input_buffer对象的写入是框架的handle_read函数来完成的,同时应用程序不端的将input_buffer里的数据取走,这样handle_read就可以不断的将接收缓冲区的数据写入input_buffer。 另一个buffer对象是output_buffer,应用程序不断的往这个缓冲区里写入待发送的数据,框架里的handle_write函数不端的将缓冲区的数据送到套接字发送缓冲区中。 缓冲区的设计中,肯定是有一个往缓冲区里写入的,另一个从缓冲区里读取数据,否则就没有缓冲区了,而是临时创建一个个的字节流对象。 使用缓冲区可以大大减少对内存的消耗。
共 2 条评论8 - keepgoing2020-09-07老师,在tcp_connection.c文件tcp_connection_new方法创建channel时传入的data是tcp_connection类型,但在channel.c中channel_write_event_enable方法会直接从channel->data中取一个event_loop类型指针出来,阅读了整个tcp框架看起来没有找到直接传入event_loop类型的地方,这里是一个代码bug吗
作者回复: 你读的很仔细,我看了一下,确实是有问题的。 最简单的方法是在channel里面保持一个event_loop对象指针,在构建channel时传过来,在channel.c的channel_write_event_enable方法里直接使用这个对象就可以了。 如果可以,欢迎你提一个patch过来,感谢~
2 - 罗兆峰2022-02-18第二题: 用户申请图片的时候可以申请一个GET 方法的request, GET URI version, URI 是图片相对服务器程序的地址,在服务器端程序使用io 函数read/或者mmap 读取图片文件的内容, 并且写到connectedfd 中即可, http response 中的文件类型标记为image/png。
作者回复: 赞
1 - 小家伙542021-07-09老师,ubuntu20.4运行lib程序会出现段错误,这是怎么回事啊? nuc@nuc-NUC8i5BEHS:~/learn/GeekTime/net_prog/yolanda/build/bin$ ./http_server01 [msg] set epoll as dispatcher, main thread [msg] add channel fd == 5, main thread [msg] set epoll as dispatcher, Thread-1 [msg] add channel fd == 9, Thread-1 [msg] event loop thread init and signal, Thread-1 [msg] event loop run, Thread-1 [msg] event loop thread started, Thread-1 [msg] set epoll as dispatcher, Thread-2 [msg] add channel fd == 12, Thread-2 [msg] event loop thread init and signal, Thread-2 [msg] event loop run, Thread-2 [msg] event loop thread started, Thread-2 [msg] add channel fd == 6, main thread [msg] event loop run, main thread [msg] epoll_wait wakeup, main thread [msg] get message channel fd==6 for read, main thread [msg] activate channel fd == 6, revents=2, main thread [msg] new connection established, socket == 13 [msg] connection completed [msg] epoll_wait wakeup, Thread-1 [msg] get message channel fd==9 for read, Thread-1 [msg] activate channel fd == 9, revents=2, Thread-1 [msg] wakeup, Thread-1 [msg] add channel fd == 13, Thread-1 [msg] epoll_wait wakeup, Thread-1 [msg] get message channel fd==13 for read, Thread-1 [msg] activate channel fd == 13, revents=2, Thread-1 [msg] get message from tcp connection connection-13 段错误 (核心已转储)展开
作者回复: 貌似是内存访问出错了,你可以看下dump文件,另外,我不能确定是和ubuntu20.4有关。
共 3 条评论1 - TinyCalf2020-11-17//初始化一个request对象 struct http_request *http_request_new() { struct http_request *httpRequest = malloc(sizeof(struct http_request)); httpRequest->method = NULL; httpRequest->current_state = REQUEST_STATUS; httpRequest->version = NULL; httpRequest->url = NULL; httpRequest->request_headers = malloc(sizeof(struct http_request) * INIT_REQUEST_HEADER_SIZE); httpRequest->request_headers_number = 0; return httpRequest; } 这里的 httpRequest->request_headers = malloc(sizeof(struct http_request) * INIT_REQUEST_HEADER_SIZE); 是不是写错了 ;)展开
作者回复: 没有哦,这个其实是一个request_header的数组,直接用指针来表示了,这个数组的最大长度是INIT_REQUEST_HEADER_SIZE。因为http request header就是一个数组。
1 - 沉淀的梦想2019-10-30在ubuntu系统上一运行老师的程序就会出现“interrupted by signal 11: SIGSEGV”错误
作者回复: 我也是ubuntu系统啊,有同学碰到同样的问题么?
共 6 条评论1 - 传说中的成大大2019-10-25第二个问题 才是把我考到了 我感觉现在我对设计模式的理解并不深,但是我现在感受特深的一点就是单一职责原理 buffer类才套接字的处理 tcpconnect应用层面的处理,而且最近在工作中我也是尝试着画流程图 把每个功能进行细分 分到一个流程分支里面只处理一个逻辑1
- dll2022-08-04好不容易看完了 打卡纪念一下
编辑回复: 真棒!
- 铲铲队2022-04-13make_room 函数就是起这个作用的,如果右边绿色的连续空间不足以容纳新的数据,而最左边灰色部分加上右边绿色部分一起可以容纳下新数据,就会触发这样的移动拷贝,最终红色部分占据了最左边,绿色部分占据了右边,右边绿色的部分成为一个连续的可写入空间,就可以容纳下新的数据 ----》个人觉得好像不用移动拷贝,数据一部分拷贝满writeable_size,剩余部分拷贝到front_spare_size。即循环缓冲,这样效率更高吧展开
作者回复: 这样你不好管理啊,统一部分数据你硬生生把它劈成两份,这两份数据你怎么读呢?
- 肥磊2022-03-07老师,用wenbench测试出现段错误,是什么原因, [msg] get message channel i==0, fd==7, Thread-1 [msg] activate channel fd == 7, revents=2, Thread-1 [msg] wakeup, Thread-1 [msg] add channel fd == 14, Thread-1 [msg] poll added channel fd==14, Thread-1 [msg] get message channel i==2, fd==14, Thread-1 [msg] activate channel fd == 14, revents=2, Thread-1 [msg] get message from tcp connection connection-14 [1] 2424 segmentation fault (core dumped) ./http_server01展开
作者回复: 我怀疑我哪里有内存拷贝没考虑细致,只能慢慢troubleshooting了,欢迎你debug下,提MR帮我改进哈。<抱拳>
共 2 条评论 - 菜鸡互啄2021-12-27哦 我知道了。front_spare_size是被读了一段之后产生的。
作者回复: 好像你悟了 :)
- 菜鸡互啄2021-12-27老师你好 为什么要设计front_spare_size?或者说为什么存在front_spare_size?readIndex和writeIndex一开始不是从0开始的吗?
作者回复: 好像你悟了 :)
- T------T2021-12-08老师好,发现一个memmem函数运行错误的Bug. 环境:Ubuntu18.04 GCC 10.3 glic 2.33 问题:返回void* 的memmem函数未声明,系统默认调用了返回int的memmem函数。返回值由int强转成char*,导致后续处理出现错误。 解决办法:在#include<string.h> 之前添加#define _GNU_SOURCE解决 参考:https://insidelinuxdev.net/article/a09522.html展开
作者回复: 嗯,严格来说,所有和OS环境有关联的函数调用,都需要抽象屏蔽一下。
- 日就月将2021-10-17老师 http服务器request初始化的时候 http_header申请内存为什么还要乘128
作者回复: 这是因为http request headers的key-value对,128这个值足够用了。要知道,request headers本身是一个map,所以需要指定map的大小。
共 2 条评论 - vv_test2021-07-03[msg] set poll as dispatcher, main thread [msg] add channel fd == 4, main thread [msg] poll added channel fd==4, main thread [msg] set poll as dispatcher, Thread-1 [msg] add channel fd == 7, Thread-1 [msg] poll added channel fd==7, Thread-1 [msg] event loop thread init and signal, Thread-1 [msg] event loop run, Thread-1 [msg] event loop thread started, Thread-1 poll failed : Invalid argument (22) [msg] set poll as dispatcher, Thread-2 [msg] add channel fd == 9, Thread-2 [msg] poll added channel fd==9, Thread-2 [msg] event loop thread init and signal, Thread-2 [msg] event loop run, Thread-2 poll failed : Invalid argument (22)展开
作者回复: 这个看上去是poll参数传入出错了,什么系统?
- study的程序员2021-05-15缓冲区可以设置为环形的,避免移动
作者回复: 管理起来难度挺大
- JeQer2021-02-17没有经过压力测试的服务器怎么能称为高性能呢?
作者回复: 欢迎压测
- 卡布猴纸2021-02-01老师你好,断连的tcpconnection和channel资源怎么管理的?
作者回复: 直接回收掉。 int handle_connection_closed(struct tcp_connection *tcpConnection) { struct event_loop *eventLoop = tcpConnection->eventLoop; struct channel *channel = tcpConnection->channel; event_loop_remove_channel_event(eventLoop, channel->fd, channel); if (tcpConnection->connectionClosedCallBack != NULL) { tcpConnection->connectionClosedCallBack(tcpConnection); } }
- GalaxyCreater2020-08-24在buffer_socket_read函数用了char additional_buffer[INIT_BUFFER_SIZE]这个临时变量,那么预创建buffer来减少内存创建的开销就没效了,最少在读数据上的优化已经没效了
作者回复: 如果实际读缓冲区的数据量比较大,后面会通过buffer_append把additional_buffer里面的数据拷贝到buffer中,这样,其实就减少了系统调用的次数,通过空间换取了时间。
- I believe you2020-07-11老师,用你的程序在linux中使用webbench进行压力测试,每次请求都只有一个成功,其他全都失败,能请问下是什么原因吗
作者回复: webbench发送的请求是啥?
共 3 条评论