網絡協議 11 - Socket 編程(下):眼見爲實耳聽爲虛

系列文章傳送門:php

  1. 網絡協議 1 - 概述
  2. 網絡協議 2 - IP 是怎麼來,又是怎麼沒的?
  3. 網絡協議 3 - 從物理層到 MAC 層
  4. 網絡協議 4 - 交換機與 VLAN:辦公室太複雜,我要回學校
  5. 網絡協議 5 - ICMP 與 ping:投石問路的偵察兵
  6. 網絡協議 6 - 路由協議:敢問路在何方?
  7. 網絡協議 7 - UDP 協議:性善碰到城會玩
  8. 網絡協議 8 - TCP 協議(上):性惡就要套路深
  9. 網絡協議 9 - TCP協議(下):聰明反被聰明誤
  10. 網絡協議 10 - Socket 編程(上):實踐是檢驗真理的惟一標準

    以前咱們基本瞭解了網絡通訊裏的大部分協議,一直都是在「聽」的過程。不少人都會以爲,好像看懂了,但關了頁面回憶起來,好像又什麼都沒懂。此次我們就「真槍實彈」的碼起來,再用一個「神器」-網絡分析系統詳細跟蹤下數據包的生命歷程,讓咱們的理論真實的呈現出來,對網絡通訊感興趣的博友,還能夠本身拿着系統分析一遍,你必定會大有所獲。html

    很少說,直接上代碼。有興趣的博友能夠按各編程語言進行相關改寫,而後拿着咱們的分析系統真實的看看網絡通訊過程。編程

本機請求轉發到網關

    代碼中的 192.168.1.10 是內網另外一臺服務器,樓主的 IP 是 192.168.1.73。在本機跑服務器的時候,要作一個路由配置,不然分析系統沒法抓取相關的包。window 下可按下面步驟配置:服務器

  1. 管理員身份打開 DOS 窗口;
  2. route add 本機ip mask 255.255.255.255 網關ip(路由轉發,還記得嗎?忘記了?點我點我點我);

    什麼?不知道怎麼查 IP 和網關?點我告訴你
    操做完成後記得刪除轉發規則,不然,你會發現本機的請求,速度會變得很慢、、、
    實例:網絡

// 添加路由轉發規則
route add 192.168.1.73 mask 255.255.255.255 192.168.1.1 

// 刪除轉發規則
route delete 192.168.1.73

基於 TCP 的 Socket

    服務端: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

基於 UDP 的Socket

<?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

分析系統介紹

    上面用到的分析系統叫:科來網絡分析系統,點我下載。這個分析系統很良心,提供了一個免費的技術交流版。有興趣的小夥伴能夠下載下來玩玩,很強大。

相關文章
相關標籤/搜索