文章目录

  • 全流程实现博客链接
  • 前引
  • (一) 万丈高楼平地起 步子得一步一步慢慢走
    • 1、simple echo server 0.01
      • 1、熟悉基本工具
      • 2、simple echo server 0.01 came out
      • 3、simple server 0.0.1 test
    • 2、simple echo server 0.05
      • 1、simple echo server 0.05 came out
      • 2、simple server 0.0.5 test
  • 结束语

全流程实现博客链接


从零开始自制实现C++ High-Performance WebServer 全流程记录(基于muduo网络库)


前引


鉴于笔者走的路线是 Linux后端开发 而且现在处于正在起步的阶段
而且发现网上对于 linux后端开发的项目 真的如何从零开始起步的文章太少了 所以笔者打算就从这篇开始 写一个后端刚起步的小白 慢慢一步步成长学习的路吧

当然 这肯定是会写一个系列的 最后的完结目标 是实现一个 底层实现思路类似于陈硕大佬的muduo网络库 基于基层网络库的框架之上 再实现出一个高性能静态http并发的服务器 这个也会是作为之后实习的项目而做的

毕竟后端开发 一个高性能的WebServer 感觉应该是必须要去选择做的项目 这里的话 就先推荐两本书籍吧 也是我由这两本书为基础 + muduo网络库源码 作为之后实现webserver的参考

《Linux高性能服务器编程》- 游双
《Linux多线程服务器编程》(基于muduo C++网络库)- 陈硕

第一本基本上是读完了 第二本目前在笔者写的时候 读到了第八章 muduo网络库设计与实现
很明显 第一本书就更偏向于linux服务器最基础的介绍 作为入门书籍 我觉得最合适不过了 如果一刚开始就来啃这本《linux多线程服务器编程》的话 肯定会是相当痛苦的

正如笔者现在的状态一样 阅读这本书的时候 揣着太多太多的疑惑与不解 如同在冰面上行走一样 稍有不慎便会一下子摔在地上 因为感觉看的时候确实太多疑惑与不解了

所以正是因为这样 笔者才会来写下这篇博客 并由此作为起点来记录 到最后由一个网络库实现出一个高性能静态web服务器


(一) 万丈高楼平地起 步子得一步一步慢慢走


何以取这个标题
哈哈 因为笔者打算先暂时停止看第二本《Linux高性能服务器编程》 而是先去把第一本书中 最后的 具有比较高的耦合性 线程池 + one loop per thread给实现出来 再继续去看那本书了

个人以为 作为初学者 直接上手这本书只会是心中徒有疑惑 原本我的计划是第一本书的源码放着 直接来看第二本书 网络库 不需要有前置的铺垫 直接来就好了 结果发现确实不行

所以这一篇标题取名为 万丈高楼平地起啊 先把一个有着雏形的webserver给写出来 捣鼓捣鼓出来了 整明白了 我们再说 之后建立网络库 套模板 去改良吧

顺带呢 再把第一本书里面的 HTTP 状态转移机给弄懂整出来吧 因为后面反正也用得到 说到状态转移 我就想到了之前 我做正则引擎的时候 最后用跳转表 前面的NFA DFA的状态转移 哈哈 所以这里看到这个词心里面就觉得倍感亲切啊 哈哈

废话也不多说了 我先去把源代码一行行给看懂了 然后凭借着自己的印象多少给捣鼓捣鼓出来再说吧


1、simple echo server 0.01


为什么笔者打算先从一个最简单的能够响应的echo 服务器开始下手呢 说来惭愧 看了看AupeUnp 自己一行代码也没敲过 包括看完第一本推荐的书的时候也是一行代码也没敲过

尽管很多框架零零碎碎的晓得了吧 但是没有自己上手过的东西总是虚的 那不妨先从一个有基本功能的echo server开始写起 然后支持并发啊 各种细节开始扣 然后最后再向http server开始写 毕竟http server呢 也只不过就是多了一个协议报头需要处理 还要一些其他的细节就要处理 高复用性的框架写出来了 那把服务器往里面套就完了


1、熟悉基本工具


所有的项目 东西都是一步一步积累起来的 本来想先写一下进程/线程池的 算了 先把一个最基本 没有协议 能够支持单行发送消息 最简单的发送消息的服务器写出来吧

其实刚开始尝试开头 我便感觉很多东西已经开始了 写一段这样简单的simple的代码 建议的话呢 别先去copy 每一行代码 每一个命令都先自己根据书里面讲的函数去写 到后面g++ 编译的时候 就可以发现自己哪里写错了 哪里有问题需要改

也是从这里开始 我也打算正式的 以后把vim作为我在Linux上面敲代码正式的编辑器 而不是那种文本编辑器了 在我写完一个simple server时 我才真的明白 就强迫自己去用这些快捷键 用多了之后发现真的很方便很便捷 用完vim后呢 对于客户端 也可以用用nc这样的瑞士军刀 来充当客户端 用用这些实用的工具 不管之前很多东西用的多顺 去尝试用这些实用 熟悉了能够大幅度提高生产力的小工具 还是相当不错的 之后的话 如果服务器出错了 也可以尝试用tcpdump去抓抓包 反正这里写的意义呢 就是仅仅在于希望各位笔者能够自己先开始用这些好用的tools


2、simple echo server 0.01 came out


这里就不多介绍了 也就从这里正式开始 一步步拓宽 一步步开始进阶的改善这个echo server了 最后再测试一下效果

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char* argv[])
{if (argc <= 2){printf( "Usage: %s ip_address portname\n", argv[0] );return 0;}const char* ip = argv[1];int port = atoi( argv[2] );int listenfd = socket( PF_INET, SOCK_STREAM, 0 );assert( listenfd >= 1 );struct sockaddr_in address;memset( &address, 0, sizeof( address ) );address.sin_family = AF_INET;address.sin_port = htons( port );inet_pton( AF_INET, ip, &address.sin_addr );int ret = 0;ret = bind( listenfd, (struct sockaddr*)( &address ), sizeof( address ) );assert( ret != -1 );ret = listen( listenfd, 5 );assert( ret != -1 );struct sockaddr_in client;socklen_t client_addrlength = sizeof( client );int sockfd = accept( listenfd, (struct sockaddr*)( &address ), &client_addrlength );char buf_size[1024] = {0};int recv_size = 0;recv_size = recv( sockfd, buf_size, sizeof( buf_size ) , 0);int send_size = 0;send_size = send( sockfd, buf_size , recv_size , 0 );close( sockfd );close( listenfd );return 0;
}

3、simple server 0.0.1 test


用一下实用的nc

服务器端 先打开服务器端 然后输入ip地址 选择回流的本地地址 然后端口选择2022


客户端 由于懒得写 就用nc命令来充当客户端了 用了才发现确实好用 哈哈 有尝试才有收获 光说不做假把式

结果发现 输入完第一条消息 确实回弹了第一条消息 但是客户端没有关闭 服务器由于笔者写的只返回第一条消息 直接关闭服务器 所以后面


2、simple echo server 0.05


莫想要一步登天 一口气吃成个胖子
一步步熟悉好每个函数 每个作用 一步布把脚步放踏实点 自己走上去才觉得心安嘛 嘿嘿

这里的话 就要引入出 单线程echo server 服务器了 只不过现在可以并发啦 能够处理并发但不是多线程的话 是因为我们用了I/O复用 Epoll函数
这种最基本的模型的话 在第二本书里面介绍过 应该是单线程Reactor了
epoll触发呢 采用的边沿ET触发 而不是LT触发 这一版本较上面一版本的话 肯定还是需要去再扣一下echo server的细节

话也不多说 走起吧


1、simple echo server 0.05 came out


写了一会 代码也都自己分析了一遍 自己开了几个端口
其实也就是用了一下 epoll原本我以为很快就写完了 结果还是花了一点时间 把写错的代码改回来 - - 下面直接放代码了

其实仔细想一下 就会很简单的发现下面代码有可能是有问题的
是因为有可能echo返回的字符串不完整 不是一整行
所以应该是需要前面加个 每一行的字节数 才能确定是否send了一个完整的行 既没有多也没有少

这部分就留到之后再完成吧

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>#define MAX_EVENTS_NUMBER 5int set_non_blocking( int fd )
{int old_state = fcntl( fd, F_GETFL );int new_state = old_state | O_NONBLOCK;fcntl( fd, F_SETFL, new_state );return old_state;
}void addfd( int epollfd , int fd )
{epoll_event event;event.events = EPOLLIN | EPOLLET;event.data.fd = fd;epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );set_non_blocking( fd );
}int main( int argc , char* argv[] )
{if (argc <= 2){printf( "Usage: %s ip_address portname\n", argv[0] );return 0;}const char* ip = argv[1];int port = atoi( argv[2] );int listenfd = socket( PF_INET, SOCK_STREAM, 0 );assert( listenfd >= 1 );struct sockaddr_in address;memset( &address, 0, sizeof( address ) );address.sin_family = AF_INET;address.sin_port = htons( port );inet_pton( AF_INET, ip, &address.sin_addr );int ret = 0;ret = bind( listenfd, (struct sockaddr*)( &address ), sizeof( address ) );assert( ret != -1 );ret = listen( listenfd, 5 );assert( ret != -1 );epoll_event events[ MAX_EVENTS_NUMBER ];int epollfd = epoll_create( 5 );assert( epollfd != -1);addfd( epollfd, listenfd );while(1){int number = epoll_wait( epollfd, events, MAX_EVENTS_NUMBER, -1 );if( number < 0 ){printf( "epoll_wait failed\n" );return -1;}for( int i = 0; i < number; ++i ){const auto& event = events[i];const auto eventfd = event.data.fd;if( eventfd == listenfd ){struct sockaddr_in client;socklen_t client_addrlength = sizeof( client );int sockfd = accept( listenfd, ( struct sockaddr* )( &address ),&client_addrlength );                    addfd( epollfd, sockfd );            }else if( event.events & EPOLLIN ){char buf[1024] = {0};while(1){memset( buf, '\0', sizeof( buf ) );int recv_size  = recv( eventfd, buf, sizeof( buf ), 0 );if( recv_size < 0 ){if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) ){break;}printf(" sockfd %d,recv msg failed\n", eventfd );close( eventfd );break;}else if( recv_size == 0){close( eventfd );break;  }else{send( eventfd, buf, recv_size, 0 );}  }}} }close( listenfd );return 0;
}

2、simple server 0.0.5 test


这里就简单的放一张图吧
下面就是两个client 一起发送消息的截图 我设置的是10秒后自动断连
所以在10秒后回到了shell

这里算是简单的测试了一下 想要继续测试的大家可以自己去尝试啦~


结束语


这部分算是最基础最基础的一些api的使用了
当然 这也是我第一次在c++/c环境下用这些api 也是第一次用epoll
感觉使用起来还是非常的简单的 哈哈

第一次感觉到了那句话 入门简单 精通难
确实是这样的 但第一篇作为入门的目的已经做到了
第一篇中 我们还是最后尝试去用了 单线程Reactor的I/O复用

在这里呢 我打算第二篇就开始前往 线程池 和 陈硕介绍的
one loop per thread 方向走了 其实这个one loop per thread呢 就是在第一本书中介绍的 那个高效的 半同步/半异步I/O模型

下面还是贴一下图吧
遗憾的是什么呢 用了one loop per thread 模型的代码 是用的进程池
用了线程池的代码呢 是用的全局队列的方式 也就是每个线程在一个全局队列中争抢业务

但是作为第一篇 个人感觉还是开了一个不错的好头的 至少让人感觉到 初初摸到了门路 哈哈 那这一篇就先写到这里 我们下一篇见了

从零开始自制实现WebServer(一)---- 万丈高楼平地起 步子得一步一步慢慢走相关推荐

  1. 从零开始自制实现WebServer(六)---- 跌跌撞撞奔向HTTP状态机 学习途中拾慧纠正过往细节偏差

    文章目录 全流程实现博客链接 前引 (六)跌跌撞撞奔向HTTP状态机 学习途中拾慧纠正过往细节偏差 1.工欲行其事 必先利其器 buffer横空出世 1.buffer.h 2.buffer.cc 2. ...

  2. 从零开始自制实现WebServer(十九)---- 正式系统的学习一下Git 捣鼓捣鼓github以及一些其他的小组件

    文章目录 全流程实现博客链接 前引 (十九)---- 正式系统的学习一下Git 捣鼓捣鼓github以及一些其他的小组件 1.悔!为什么不在一开始做项目的时候就用Git 错过学习实践Git的最好机会 ...

  3. 从零开始自制实现WebServer(五)---- 浅沿芳草鲜花小路静心踱步 拨云见雾终见多线程ThreadPool

    文章目录 全流程实现博客链接 前引 (五)---- 浅沿芳草鲜花小路静心踱步 拨云见雾终见多线程ThreadPool 1.理清头绪 直通主路 抓住重点 2.罗列代码 一回生二回熟 再次起航 1.ech ...

  4. 从零开始自制实现WebServer(十八)---- 对服务器做最后的压力测试 WebBench压测小工具 项目迎来终章

    文章目录 全流程实现博客链接 前引 (十八)---- 对服务器做最后的压力测试 WebBench压测小工具 项目迎来终章 1.正确处理Vmware-Station 处理器设置(初步调试过程) 2.正确 ...

  5. 从零开始自制实现WebServer(十六)---- 学习新工具CMake自动编写MakeFile 分门别类整理源文件心情愉悦

    文章目录 全流程实现博客链接 前引 (十六)---- 学习新工具CMake自动编写MakeFile 小改小动项目接近尾声 1.学习新工具 cmake / shell脚本 需要耐心与时间 2.分门别类整 ...

  6. 《操作系统真象还原》从零开始自制操作系统 自写源码实现 (fs相关文件)

    文章目录 专栏博客链接 fs相关文件 编写完的dir.c 编写完的dir.h 编写完的file.c 编写完的file.h 编写完的fs.c 编写完的fs.h 编写完的inode.c 编写完的inode ...

  7. 从零开始自制实现正则引擎 全流程记录

    文章目录 前引 Regex_Engine 1.0 1.全流程实现博客链接 2.源码仓库链接 前引 笔者在完成 正则引擎初版 如果算上有效时间的话 大概是花了一周的时间就完成 只包括了 最基础的正则引擎 ...

  8. 《操作系统真象还原》从零开始自制操作系统 全流程记录

    文章目录 前引 章节博客链接 实现源码链接 前引 这本<操作系统真象还原>里面一共有十五个章节 大约760页 这些系列博客也是我在做完哈工大操作系统Lab之后 觉得还是有些朦朦胧胧 毅然决 ...

  9. 从零开始带你一步一步使用 YOLOv3 测试自己的数据

    上一篇: 从零开始带你一步一步使用YOLOv3训练自己的数据 我给大家详细介绍了如何使用 YOLOv3 模型来训练自己的数据集.训练部分完成,本文将继续给大家详细介绍如何使用我们训练好的模型来进行图片 ...

  10. 从零开始带你一步一步使用YOLOv3测试自己的数据

    红色石头的个人网站:redstonewill.com 知乎:https://www.zhihu.com/people/red_stone_wl 公众号:AI有道(redstonewill) 上一篇: ...

最新文章

  1. 毕业论文中使用的技术—FileReader接口
  2. Mantis安装过程笔记
  3. 鳄鱼洗澡安卓版关卡重置
  4. java-第九章-编写程序,打印如图案,要求图案的行数由用户输入.
  5. Harbour.Space Scholarship Contest 2021-2022 F. Pairwise Modulo 逆向思维 + 树状数组
  6. python将整数逆序_python练手入门小项目:字符串的妙用
  7. linux系统无法识别固态硬盘_linux查看硬盘是不是ssd
  8. Upload LABS Pass-1
  9. fhq-treap模板
  10. leetcode 292 NimGame
  11. 【图像处理】基于matlab GUI图像处理(编辑+分析+调整+平滑+锐化+小波变换)【含Matlab源码 207期】
  12. MATLAB最新官方中文文档
  13. Linux补全命令分析,实现Linux下上下键和命令补全
  14. 计算机技巧数学,如何快速掌握数学技巧
  15. 【拉灯君第三弹】母函数-HDU1085-泰山下的AC
  16. Nat Chem Biol | 李大力/宋高洁/赵永祥合作开发“精准安全”的腺嘌呤碱基编辑器ABE9...
  17. ThinkPad E450 Fn键设置为传统方式
  18. 5g nr,PDSCH/PUSCH,UE如何选择MCS table
  19. super this
  20. 35岁,转行AI年薪100万,牛逼的人生无需解释

热门文章

  1. 详解透明网桥的三个功能
  2. 腾讯的星星海服务器芯片,腾讯云星星海重磅发布两款自研新品 打造软硬一体云计算基础设施...
  3. python 64式: 第28式、分布式锁与群组管理__3、tooz应用之分布式锁
  4. 获得当天的 零点 和 当天的最后 一秒的时刻
  5. 机器视觉工程师之关于程序员的头发
  6. php设备巡检管理,设备巡检管理:你必须知道的4个玩法
  7. [I T]深圳唯冠与苹果达成和解协议后被指拖欠律师费遭起诉
  8. 0/0型极限等于多少_两个重要极限的一点理解(下)
  9. 假若明天来临——《AI.未来》读后感3900字
  10. RAW 图像格式转换工具 bayer2rgb