这个网络库是我一直想完成的一个个人项目,到现在也只能说完成了基础的一部分,还有很多功能没完成。因为想往linux c++后台方向发展,所以就打算实现一个网络库,来串联学到的知识,包括APUE、UNP、《Effevtive C++》等等可以说是该方向必看的书籍。暑假的时候我照着陈硕先生的Muduo网络库模仿了一个,学到了很多,但对很多细节不解,所以现在就想重新实现,同时也熟悉整个开发流程。
在这里我将会详细的讲解整个项目的实现,一来是对自己的一个总结,二来也相当于开源出来,以飨读者。当前由于水平所限,可能会有许多谬误之处,望读者多加包涵,并加以指出。
如果觉得对你有所帮助,还望能点一下star给我个前进的动力!
silence1772/Sinetlib
Sinetlib是一个仿照Muduo实现的基于Reactor模式的多线程网络库,附有异步日志,要求Linux 2.6以上内核版本。同时内嵌一个简洁HTTP服务器,可实现路由分发及静态资源访问。
我使用的系统是Ubuntu 16.04
,因为并没有做多平台,只支持Linux,所以如果你使用的是Windows的话,建议你使用虚拟机或者安装双系统,系统版本自然是推荐Ubuntu 16.04,不建议安装最新版的原因是新版可能不太稳定,另外如果你安装的是CentOS等非Debian系的系统的话需要修改构建脚本里面sudo等命令。
因为源码文件有点多,我没有直接用Makefile来编译项目,而是使用CMake
。其实CMake只是多了一层抽象,你需要编写CMakeList.txt,然后它会把这个翻译成Makefile文件,最终使用的还是Makefile。
另外,我还使用了持续集成工具Travis CI
,它的功能就是当你修改了代码push到github上时,它会根据你写的配置文件自动构建项目,如果构建成功,符合预期,将能够直接部署到服务器上。但是这里我只是想熟悉一下这个工具,并没有使用部署功能,关于如何使用,我也不再赘述,有兴趣的读者可以自行去了解。
编写代码我并没有用IDE,而是使用文本编辑器Sublime Text,这方面看个人习惯吧。
├── build.sh................. 工程构建脚本
├── CMakeLists.txt........... CMake规则
├── .travis.yml.............. Travis CI配置文件
├── LICENSE.................. MIT开源协议
├── README.md................ 文档
├── src...................... 源码目录
│ ├── base................. 基础库
│ ├── log.................. 日志库
│ ├── net.................. 网络库
│ └── http................. HTTP服务器
└── example.................. 示例代码
项目的目录结构如上所示,build.sh
是整个项目的构建脚本,CMakeLists.txt
是CMake的构建规则,源码在src
目录中,这里面按照功能分成几个目录,一些示例程序放在example目录下。
首先我们要明确一下编译的目标。我们的项目是一个网络库,既然作为一个库,那么就是要编译成可被其他程序链接的静态库或动态库,另外还需要把example目录下的示例编译成可执行程序。
在Windows下我们一般都是用IDE来开发项目,编译只需轻轻一点,当然Linux下也可以使用IDE,比如Clion,但我一般都是直接写代码,然后使用g++进行编译,比如我们写了一个main.cpp文件,使用了c++11标准,然后要生成可执行程序main,可以使用命令:
g++ -std=c++11 main.cpp -o main
虽然上面只有一条语句,但实际上经过了几个步骤,对于每个.cpp文件,首先要通过预处理把头文件展开出来,然后再通过g++编译成汇编文件.s,接着经过汇编器得到目标文件.o。对于有多个源文件的程序,就是把这多个源文件生成的.o文件链接在一起得到可执行程序。
我们的目标是要得到一个库,那么什么是库呢?其实库就是一组目标文件.o的包,无论静态库还是动态库,都是由.o文件创建的。静态库的后缀名为 .a,动态库为 .so。以静态库为例,我们可以先生成.o文件,再打包生成静态库:
g++ -std=c++11 -c main.cpp
ar cr libmytest.a main.o
现在我们已经知道如何生成库了,但项目里的文件有点多,而且还分了目录,每次编译都要写这么长的命令显然有点力不从心,所以这里就轮到CMake上场了。CMake的语法我就不介绍了,我就讲一下大概的逻辑,可以配合源码来看Sinetlib/CMakeLists.txt
首先我们要设置一下编译选项,比如使用g++
编译器,并使用c++11
标准,在编译开启所有警告-Wall
。另外根据编译类型的不同设置不同的优化级别,可以在build.sh
中cmake的语句后加上要编译的类型。
接着我们要设置一下生成文件的目录,这里我设置的是执行cmake命令的目录。然后还需要设置安装的目录,安装的意思就是把生成的库和头文件放到某个目录,要使用时就指定这两个目录即可,我是放在/usr/local/lib和/usr/local/include,这是g++默认的搜索目录,这样我们就可以省去指定目录这一步了。
设置完路径后我们就开始处理源码了,首先遍历所有的源码文件,将它们的文件名保存起来,然后后面直接使用add_library
命令生成库,这里还需要使用target_link_libraries
命令链接到pthread库,因为我们使用了多线程。这里同时生成了静态和动态库。
最后,我们还需要编译示例程序,在example目录下也有一个CMakeLists.txt,我们只要使用add_subdirectory
把example目录包括进来即可。
上面我们已经写好了CMakeLists.txt,接下来就是使用它,这里通过build.sh脚本来构建。
因为生成的文件会在执行cmake命令的目录下,为了不污染原工程的目录,我们首先创建一个build
目录,然后进入该目录,因为主CMakeLists.txt在上一级目录,因此我们执行的是cmake ..
,这样就会把我们的CMakeLists.txt翻译成可以使用的MakeFile文件了,注意如果要编译Debug版本,可以使用cmake -DCMAKE_BUILD_TYPE=Debug ..
,后面加的-D其实就是给cmake传参数,然后我们写的CMakeLists.txt就可以根据这个参数来决定要干什么。
得到MakeFile文件后执行make
即可编译,接着执行make install
就是把生成的库安装到指定位置。
构建完成后,示例的程序在build/bin目录下,可以直接执行。如果你想修改源码后重新编译,那么不用重新执行build.sh
,只需在build目录下执行make即可,当然如果你增加或删除了文件,就需要重新执行了,因为cmake生成的makefile文件只是根据原先的目录情况生成的。