本書從操作系統(tǒng)原理角度講解進行C++服務器開發(fā)所需掌握的技術棧。全書總計9章,第1~2章講解C++ 11/14/17新標準中的常用特性、新增類庫,以及C++開發(fā)者必須熟練掌握的開發(fā)調(diào)試工具鏈;第3~6章詳細講解C++服務器開發(fā)中的多線程編程技術、網(wǎng)絡編程重難點知識、網(wǎng)絡故障調(diào)試與排查常用工具,以及通信協(xié)議的設計思路、技巧;第7~8章詳細講解一個帶網(wǎng)絡通信組件的高性能服務的基本設計思路和注意事項;第9章進一步補充服務相關的常用模塊設計思路和方法。本書秉承的思想是,通過掌握技術原理,可以輕松制造“輪子”,靈活設計出優(yōu)雅、魯棒的服務,并快速學習新技術。無論是對于C/C++開發(fā)者、計算機專業(yè)的學生,還是對于想了解操作系統(tǒng)原理的讀者,本書都極具參考價值。
本人有一個千人的技術QQ群,群成員為C++服務器開發(fā)相關學習者及同行;另外是技術公眾號【高性能服務器開發(fā)】的維護者,目前粉絲8000左右。另有CSDN博客、個人站點也可用于宣傳。
第1章 C++必知必會 1
1.1 C++ RAII慣用法 1
1.1.1 版本1:最初的寫法 1
1.1.2 版本2:使用goto語句 3
1.1.3 版本3:使用do...while(0)循環(huán) 5
1.1.4 版本4:使用RAII慣用法 7
1.1.5 小結(jié) 12
1.2 pimpl慣用法 12
1.3 C++ 11/14/17新增的實用特性 17
1.4 統(tǒng)一的類成員初始化語法與std::initializer_list<T> 19
1.5 C++ 17注解標簽(attributes) 24
1.5.1 C++ 98/03的enumeration和C++ 11的enumerator 25
1.5.2 C++ 17的注解標簽 25
1.6 final、override關鍵字和=default、=delete語法 28
1.6.1 final關鍵字 28
1.6.2 override關鍵字 29
1.6.3 =default語法 31
1.6.4 =delete語法 32
1.7 auto關鍵字的用法 34
1.8 Range-based循環(huán)語法 35
1.8.1 自定義對象如何支持Range-based循環(huán)語法 37
1.8.2 for-each循環(huán)的實現(xiàn)原理 38
1.9 C++ 17結(jié)構(gòu)化綁定 39
1.10 stl容器新增的實用方法 43
1.10.1 原位構(gòu)造與容器的emplace系列函數(shù) 43
1.10.2 std::map的try_emplace方法與insert_or_assign方法 44
1.11 stl 中的智能指針類詳解 52
1.11.1 C++ 98/03的嘗試——std::auto_ptr 52
1.11.2 std::unique_ptr 55
1.11.3 std::shared_ptr 59
1.11.4 std::enable_shared_from_this 61
1.11.5 std::weak_ptr 63
1.11.6 智能指針對象的大小 67
1.11.7 使用智能指針時的注意事項 68
第2章 C++后端開發(fā)必備的工具和調(diào)試知識 71
2.1 SSH工具與FTP工具 71
2.1.1 Xshell 71
2.1.2 FTP 75
2.2 makefile與CMake 76
2.3 使用Visual Studio管理和閱讀開源項目代碼 83
2.4 gdb調(diào)試 87
2.4.1 被調(diào)試的程序需要帶調(diào)試信息 87
2.4.2 啟動gdb調(diào)試的方法 89
2.5 gdb常用命令詳解——利用gdb調(diào)試Redis 94
2.5.1 gdb常用調(diào)試命令概覽和說明 94
2.5.2 用gdb調(diào)試Redis前的準備工作 96
2.5.3 run命令 97
2.5.4 continue命令 98
2.5.5 break命令 98
2.5.6 tbreak命令 101
2.5.7 backtrace與frame命令 101
2.5.8 info break、enable、disable、delete命令 102
2.5.9 list命令 104
2.5.10 print與ptype命令 107
2.5.11 info與thread命令 109
2.5.12 next、step、until、finish、return、jump命令 112
2.5.13 disassemble命令 122
2.5.14 set args與show args命令 122
2.5.15 watch命令 123
2.5.16 display命令 124
2.5.17 dir命令 125
2.6 使用gdb調(diào)試多線程程序 126
2.6.1 調(diào)試多線程程序的方法 126
2.6.2 在調(diào)試時控制線程切換 128
2.7 使用gdb調(diào)試多進程程序——以調(diào)試Nginx為例 137
2.8 gdb實用調(diào)試技巧 143
2.8.1 將print輸出的字符串或字符數(shù)組完整顯示 144
2.8.2 讓被gdb調(diào)試的程序接收信號 144
2.8.3 函數(shù)明明存在,添加斷點時卻無效 145
2.8.4 調(diào)試中的斷點 146
2.8.5 自定義gdb調(diào)試命令 147
2.9 gdb tui——gdb圖形化界面 148
2.9.1 開啟gdb TUI模式 149
2.9.2 gdb TUI模式下的4個窗口 149
2.9.3 解決tui窗口不自動更新內(nèi)容的問題 150
2.9.4 窗口焦點切換 150
2.10 gdb的升級版——cgdb 151
2.11 使用VisualGDB調(diào)試 154
2.11.1 使用VisualGDB調(diào)試已經(jīng)運行的程序 155
2.11.2 使用VisualGDB從頭調(diào)試程序 156
第3章 多線程編程與資源同步 159
3.1 線程的基本概念及常見問題 159
3.1.1 主線程退出,支線程也將退出嗎 159
3.1.2 某個線程崩潰,會導致進程退出嗎 160
3.2 線程的基本操作 160
3.2.1 創(chuàng)建線程 160
3.2.2 獲取線程ID 166
3.2.3 等待線程結(jié)束 173
3.3 慣用法:將C++類對象實例指針作為線程函數(shù)的參數(shù) 178
3.4 整型變量的原子操作 184
3.4.1 為什么給整型變量賦值不是原子操作 185
3.4.2 Windows平臺上對整型變量的原子操作 186
3.4.3 C++ 11對整型變量原子操作的支持 187
3.5 Linux線程同步對象 190
3.5.1 Linux互斥體 190
3.5.2 Linux信號量 198
3.5.3 Linux條件變量 202
3.5.4 Linux讀寫鎖 208
3.6 Windows線程同步對象 217
3.6.1 WaitForSingleObject與WaitForMultipleObjects函數(shù) 217
3.6.2 Windows臨界區(qū)對象 219
3.6.3 Windows Event對象 224
3.6.4 Windows Mutex對象 229
3.6.5 Windows Semaphore對象 231
3.6.6 Windows讀寫鎖 235
3.6.7 Windows條件變量 238
3.6.8 在多進程之間共享線程同步對象 243
3.7 C++ 11/14/17線程同步對象 244
3.7.1 std::mutex系列 244
3.7.2 std::shared_mutex 248
3.7.3 std::condition_variable 253
3.8 如何確保創(chuàng)建的線程一定能運行 256
3.9 多線程使用鎖經(jīng)驗總結(jié) 258
3.9.1 減少鎖的使用次數(shù) 258
3.9.2 明確鎖的范圍 259
3.9.3 減少鎖的使用粒度 259
3.9.4 避免死鎖的一些建議 260
3.9.5 避免活鎖的一些建議 262
3.10 線程局部存儲 262
3.10.1 Windows的線程局部存儲 262
3.10.2 Linux的線程局部存儲 264
3.10.3 C++ 11 的 thread_local 關鍵字 267
3.11 C庫的非線程安全函數(shù) 268
3.12 線程池與隊列系統(tǒng)的設計 270
3.12.1 線程池的設計原理 270
3.12.2 環(huán)形隊列 275
3.12.3 消息中間件 275
3.13 纖程(Fiber)與協(xié)程(Routine) 277
3.13.1 纖程 277
3.13.2 協(xié)程 280
第4章 網(wǎng)絡編程重難點解析 282
4.1 學習網(wǎng)絡編程時應該掌握的socket函數(shù) 282
4.1.1 在Linux上查看socket函數(shù)的幫助信息 283
4.1.2 在Windows上查看socket函數(shù)的幫助信息 285
4.2 TCP網(wǎng)絡通信的基本流程 286
4.3 設計跨平臺網(wǎng)絡通信庫時的一些socket函數(shù)用法 290
4.3.1 socket數(shù)據(jù)類型 290
4.3.2 在Windows上調(diào)用socket函數(shù) 290
4.3.3 關閉socket函數(shù) 291
4.3.4 獲取socket函數(shù)的錯誤碼 291
4.3.5 套接字函數(shù)的返回值 293
4.3.6 select函數(shù)第1個參數(shù)的問題 293
4.3.7 錯誤碼WSAEWOULDBLOCK和EWOULDBLOCK 294
4.4 bind函數(shù)重難點分析 294
4.4.1 對bind函數(shù)如何選擇綁定地址 294
4.4.2 bind函數(shù)的端口號問題 295
4.5 select函數(shù)的用法和原理 302
4.5.1 Linux上的select函數(shù) 302
4.5.2 Windows上的select函數(shù) 317
4.6 socket的阻塞模式和非阻塞模式 318
4.6.1 如何將socket設置為非阻塞模式 318
4.6.2 send和recv函數(shù)在阻塞和非阻塞模式下的表現(xiàn) 320
4.6.3 非阻塞模式下send和recv函數(shù)的返回值總結(jié) 331
4.6.4 阻塞與非阻塞socket的各自適用場景 333
4.7 發(fā)送0字節(jié)數(shù)據(jù)的效果 333
4.8 connect函數(shù)在阻塞和非阻塞模式下的行為 339
4.9 連接時順便接收第1組數(shù)據(jù) 343
4.10 如何獲取當前socket對應的接收緩沖區(qū)中的可讀數(shù)據(jù)量 346
4.10.1 分析 346
4.10.2 注意事項 350
4.11 Linux EINTR錯誤碼 351
4.12 Linux SIGPIPE信號 352
4.13 Linux poll 函數(shù)的用法 353
4.14 Linux epoll模型 361
4.14.1 基本用法 361
4.14.2 epoll_wait與poll函數(shù)的區(qū)別 363
4.14.3 LT 模式和ET 模式 363
4.14.4 EPOLLONESHOT 選項 380
4.15 高效的readv和writev函數(shù) 386
4.16 主機字節(jié)序和網(wǎng)絡字節(jié)序 387
4.16.1 主機字節(jié)序 387
4.16.2 網(wǎng)絡字節(jié)序 388
4.16.3 操作系統(tǒng)提供的字節(jié)轉(zhuǎn)換函數(shù)匯總 389
4.17 域名解析API介紹 390
第5章 網(wǎng)絡通信故障排查常用命令 397
5.1 ifconfig命令 397
5.2 ping命令 401
5.3 telnet命令 402
5.4 netstat命令 407
5.5 lsof命令 409
5.6 nc命令 412
5.7 curl命令 415
5.8 tcpdump命令 416
第6章 網(wǎng)絡通信協(xié)議設計 422
6.1 理解TCP 422
6.2 如何解決粘包問題 423
6.3 解包與處理 425
6.4 從struct到TLV 430
6.4.1 協(xié)議的演化 430
6.4.2 協(xié)議的分類 434
6.4.3 協(xié)議設計工具 434
6.5 整型數(shù)值的壓縮 435
6.6 設計通信協(xié)議時的注意事項 437
6.6.1 字節(jié)對齊 437
6.6.2 顯式地指定整型字段的長度 438
6.6.3 涉及浮點數(shù)時要考慮精度問題 438
6.6.4 大小端問題 438
6.6.5 協(xié)議與自動升級功能 438
6.7 包分片 439
6.8 XML與JSON格式的協(xié)議 444
6.9 一個自定義協(xié)議示例 445
6.10 理解HTTP 460
6.10.1 HTTP格式介紹 460
6.10.2 GET與POST方法 461
6.10.3 HTTP chunk編碼 465
6.10.4 HTTP客戶端的編碼實現(xiàn) 466
6.10.5 HTTP服務端的實現(xiàn) 466
6.10.6 HTTP與長連接 471
6.10.7 libcurl 471
6.10.8 Restful接口與Java Spring MVC 477
6.11 SMTP、POP3與郵件客戶端 478
6.11.1 郵件協(xié)議簡介 478
6.11.2 SMTP 479
6.11.3 POP3 494
6.11.4 郵件客戶端 499
6.12 WebSocket協(xié)議 499
6.12.1 WebSocket協(xié)議的握手過程 500
6.12.2 WebSocket協(xié)議的格式 503
6.12.3 WebSocket協(xié)議的壓縮格式 506
6.12.4 WebSocket協(xié)議裝包與解包示例 508
6.12.5 解析握手協(xié)議 512
第7章 單個服務的基本結(jié)構(gòu) 515
7.1 網(wǎng)絡通信組件的效率問題 515
7.1.1 高效網(wǎng)絡通信框架的設計原則 515
7.1.2 連接的被動關閉與主動關閉 519
7.1.3 長連接和短連接 519
7.2 原始的服務器結(jié)構(gòu) 520
7.3 一個連接對應一個線程模型 522
7.4 Reactor模式 523
7.5 one thread one loop思想 524
7.5.1 one thread one loop程序的基本結(jié)構(gòu) 524
7.5.2 線程的分工 525
7.5.3 喚醒機制的實現(xiàn) 527
7.5.4 handle_other_things方法的實現(xiàn)邏輯 532
7.5.5 帶定時器的程序結(jié)構(gòu) 533
7.5.6 one thread one loop的效率保障 534
7.6 收發(fā)數(shù)據(jù)的正確做法 534
7.6.1 如何收取數(shù)據(jù) 534
7.6.2 如何發(fā)送數(shù)據(jù) 535
7.6.3 不要多個線程同時利用一個socket收(發(fā))數(shù)據(jù) 538
7.7 發(fā)送、接收緩沖區(qū)的設計要點 538
7.7.1 為什么需要發(fā)送緩沖區(qū)和接收緩沖區(qū) 539
7.7.2 如何設計發(fā)送緩沖區(qū)和接收緩沖區(qū) 539
7.7.3 服務端發(fā)送數(shù)據(jù)時對端一直不接收的問題 543
7.8 網(wǎng)絡庫的分層設計 544
7.8.1 網(wǎng)絡庫設計中的各個層 544
7.8.2 將Session進一步分層 550
7.8.3 連接信息與EventLoop/Thread的對應關系 551
7.9 后端服務中的定時器設計 551
7.9.1 最簡單的定時器 551
7.9.2 定時器設計的基本思路 552
7.9.3 定時器邏輯的性能優(yōu)化 561
7.9.4 對時間的緩存 564
7.10 處理業(yè)務數(shù)據(jù)時是否一定要單獨開線程 565
7.11 非侵入式結(jié)構(gòu)與侵入式結(jié)構(gòu) 570
7.11.1 非侵入式結(jié)構(gòu) 570
7.11.2 侵入式結(jié)構(gòu) 571
7.12 帶有網(wǎng)絡通信模塊的服務器的經(jīng)典結(jié)構(gòu) 578
7.12.1 為何要將listenfd設置成非阻塞模式 578
7.12.2 基于one thread one loop結(jié)構(gòu)的經(jīng)典服務器結(jié)構(gòu) 584
7.12.3 服務器的性能瓶頸 586
第8章 Redis網(wǎng)絡通信模塊源碼分析 587
8.1 調(diào)試Redis環(huán)境與準備 587
8.1.1 Redis源碼編譯與啟動 587
8.1.2 通信示例與術語約定 589
8.2 探究redis-server端的網(wǎng)絡通信模塊 589
8.2.1 監(jiān)聽fd的初始化工作 589
8.2.2 接受客戶端連接 592
8.2.3 epollfd的創(chuàng)建 600
8.2.4 監(jiān)聽fd與客戶端fd是如何掛載到epollfd上的 601
8.2.5 readQueryFromClient函數(shù) 611
8.2.6 如何處理可寫事件 613
8.2.7 Redis 6.0多線程網(wǎng)絡I/O 620
8.2.8 Redis對客戶端的管理 635
8.2.9 客戶端斷開流程 646
8.2.10 Redis中收發(fā)緩沖區(qū)的設計 653
8.2.11 定時器邏輯 659
8.2.12 鉤子函數(shù) 662
8.2.13 redis-server端網(wǎng)絡通信模塊小結(jié) 662
8.3 探究redis-cli端的網(wǎng)絡通信模型 663
8.4 Redis的通信協(xié)議格式 673
8.4.1 請求命令格式 673
8.4.2 應答命令格式 674
8.4.3 多命令和流水線 677
8.4.4 特殊的redis-cli與內(nèi)聯(lián)命令 677
8.4.5 Redis對協(xié)議數(shù)據(jù)的解析邏輯 678
第9章 服務器開發(fā)中的常用模塊設計 681
9.1 斷線自動重連的應用場景和邏輯設計 681
9.2 ;顧C制與心跳包 683
9.2.1 TCP keepalive選項 683
9.2.2 應用層的心跳包機制設計 684
9.2.3 有代理的心跳包機制設計 689
9.2.4 帶業(yè)務數(shù)據(jù)的心跳包 690
9.2.5 心跳包與流量 690
9.2.6 心跳包與調(diào)試 691
9.2.7 心跳包與日志 691
9.3 日志模塊的設計 692
9.3.1 為什么需要日志 692
9.3.2 日志系統(tǒng)的技術實現(xiàn) 692
9.3.3 在C/C++中輸出網(wǎng)絡數(shù)據(jù)包日志 716
9.3.4 調(diào)試時的日志 719
9.3.5 統(tǒng)計程序性能日志 719
9.3.6 根據(jù)類型將日志寫入不同的文件中 725
9.3.7 集中式日志服務與分布式日志服務 725
9.3.8 從業(yè)務層面看在一條日志中應該包含什么內(nèi)容 727
9.3.9 在日志中不要出現(xiàn)敏感信息 729
9.3.10 開發(fā)過程中的日志遞進縮減策略 730
9.4 錯誤碼系統(tǒng)的設計 730
9.4.1 錯誤碼的作用 730
9.4.2 錯誤碼系統(tǒng)設計實踐 731
9.5 監(jiān)控端口 733