[TOC]
## 前言
### 說在前面
**把**他叫做筆記,就是因為這些內容是我在閱讀了各種書籍,論壇以及博客之后自己整理的,所以,不是百分百原創但是又可以說是百分百原創。所以特此聲明一下。
**整**理這篇筆記我用了好幾天的時間,雖然自己知道很多內容,但真的要一點一點整理出來還是挺麻煩的,因為要把和Swoole相關的知識單獨拎出來,說簡單也很簡單,說難其實也挺難。
其實搞Swoole的話,我感覺最基本的就是TCP,UDP了。之前我把OSI七層的全寫了,但是感覺太亂了,這么多層,知識點本來就雜亂,而且任意一層都會有不同的技術細節,都整理出來,GET不到重點,所以這里我們就從IP/TCP/UPD/HTTP開始。
>ps:作為一個初級phper,下面的內容了解即可,如果想深入學習網絡編程,我推薦幾本書,這幾本書都是我看過并且經常翻閱的,都寫的很好,作為一個程序員想計算機知識自上而下不說精通,光完全理解都談何容易,所以個人感覺最好有個側重點比較好,所以書籍我也只是推薦,畢竟我自己走了不少彎路,曾經年幼無知的我連cpu上電時序圖都看,然而現在我只是個phper,curdboy~
>初級入門的話我推薦《圖解HTTP》
然后我看了《一本書讀懂TCP/IP》
下面的書是我平時當作字典遇到問題查的
《UNIX網絡編程 卷1:套接字聯網API》
《UNIX網絡編程 卷2:進程間通信》
《TCP&IP網絡編程》
## 正文
**首先貼一個TCP/IP協議棧的圖,下面的知識參照這個圖來理解。**
:-: 
### IP地址(網絡層)
IP地址眾所周知,Windows系統查詢網絡IP地址為ipconfig,Linux中為ifconfig或者ip addr命令。Windows如下圖所示。
:-: 
:-: **ipconfig效果圖**
IPv4的IP地址是32個字節的,如百度的115.239.210.27就是一個IP地址,它的作用是一個網卡的地址,具有定位作用。現在想一想MAC地址是一個網卡出廠就具備的全球唯一的地址,那么有了MAC地址后,怎么還需要IP地址了?答案就是沒有IP地址根本定位不了,給你一個百度的服務器MAC地址,你怎么能找到他了。MAC地址就像我們的身份證,IP地址就如同我們的家庭住址+名字。比如給你小王的MAC地址,你也不可能找到他,只有先通過他的家庭住址和名字,才能找到他。**所以說MAC地址不具備大范圍的定位作用,只有在局域網內才有作用**,這就是MAC地址和IP地址的一個區別。
32位的IP地址被分成了一下五類,又大大的減少了IP地址數量。A、B、C是有網絡號和主機號組成的。這樣是為了區別是不是在同一個局域網內,網絡號代表的是小區,主機號代表的是哪一號房間。這樣不同的小區就可以都有1001號房間了,但不會產生沖突,因為他們的網絡號不同。
:-: 
:-: **IP地址分類圖**
如下圖所示,A、B、C三類的最大主機數量,很容易產生浪費。私密地址代表什么了,我們上學的時候IP都是172.16開頭的,貌似很多大學的IP都是一樣的,你會問這不是產生沖突了嗎,只能說,這些IP在局域網里面是正常使用的,一段到了公網后,這個就會被替換成一個不沖突的IP了,數據請求返回時,再被替換回來,這樣就保證了不沖突,詳情下面會說,現在有個概念就可以了。
:-: 
**怎么判斷是否是同一個局域網的IP地址了?**
通過配置的子網掩碼。如下圖是一個C類的私有IP地址,網絡號為24位,主機號位8位,廣播地址為192.168.1.255,一般默認的網關為192.168.1.1.將子網掩碼和IP地址按位與計算后,就可以得到網絡號了,也就可以判斷目標IP是否是要出網關了。
D類地址是組播地址,也就是說這個組的網卡都會收到改組的數據。
此外還介紹一個特殊的IP地址,127.0.0.1。他是個環回接口,這個地址用于本機通信,經過內核處理后直接返回給本機。
:-: 
:-: **IPv4相關配置**
Linux中配置IP地址如下:
~~~cpp
sudo ifconfig eth1 192.168.1.174/24 //給eth1這個網卡配置IP和子網掩碼
sudo ifconfig eth1 up // 啟動
~~~
**簡單的數據發送過程:**系統會判斷數據包的目標IP是不是在同一個網段中,如果是是同一個網段,他會通過ARP協議廣播獲取目標IP的MAC地址,直接發送給目標即可。如果是跨網段,那么獲取網關的MAC地址,把數據發給網關就不管了。
>關于IP協議我就不寫了,如果后續有時間了,我爭取整理一份網絡編程的筆記,但應該會是很久之后了。
### TCP(傳輸層)
TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議,由IETF的RFC 793定義。在簡化的計算機網絡OSI模型中,它完成第四層傳輸層所指定的功能,用戶數據報協議(UDP)是同一層內 另一個重要的傳輸協議。在因特網協議族(Internet protocol suite)中,TCP層是位于IP層之上,應用層之下的中間層。不同主機的應用層之間經常需要可靠的、像管道一樣的連接,但是IP層不提供這樣的流機制,而是提供不可靠的包交換。
應用層向TCP層發送用于網間傳輸的、用8位字節表示的數據流,然后TCP把數據流分區成適當長度的報文段(通常受該計算機連接的網絡的數據鏈路層的最大傳輸單元( MTU)的限制)。之后TCP把結果包傳給IP層,由它來通過網絡將包傳送給接收端實體 的TCP層。TCP為了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然后接收端實體對已成功收到的包發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那么對應的數據包就被假設為已丟失將會被進行重傳。TCP用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和。
#### 三次握手
TCP是因特網中的傳輸層協議,使用三次握手協議建立連接。當主動方發出SYN連接請求后,等待對方回答SYN+ACK ,并最終對對方的 SYN 執行 ACK 確認。這種建立連接的方法可以防止產生錯誤的連接,TCP使用的流量控制協議是可變大小的滑動窗口協議。 TCP三次握手的過程如下:
* 客戶端發送SYN(SEQ=x)報文給服務器端,進入SYN\_SEND狀態。
* 服務器端收到SYN報文,回應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN\_RECV狀態。
* 客戶端收到服務器端的SYN報文,回應一個ACK(ACK=y+1)報文,進入Established狀態。
#### 連接成功
連接成功之后雙方即可互相傳輸字節流,并隨時可關閉連接,傳輸的數據有以下特性
* 傳輸的數據被tcp分割成了最適合發送的數據塊 傳遞給ip協議,這個發送數據稱為 報文段 或 段
* tcp作為可靠性連接,每次發送數據段,會啟動一個定時器,每次接收數據段,會發送一次確認,如果定時器沒有及時收到確認,則會重發數據
* TCP將保持它首部和數據的檢驗和。這是一個端到端的檢驗和,目的是檢測數據在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段(希望發端超時并重發)。
* 兩個應用程序通過TCP連接交換8bit字節構成的字節流。TCP不在字節流中插入記錄標識符。我們將這稱為字節流服務(bytestreamservice)。如果一方的應用程序先傳10字節,又傳20字節,再傳50字節,連接的另一方將無法了解發方每次發送了多少字節。只要自己的接收緩存沒有塞滿,TCP 接收方將有多少就收多少。一端將字節流放到TCP連接上,同樣的字節流將出現在TCP連接的另一端。
#### 四次揮手
建立一個連接需要三次握手,而終止一個連接要經過四次揮手,這是由TCP的半關閉(half-close)造成的。具體過程如下所示。
* 某個應用進程首先調用close,稱該端執行“主動關閉”(active close)。該端的TCP于是發送一個FIN分節,表示數據發送完畢。
* 接收到這個FIN的對端執行 “被動關閉”(passive close),這個FIN由TCP確認。
* 注意:FIN的接收也作為一個文件結束符(end-of-file)傳遞給接收端應用進程,放在已排隊等候該應用進程接收的任何其他數據之后,因為,FIN的接收意味著接收端應用進程在相應連接上再無額外數據可接收。
* 一段時間后,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這導致它的TCP也發送一個FIN。
* 接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。 既然每個方向都需要一個FIN和一個ACK,因此通常需要4個分節。
> “通常”是指,某些情況下,步驟1的FIN隨數據一起發送,另外,步驟2和步驟3發送的分節都出自執行被動關閉那一端,有可能被合并成一個分節。 在步驟2與步驟3之間,從執行被動關閉一端到執行主動關閉一端流動數據是可能的,這稱為“半關閉”(half-close)。 當一個Unix進程無論自愿地(調用exit或從main函數返回)還是非自愿地(收到一個終止本進程的信號)終止時,所有打開的描述符都被關閉,這也導致仍然打開的任何TCP連接上也發出一個FIN。 無論是客戶還是服務器,任何一端都可以執行主動關閉。通常情況是,客戶執行主動關閉,但是某些協議,例如,HTTP/1.0卻由服務器執行主動關閉。
#### php中的tcp
php可通過socket函數,swoole擴展,stream流函數進行創建tcp協議的socket,綁定網卡端口,進行tcp服務端/客戶端操作 在php中,我們并不需要了解tcp的握手/揮手,我們只需要知道ip:port能連接/創建 一個tcp服務端/客戶端就行了
使用php的socket,我們可以直接發送字符串,接收的也是字符串,其他一切都是語言,操作系統所需要做的事,
我們只需要處理好字符串的完整性,例如我們使用php做tcp服務端
* 客戶端連接成功后,發送了一個"abcabcabcabc"的字符串
* 而服務端每次只接收9個字節,那第一次獲取只會接收到"abcabcabc"的殘缺字符串,需要繼續獲取數據
### HTTP
超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議。所有的WWW文件都必須遵守這個標準。設計HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法。1960年美國人Ted Nelson構思了一種通過計算機處理文本信息的方法,并稱之為超文本(hypertext),這成為了HTTP超文本傳輸協議標準架構的發展根基。Ted Nelson組織協調萬維網協會(World Wide Web Consortium)和互聯網工程工作小組(Internet Engineering Task Force )共同合作研究,最終發布了一系列的RFC,其中著名的RFC 2616定義了HTTP 1.1。
#### 技術架構
HTTP是一個客戶端和服務器端請求和應答的標準(TCP)。客戶端是終端用戶,服務器端是網站。通過使用Web瀏覽器、網絡爬蟲或者其它的工具,客戶端發起一個到服務器上指定端口(默認端口為80)的HTTP請求。(我們稱這個客戶端)叫用戶代理(user agent)。應答的服務器上存儲著(一些)資源,比如HTML文件和圖像。(我們稱)這個應答服務器為源服務器(origin server)。在用戶代理和源服務器中間可能存在
多個中間層,比如代理,網關,或者隧道(tunnels)。盡管TCP/IP協議是互聯網上最流行的應用,HTTP協議并沒有規定必須使用它和(基于)它支持的層。 事實上,HTTP可以在任何其他互聯網協議上,或者在其他網絡上實現。HTTP只假定(其下層協議提供)可靠的傳輸,任何能夠提供這種保證的協議都可以被其使用。
通常,由HTTP客戶端發起一個請求,建立一個到服務器指定端口(默認是80端口)的TCP連接。HTTP服務器則在那個端口監聽客戶端發送過來的請求。一旦收到請求,服務器(向客戶端)發回一個狀態行,比如"HTTP/1.1 200 OK",和(響應的)消息,消息的消息體可能是請求的文件、錯誤消息、或者其它一些信息。
HTTP使用TCP而不是UDP的原因在于(打開)一個網頁必須傳送很多數據,而TCP協議提供傳輸控制,按順序組織數據,和錯誤糾正。
通過HTTP或者HTTPS協議請求的資源由統一資源標示符(Uniform Resource Identifiers)(或者,更準確一些,URLs)來標識。
:-: 
#### 過程解析
http一次請求的過程大概如下:
* 用戶在瀏覽器輸入www.baidu.com
* dns服務器解析/或者本機hosts,路由器hosts對比 獲得ip
* 瀏覽器訪問默認端口80,則訪問的tcp地址為 ip:80
* tcp協議3次握手,建立連接
* 發送一個http request請求頭
* 服務器獲得http request請求頭,表明該次訪問為http訪問,解析http請求頭,獲得請求類型,請求格式,以及請求數據(cookie,get,post數據)
* 服務器發送response響應數據,主動斷開
* 瀏覽器接收response響應數據,解析響應文本類型,解析數據,斷開連接
> https協議中,在請求以及響應時多了一層tls,ssl加密解密協議,默認端口從80變為了443
### WebSocket
- 微服務
- 服務器相關
- 操作系統
- 極客時間操作系統實戰筆記
- 01 程序的運行過程:從代碼到機器運行
- 02 幾行匯編幾行C:實現一個最簡單的內核
- 03 黑盒之中有什么:內核結構與設計
- Rust
- 入門:Rust開發一個簡單的web服務器
- Rust的引用和租借
- 函數與函數指針
- Rust中如何面向對象編程
- 構建單線程web服務器
- 在服務器中增加線程池提高吞吐
- Java
- 并發編程
- 并發基礎
- 1.創建并啟動線程
- 2.java線程生命周期以及start源碼剖析
- 3.采用多線程模擬銀行排隊叫號
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的應用分析
- 6.Daemon線程的創建以及使用場景分析
- 7.線程ID,優先級
- 8.Thread的join方法
- 9.Thread中斷Interrupt方法學習&采用優雅的方式結束線程生命周期
- 10.編寫ThreadService實現暴力結束線程
- 11.線程同步問題以及synchronized的引入
- 12.同步代碼塊以及同步方法之間的區別和關系
- 13.通過實驗分析This鎖和Class鎖的存在
- 14.多線程死鎖分析以及案例介紹
- 15.線程間通信快速入門,使用wait和notify進行線程間的數據通信
- 16.多Product多Consumer之間的通訊導致出現程序假死的原因分析
- 17.使用notifyAll完善多線程下的生產者消費者模型
- 18.wait和sleep的本質區別
- 19.完善數據采集程序
- 20.如何實現一個自己的顯式鎖Lock
- 21.addShutdownHook給你的程序注入鉤子
- 22.如何捕獲線程運行期間的異常
- 23.ThreadGroup API介紹
- 24.線程池原理與自定義線程池一
- 25.給線程池增加拒絕策略以及停止方法
- 26.給線程池增加自動擴充,閑時自動回收線程的功能
- JVM
- C&C++
- GDB調試工具筆記
- C&C++基礎
- 一個例子理解C語言數據類型的本質
- 字節順序-大小端模式
- Php
- Php源碼閱讀筆記
- Swoole相關
- Swoole基礎
- php的五種運行模式
- FPM模式的生命周期
- OSI網絡七層圖片速查
- IP/TCP/UPD/HTTP
- swoole源代碼編譯安裝
- 安全相關
- MySql
- Mysql基礎
- 1.事務與鎖
- 2.事務隔離級別與IO的關系
- 3.mysql鎖機制與結構
- 4.mysql結構與sql執行
- 5.mysql物理文件
- 6.mysql性能問題
- Docker&K8s
- Docker安裝java8
- Redis
- 分布式部署相關
- Redis的主從復制
- Redis的哨兵
- redis-Cluster分區方案&應用場景
- redis-Cluster哈希虛擬槽&簡單搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸縮調優
- 源碼閱讀筆記
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的東西
- 一次折騰了幾天的大華攝像頭調試經歷
- 搬磚實用代碼
- python讀取excel拼接sql
- mysql大批量插入數據四種方法
- composer好用的鏡像源
- ab
- 環境搭建與配置
- face_recognition本地調試筆記
- 虛擬機配置靜態ip
- Centos7 Init Shell
- 發布自己的Composer包
- git推送一直失敗怎么辦
- Beyond Compare過期解決辦法
- 我的Navicat for Mysql
- 小錯誤解決辦法
- CLoin報錯CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM無法連接到虛擬機
- Jetbrains相關
- IntelliJ IDEA 筆記
- CLoin的配置與使用
- PhpStormDocker環境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer