国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP回顧之socket編程

tomorrowwu / 1247人閱讀

摘要:如果你想體驗原味編程,用開頭的比較適合否則建議使用流函數(shù)。有關(guān)流的知識,請參考本人之前的博文回顧之流。接下來我們用流函數(shù)實現(xiàn)一個簡單的客戶端和服務(wù)端。流函數(shù)中的和兩個函數(shù)是我們想要的。本文目的是簡要介紹中的編程,行文到此已經(jīng)達到目的。

轉(zhuǎn)載請注明文章出處: https://tlanyan.me/php-review...
PHP回顧系列目錄

PHP基礎(chǔ)

web請求

cookie

web響應(yīng)

session

數(shù)據(jù)庫操作

加解密

Composer

創(chuàng)建自己的Composer包

發(fā)送郵件

IO

web開發(fā)一直是PHP的主戰(zhàn)場,也是PHP最為被世人所熟知的一面。其實只要你愿意去發(fā)掘,PHP除了做網(wǎng)頁在許多其他方面也是小能手。

本文簡要介紹PHP的Socket編程。

準備知識

在開始之前,希望你已經(jīng)知道網(wǎng)絡(luò)編程中的一些基本概念。比如OSI七層模型、TCP/IP四層模型;TCP中的三次握手、四次揮手等。這些概念是網(wǎng)絡(luò)編程的理論基礎(chǔ),實踐中不一定用得到,但能讓你把握整體脈絡(luò),更快的定位編程中出現(xiàn)的問題。

再說一下Socket。我們常說的網(wǎng)絡(luò)編程就是指Socket編程,它既指代實現(xiàn)了TCP/IP協(xié)議簇的一套網(wǎng)絡(luò)編程API,也指代一個客戶端與服務(wù)器的連接。socket是插座/接口的意思,計算機中常翻譯成“套接字”。實際中可以簡單的認為網(wǎng)絡(luò)編程與Socket編程等價,一個tcp連接的說法等價于一個socket。

PHP中的API

PHP中有以socket開頭的一套函數(shù)API用于Socket編程,PHP5引入“流”的抽象概念后,以stream開頭的一套API也可以用于網(wǎng)絡(luò)編程。兩者的主要區(qū)別是:

流是PHP中的核心概念,所以stream開頭的函數(shù)總是可用;sockets是PHP的一個拓展,雖然大部分情況下都默認啟用;

socket系列函數(shù)相對底層,而stream系列函數(shù)是高層的抽象。

如果你想體驗原味Socket編程,用socket開頭的API比較適合;否則建議使用流函數(shù)。有關(guān)流的知識,請參考本人之前的博文:PHP回顧之流。

接下來我們用流函數(shù)實現(xiàn)一個簡單的TCP客戶端和服務(wù)端。

客戶端

客戶端網(wǎng)絡(luò)編程可以歸結(jié)為簡單的三步:

連接服務(wù)端(connect);

收發(fā)消息(receive/send);

關(guān)閉連接(close)。

下面是客戶端的代碼,發(fā)送10條消息到服務(wù)端:

// client.php
$host = "127.0.0.1";
$port = 8000;

$socket = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errMsg);
if ($socket === false) {
    throw new RuntimeException("unable to create socket: " . $errMsg);
}
fwrite(STDOUT, "success connect to server: [{$host}:{$port}]...
");

foreach (range(1, 10) as $i) {
    if ($i % 5 === 0) {
        $method = "broadcast";
    } else {
        $method = "echo";
    }
    $args = [sprintf("The %dth greeting", $i)];
    $message = json_encode([
        "method" => $method,
        "args" => $args,
    ]);
    fwrite(STDOUT, "
send to server: $message
");
    $len = @fwrite($socket, $message);
    if ($len === 0) {
        fwrite(STDOUT, "socket closed
");
        break;
    }

    $msg = @fread($socket, 4096);
    if ($msg) {
        fwrite(STDOUT, "receive server: $msg
");
    }
    elseif (feof($socket)) {
       fwrite(STDOUT, "socket closed
");
       break;
    }

    sleep(2);
}

fwrite(STDOUT, "close connnection...
");
fclose($socket);

客戶端已經(jīng)搞定,接下來看服務(wù)端。

服務(wù)端

服務(wù)端編程也很簡單,四步搞定:

監(jiān)聽端口(listen);

接受新連接(accept);

收發(fā)網(wǎng)絡(luò)消息(receive/send);

循環(huán)第二步和第三步(loop)。

由于服務(wù)端一般是長時間運行,除非重啟或進程被殺死,極少會主動關(guān)閉服務(wù)。另外服務(wù)端一般需要長時間運行,所以應(yīng)當運行在CLI模式下(短連的客戶端代碼可以在web中使用,例如代替CURL獲取網(wǎng)頁內(nèi)容,連接redis/MQ等)。

我們簡單的將收到的消息返回客戶端(Echo服務(wù)器):

// server.php
$port = 8000;
$socket = @stream_socket_server("tcp://0.0.0.0:$port", $errno, $errMsg);
if ($socket === false) {
    throw new RuntimeException("fail to listen on port: {$port}!");
}
fwrite(STDOUT, "socket server listen on port: {$port}" . PHP_EOL);

while (true) {
    $client = @stream_socket_accept($socket);
    if ($client == false) {
        continue;
    }

    fwrite(STDOUT, "client:" . (int)$client . " connnected.
");
    @fwrite($client, "Welcome aboard!
");

    while (true) {
        $msg = @fread($client, 4096);
        if ($msg) {
            fwrite(STDOUT, "
receive client: $msg
");
            // echo
            @fwrite($client, $msg);
        } elseif (feof($client)) {
            fwrite(STDOUT, "client:" . (int)$client . " disconnnect!
");
            fclose($client);
            break;
        }
    }
}

先啟動服務(wù)端腳本:php server.php, 然后打開新的窗口啟動客戶端:php client.php。可以看到消息被正確的發(fā)送和接收。客戶端退出后,可多次重新運行客戶端腳本查看效果。

并發(fā)

同時運行兩個或以上客戶端,會發(fā)現(xiàn)第二個起卡住,前面的客戶端退出后才繼續(xù)運行?;仡櫡?wù)端代碼,可以看到accept一個客戶端后,服務(wù)端就專心為其服務(wù),直到斷開才服務(wù)下一個。

同時服務(wù)多個客戶端,這才是我們期望的。默認情況下socket處于阻塞模式,無數(shù)據(jù)時fread函數(shù)會一直等待,導(dǎo)致程序不能抽身服務(wù)其他客戶端。要同時服務(wù)多個客戶端,第一步是設(shè)置非阻塞模式,第二步是更改輪詢方式。流函數(shù)中的stream_set_blockingstream_select兩個函數(shù)是我們想要的。

將服務(wù)端的代碼更改如下:

// server.php
 $client) {
        while (true) {
            $msg = @fread($client, 4096);
            if ($msg) {
                fwrite(STDOUT, "receive client " . (int)$client . " message: $msg
");
                $json = json_decode($msg, true);
                if ($json) {
                    $method = $json["method"];
                    if ($method === "echo") {
                        @fwrite($client, $msg);
                    } else {
                        foreach ($clients as $cl) {
                            @fwrite($cl, "message from " . (int)$client . ": $msg");
                        }
                    }
                }
            } else {
                if (feof($client)) {
                    fwrite(STDOUT, "
client " . (int)$client . " closed.
");
                    fclose($client);
                    $key = array_search($client, $clients);
                    unset($clients[$key]);
                }
                break;
            }
        }
    }
}

然后啟動服務(wù)端:php server.php,再同時啟動多個客戶端,或者用多個進程同時發(fā)送消息(需安裝pcntl拓展):

// client.php
for ($index = 0; $index < 10; ++ $index) {
    $pid = pcntl_fork();
    if ($pid < 0) {
        fwrite(STDERR, "fail to fork!
");
        exit;
    }

    if ($pid === 0) {
        connectServer();  // connectServer就是上文中client.php中的代碼
        exit;
    }
}
// 父進程先退出,不會出現(xiàn)僵尸進程,忽略孤兒進程的處理

啟動客戶端后,可以看到服務(wù)端正確的同時處理多個客戶端,這正是我們期待的。

缺憾

上述代碼實現(xiàn)了客戶端和可并發(fā)的服務(wù)端,作為演示基本夠用。如果要投入到實踐中使用,至少有以下方面的不足:

多進程/多線程/協(xié)程缺失,除處理網(wǎng)絡(luò)消息外,不能(難)做其他邏輯業(yè)務(wù);

沒有協(xié)議解析,會導(dǎo)致多條信息合并成一條讀?。ɑ蛘咭粭l信息被拆成多條);

select低效且有并發(fā)連接數(shù)目限制,客戶端量大時需要poll/epoll等技術(shù);

每個方面展開來說至少都是一篇長文。本文目的是簡要介紹PHP中的Socket編程,行文到此已經(jīng)達到目的。由于網(wǎng)絡(luò)協(xié)議十分繁雜,想深入網(wǎng)絡(luò)編程請參閱更多權(quán)威文檔。

總結(jié)

本文基于PHP5引入的流簡要介紹了PHP中的Socket編程,并給出了一個簡單并發(fā)服務(wù)器的實現(xiàn)。文中代碼僅做演示用,在生產(chǎn)環(huán)境中,請使用成熟的網(wǎng)絡(luò)框架/庫。

參考

http://php.net/manual/en/book...

http://www.unixguide.net/netw...

http://php.net/manual/en/book...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/28901.html

相關(guān)文章

  • PHP回顧多進程編程

    摘要:多進程中與多進程相關(guān)的兩個重要拓展是和。函數(shù)執(zhí)行期間,主進程除了等待無法處理其他任務(wù),所以一般不認為這是多進程編程?;厥兆舆M程有兩種方式,一種是主進程調(diào)用函數(shù)等待子進程結(jié)束另外一種是處理信號。 轉(zhuǎn)載請注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請求 cookie web響應(yīng) session 數(shù)據(jù)庫操作 加解...

    lifesimple 評論0 收藏0
  • PHP回顧執(zhí)行流程及相關(guān)概念

    摘要:通過,腳本層無需過多考慮執(zhí)行的具體環(huán)境,而本身則可以讓針對自己的特點給出特有實現(xiàn)。模式下,也只執(zhí)行一次。這幾個概念的關(guān)系如下網(wǎng)關(guān)協(xié)議,與語言無關(guān),所以與關(guān)系也不大??偨Y(jié)本文簡要回顧了程序的架構(gòu)和執(zhí)行流程,并對幾個容易混淆概念做了介紹。 轉(zhuǎn)載請注明文章出處:https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請求 cookie we...

    jsdt 評論0 收藏0
  • PHP回顧協(xié)程

    摘要:本文先回顧生成器,然后過渡到協(xié)程編程。其作用主要體現(xiàn)在三個方面數(shù)據(jù)生成生產(chǎn)者,通過返回數(shù)據(jù)數(shù)據(jù)消費消費者,消費傳來的數(shù)據(jù)實現(xiàn)協(xié)程。解決回調(diào)地獄的方式主要有兩種和協(xié)程。重點應(yīng)當關(guān)注控制權(quán)轉(zhuǎn)讓的時機,以及協(xié)程的運作方式。 轉(zhuǎn)載請注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請求 cookie web響應(yīng) sess...

    Java3y 評論0 收藏0
  • PHP回顧IO

    摘要:命令行時返回值為,標準輸入輸出均指向終端可用進程號查看。會在腳本執(zhí)行完畢后關(guān)閉三個流,無需用戶手動關(guān)閉。與遠程網(wǎng)址交互是一個請求和響應(yīng)的過程,其中細節(jié)可參考本人之前的文章回顧之請求和回顧之響應(yīng),也可參考協(xié)議的權(quán)威文檔。 轉(zhuǎn)載請注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請求 cookie web響應(yīng) ses...

    happen 評論0 收藏0
  • SegmentFault D-Day 2016 「北京:后端」 活動回顧

    摘要:今年從北京站開始,分享主題與后端相關(guān)。嘉賓匯總高馳濤性能之路姜季廷的前后之道孫宏亮生態(tài)中的現(xiàn)狀與實踐信海龍異步化探索今年還會在其他九個城市巡回分享,感謝大家的關(guān)注與分享。 今年 SegmentFault D-Day 從北京站開始,分享主題與「后端」相關(guān)。當然,我們還會在其他九個城市巡回分享,歡迎大家關(guān)注,幫忙擴散。 開場介紹 首先是 youku 美女星宇對 SegmentFault 社...

    PingCAP 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<