网络编程实战
盛延敏
前大众点评云平台首席架构师
44207 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
网络编程实战
15
15
1.0x
00:00/00:00
登录|注册

34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现

思考题
总结
完整的HTTP服务器例子
HTTP协议实现
buffer对象
高性能HTTP服务器

该思维导图由 AI 生成,仅供参考

你好,我是盛延敏,这里是网络编程实战第 34 讲,欢迎回来。
这一讲,我们延续第 33 讲的话题,继续解析高性能网络编程框架的字节流处理部分,并为网络编程框架增加 HTTP 相关的功能,在此基础上完成 HTTP 高性能服务器的编写。

buffer 对象

你肯定在各种语言、各种框架里面看到过不同的 buffer 对象,buffer,顾名思义,就是一个缓冲区对象,缓存了从套接字接收来的数据以及需要发往套接字的数据。
如果是从套接字接收来的数据,事件处理回调函数在不断地往 buffer 对象增加数据,同时,应用程序需要不断把 buffer 对象中的数据处理掉,这样,buffer 对象才可以空出新的位置容纳更多的数据。
如果是发往套接字的数据,应用程序不断地往 buffer 对象增加数据,同时,事件处理回调函数不断调用套接字上的发送函数将数据发送出去,减少 buffer 对象中的写入数据。
可见,buffer 对象是同时可以作为输入缓冲(input buffer)和输出缓冲(output buffer)两个方向使用的,只不过,在两种情形下,写入和读出的对象是有区别的。
这张图描述了 buffer 对象的设计。
下面是 buffer 对象的数据结构。
//数据缓冲区
struct buffer {
char *data; //实际缓冲
int readIndex; //缓冲读取位置
int writeIndex; //缓冲写入位置
int total_size; //总大小
};
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文详细介绍了如何自己动手编写高性能HTTP服务器,主要包括TCP字节流处理和HTTP协议实现。首先讲解了buffer对象的设计和数据结构,以及make_room函数的实现,用于处理缓冲区的扩容操作。接着详细讲解了套接字接收数据处理和套接字发送数据处理的实现细节,包括如何将套接字接收的数据流缓冲到buffer对象中,并通过额外的缓冲触发扩容操作,以及如何将编码后的数据通过套接字缓冲区发送出去。文章还介绍了HTTP协议的解析和处理过程,包括解析状态行、请求头部和请求体等细节。最后,通过一个完整的HTTP服务器例子展示了如何根据不同的HTTP请求信息进行计算和处理,并给出了两道思考题,引发读者深入思考。 整篇文章通过实例程序和详细的代码解析,展现了作者对于高性能HTTP服务器的深入理解和实践经验,为读者提供了一个全面的技术指南。读者可以通过本文了解如何利用buffer对象处理TCP字节流,并实现HTTP协议相关的功能,从而完成高性能HTTP服务器的编写。文章内容丰富,技术深度适中,适合对网络编程和HTTP协议感兴趣的技术人员阅读学习。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《网络编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(28)

  • 最新
  • 精选
  • chs
    老师不明白缓冲区为什么要这样设计。用两块内存当做缓冲区,一个用于接收数据,另一个用于发送数据。这两种方式的优缺点能说一下吗?

    作者回复: 这里你的理解有点问题,确实是两个buffer对象,一个input_buffer用来接收数据,这个input_buffer对象的写入是框架的handle_read函数来完成的,同时应用程序不端的将input_buffer里的数据取走,这样handle_read就可以不断的将接收缓冲区的数据写入input_buffer。 另一个buffer对象是output_buffer,应用程序不断的往这个缓冲区里写入待发送的数据,框架里的handle_write函数不端的将缓冲区的数据送到套接字发送缓冲区中。 缓冲区的设计中,肯定是有一个往缓冲区里写入的,另一个从缓冲区里读取数据,否则就没有缓冲区了,而是临时创建一个个的字节流对象。 使用缓冲区可以大大减少对内存的消耗。

    2019-11-15
    2
    8
  • keepgoing
    老师,在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过来,感谢~

    2020-09-07
    3
  • 罗兆峰
    第二题: 用户申请图片的时候可以申请一个GET 方法的request, GET URI version, URI 是图片相对服务器程序的地址,在服务器端程序使用io 函数read/或者mmap 读取图片文件的内容, 并且写到connectedfd 中即可, http response 中的文件类型标记为image/png。

    作者回复: 赞

    2022-02-18
    2
  • T------T
    老师好,发现一个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-12-08
    2
    1
  • 小家伙54
    老师,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有关。

    2021-07-09
    4
    1
  • JeQer
    没有经过压力测试的服务器怎么能称为高性能呢?

    作者回复: 欢迎压测

    2021-02-17
    1
  • TinyCalf
    //初始化一个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就是一个数组。

    2020-11-17
    1
  • J.M.Liu
    c语言写bbs网站的年代,真的是太疯狂了,一个一个字符的print(哭脸)

    作者回复: 是有点疯狂。(哭脸)

    2019-11-03
    1
  • 沉淀的梦想
    在ubuntu系统上一运行老师的程序就会出现“interrupted by signal 11: SIGSEGV”错误

    作者回复: 我也是ubuntu系统啊,有同学碰到同样的问题么?

    2019-10-30
    6
    1
  • dll
    好不容易看完了 打卡纪念一下

    编辑回复: 真棒!

    2022-08-04
收起评论
显示
设置
留言
28
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部