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

資訊專(zhuān)欄INFORMATION COLUMN

PHP回顧之socket編程

tomorrowwu / 1240人閱讀

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

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

PHP基礎(chǔ)

web請(qǐng)求

cookie

web響應(yīng)

session

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

加解密

Composer

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

發(fā)送郵件

IO

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

本文簡(jiǎn)要介紹PHP的Socket編程。

準(zhǔn)備知識(shí)

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

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

PHP中的API

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

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

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

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

接下來(lái)我們用流函數(shù)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的TCP客戶(hù)端和服務(wù)端。

客戶(hù)端

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

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

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

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

下面是客戶(hù)端的代碼,發(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);

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

服務(wù)端

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

監(jiān)聽(tīng)端口(listen);

接受新連接(accept);

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

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

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

我們簡(jiǎn)單的將收到的消息返回客戶(hù)端(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;
        }
    }
}

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

并發(fā)

同時(shí)運(yùn)行兩個(gè)或以上客戶(hù)端,會(huì)發(fā)現(xiàn)第二個(gè)起卡住,前面的客戶(hù)端退出后才繼續(xù)運(yùn)行。回顧服務(wù)端代碼,可以看到accept一個(gè)客戶(hù)端后,服務(wù)端就專(zhuān)心為其服務(wù),直到斷開(kāi)才服務(wù)下一個(gè)。

同時(shí)服務(wù)多個(gè)客戶(hù)端,這才是我們期望的。默認(rèn)情況下socket處于阻塞模式,無(wú)數(shù)據(jù)時(shí)fread函數(shù)會(huì)一直等待,導(dǎo)致程序不能抽身服務(wù)其他客戶(hù)端。要同時(shí)服務(wù)多個(gè)客戶(hù)端,第一步是設(shè)置非阻塞模式,第二步是更改輪詢(xún)方式。流函數(shù)中的stream_set_blockingstream_select兩個(gè)函數(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;
            }
        }
    }
}

然后啟動(dòng)服務(wù)端:php server.php,再同時(shí)啟動(dòng)多個(gè)客戶(hù)端,或者用多個(gè)進(jìn)程同時(shí)發(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;
    }
}
// 父進(jìn)程先退出,不會(huì)出現(xiàn)僵尸進(jìn)程,忽略孤兒進(jìn)程的處理

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

缺憾

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

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

沒(méi)有協(xié)議解析,會(huì)導(dǎo)致多條信息合并成一條讀取(或者一條信息被拆成多條);

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

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

總結(jié)

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

參考

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

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

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

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

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

相關(guān)文章

  • PHP回顧多進(jìn)程編程

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

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

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

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

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

    Java3y 評(píng)論0 收藏0
  • PHP回顧IO

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

    happen 評(píng)論0 收藏0
  • SegmentFault D-Day 2016 「北京:后端」 活動(dòng)回顧

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

    PingCAP 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<