系列文章傳送門:php
以前咱們基本瞭解了網絡通訊裏的大部分協議,一直都是在「聽」的過程。不少人都會以爲,好像看懂了,但關了頁面回憶起來,好像又什麼都沒懂。此次我們就「真槍實彈」的碼起來,再用一個「神器」-網絡分析系統詳細跟蹤下數據包的生命歷程,讓咱們的理論真實的呈現出來,對網絡通訊感興趣的博友,還能夠本身拿着系統分析一遍,你必定會大有所獲。html
很少說,直接上代碼。有興趣的博友能夠按各編程語言進行相關改寫,而後拿着咱們的分析系統真實的看看網絡通訊過程。編程
代碼中的 192.168.1.10 是內網另外一臺服務器,樓主的 IP 是 192.168.1.73。在本機跑服務器的時候,要作一個路由配置,不然分析系統沒法抓取相關的包。window 下可按下面步驟配置:服務器
什麼?不知道怎麼查 IP 和網關?點我告訴你
操做完成後記得刪除轉發規則,不然,你會發現本機的請求,速度會變得很慢、、、
實例:網絡
// 添加路由轉發規則 route add 192.168.1.73 mask 255.255.255.255 192.168.1.1 // 刪除轉發規則 route delete 192.168.1.73
服務端:socket
<?php /** * 1. socket_create: 新建 socket * 2. socket_bind: 綁定 IP 和 port * 3. socket_listen: 監聽 * 4. socket_accept: 接收客戶端鏈接,返回鏈接 socket * 5. socket_read: 讀取客戶端發送數據 * 6. socket_write: 返回數據 * 7. socket_close: 關閉 socket */ $ip = '192.168.1.10'; $port = 23333; // $port = 80; $sk = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); !$sk && outInfo('socket_create error'); // 綁定 IP !socket_bind($sk, $ip, $port) && outInfo('socket_bind error'); // 監聽 !socket_listen($sk) && outInfo('sever listen error'); outInfo("Success Listen: $ip:$port", 'INFO'); while (true) { $accept_res = socket_accept($sk); !$accept_res && outInfo('sever accept error'); $reqStr = socket_read($accept_res, 1024); if (!$reqStr) outInfo('sever read error'); outInfo("Server receive client msg: $reqStr", 'INFO'); $response = 'Hello A, I am B. you msg is : ' . $reqStr . PHP_EOL; if (socket_write($accept_res, $response, strlen($response)) === false) { outInfo('response error'); } socket_close($accept_res); } socket_close($sk); function outInfo($errMsg, $level = 'ERROR') { if ($level === 'ERROR') { $errMsg = "$errMsg, msg: " . socket_strerror(socket_last_error()); } echo $errMsg . PHP_EOL; $level === 'ERROR' && die; }
客戶端:tcp
<?php /** * 1. socket_create: 新建 socket * 2. socket_connect: 鏈接服務端 * 3. socket_write: 給服務端發數據 * 4. socket_read: 讀取服務端返回的數據 * 5. socket_close: 關閉 socket */ $ip = '192.168.1.10'; $port = 23333; $sk = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); !$sk && outInfo('socket_create error'); !socket_connect($sk, $ip, $port) && outInfo('connect fail'); $msg = 'hello, I am A'; if (socket_write($sk, $msg, strlen($msg)) === false) { outInfo('socket_write fail'); } while ($res = socket_read($sk, 1024)) { echo 'server return message is:'. PHP_EOL. $res; } socket_close($sk);//工做完畢,關閉套接流 function outInfo($errMsg, $level = 'ERROR') { if ($level === 'ERROR') { $errMsg = "$errMsg, msg: " . socket_strerror(socket_last_error()); } echo $errMsg . PHP_EOL; $level === 'ERROR' && die; }
上面的代碼是基於 PHP 原生 Socket 寫的,其它語言也有對應 Socket 操做函數,進行相關的改寫便可。主要是下面的分析過程。編程語言
如上圖,這是咱們的分析系統捕捉的全部數據傳輸過程,你能夠真實的看到每一步都發生了什麼,以及對應的狀態的改變(圖片較大,建議右鍵在新標籤頁打開看)。函數
在圖中上半部分,咱們能夠看到分析系統將整個 TCP 的生命歷程分爲了三個階段:創建鏈接、交易、關閉鏈接。這和咱們以前瞭解的理論知識徹底相符。
左下角的交易時序圖,則詳細記錄了客戶端和服務端每次通訊的詳細信息,而右下角部分,則展現了每次通訊,數據包的狀態等信息。code
<?php /** * 1. socket_create: 新建 socket * 2. socket_bind: 綁定 IP 和 port * 3. socket_recvfrom: 讀取客戶端發送數據 * 4. socket_sendto: 返回數據 * 5. socket_close: 關閉 socket */ $ip = '192.168.1.10'; $port = 23333; $sk = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); !$sk && outInfo('socket_create error'); // 綁定 IP !socket_bind($sk, $ip, $port) && outInfo('socket_bind error'); outInfo("Success Listen: $ip:$port", 'INFO'); while (true) { $from = ''; $reqPort = 0; if (!socket_recvfrom($sk, $buf, 1024, 0, $from, $reqPort)) { outInfo('sever socket_recvfrom error'); } outInfo("Received msg $buf from remote address $from:$port", 'INFO'); $response = "Hello $from:$port, I am Server. your msg : " . $buf . PHP_EOL; if (!socket_sendto($sk, $response, strlen($response), 0, $from, $reqPort)) { outInfo('socket_sendto error'); } } socket_close($sk); function outInfo($errMsg, $level = 'ERROR') { if ($level === 'ERROR') { $errMsg = "$errMsg, msg: " . socket_strerror(socket_last_error()); } echo $errMsg . PHP_EOL; $level === 'ERROR' && die; }
客戶端:
<?php /** * 1. socket_create: 新建 socket * 2. socket_write: 給服務端發數據 * 3. socket_read: 讀取服務端返回的數據 * 4. socket_close: 關閉 socket */ $ip = '192.168.1.10'; $port = 23333; $sk = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); !$sk && outInfo('socket_create error'); $msg = 'hello, I am A'; if (!socket_sendto($sk, $msg, strlen($msg), 0, $ip, $port)) { outInfo('socket_sendto fail'); } $from = ''; $reqPort = 0; if (!socket_recvfrom($sk, $buf, 1024, 0, $from, $reqPort)) { outInfo('server socket_recvfrom error'); } outInfo("Received $buf from server address $from:$port", 'INFO'); socket_close($sk); function outInfo($errMsg, $level = 'ERROR') { if ($level === 'ERROR') { $errMsg = "$errMsg, msg: " . socket_strerror(socket_last_error()); } echo $errMsg . PHP_EOL; $level === 'ERROR' && die; }
UDP 數據包分析圖:
如上圖,UDP 數據包分析圖,明顯比 TCP 要簡單不少,人家單純嘛,就很少說了。不過要注意的,寫代碼的時候,UDP 的服務端,在循環裏千萬不要關閉 Socket。
上面用到的分析系統叫:科來網絡分析系統,點我下載。這個分析系統很良心,提供了一個免費的技術交流版。有興趣的小夥伴能夠下載下來玩玩,很強大。