찾다
백엔드 개발PHP 튜토리얼PHP 네트워크 프로그래밍의 차단 모델 허용 소개

이 기사의 내용은 PHP 네트워크 프로그래밍의 Accept 차단 모델에 대한 소개입니다. 내용이 매우 자세하므로 도움이 필요한 모든 사람에게 도움이 되기를 바랍니다.

Accept 차단 모델은 비교적 오래된 모델이지만 차단/비차단, 잠금, 시간 제한 재전송 등 흥미로운 지식이 많이 포함되어 있습니다.

서버 프로그램 acceptSever.php

<?php set_time_limit(0);  # 设置脚本执行时间无限制

class SocketServer 
{
    private static $socket;
    function SocketServer($port) 
    {
        global $errno, $errstr;
        if ($port < 1024) {
            die("Port must be a number which bigger than 1024/n");
        }
        
        $socket = stream_socket_server("tcp://0.0.0.0:{$port}", $errno, $errstr);
        if (!$socket) die("$errstr ($errno)");
        
        
        while ($conn = stream_socket_accept($socket, -1)) { // 这样设置不超时才有用
            static $id = 0; # 进程 id
            static $ct = 0; # 接收数据的长度  
            $ct_last = $ct; 
            $ct_data = &#39;&#39;; # 接受的数据
            $buffer = &#39;&#39;;  # 分段读取数据
        
            $id++; 
            echo "Client $id come" . PHP_EOL;
            
            # 持续监听
            while (!preg_match(&#39;{/r/n}&#39;, $buffer)) { // 没有读到结束符,继续读
//                if (feof($conn)) break; // 防止 popen 和 fread 的 bug 导致的死循环
                $buffer = fread($conn, 1024);
                echo &#39;R&#39; . PHP_EOL; #  打印读的次数
                $ct += strlen($buffer);
                $ct_data .= preg_replace(&#39;{/r/n}&#39;, &#39;&#39;, $buffer);
            }
            $ct_size = ($ct - $ct_last) * 8;
            echo "[$id] " . __METHOD__ . " > " . $ct_data . PHP_EOL;
            fwrite($conn, "Received $ct_size byte data./r/n");
            fclose($conn);
        }
        
        fclose($socket);
    }
}
new SocketServer(2000);

클라이언트 프로그램 acceptClient. php

<?php # 日志记录
function debug ($msg)
{
    error_log($msg, 3, &#39;./socket.log&#39;);
}

if ($argv[1]) {
    
    $socket_client = stream_socket_client(&#39;tcp://0.0.0.0:2000&#39;, $errno, $errstr, 30);
    
    /*    设置脚本为非阻塞    */
#    stream_set_blocking($socket_client, 0);

    /*    设置脚本超时时间    */
#    stream_set_timeout($socket_client, 0, 100000);
    
    if (!$socket_client) {
        die("$errstr ($errno)");
    } else {
        # 填充容器
        $msg = trim($argv[1]);

        for ($i = 0; $i < 10; $i++) {
            $res = fwrite($socket_client, "$msg($i)");
            usleep(100000);
            echo &#39;W&#39;; // 打印写的次数
#            debug(fread($socket_client, 1024)); // 将产生死锁,因为 fread 在阻塞模式下未读到数据时将等待
        }
        fwrite($socket_client, "/r/n"); // 传输结束符
        # 记录日志
        debug(fread($socket_client, 1024));
        fclose($socket_client);
    }
}
else {
    
//    $phArr = array();
//    for ($i = 0; $i < 10; $i++) {
//        $phArr[$i] = popen("php ".__FILE__." &#39;{$i}:test&#39;", &#39;r&#39;);
//    }
//    foreach ($phArr as $ph) {
//        pclose($ph);
//    }
    
    for ($i = 0; $i < 10; $i++) {
        system("php ".__FILE__." &#39;{$i}:test&#39;");    # 这里等于 php "当前文件" "脚本参数"
    }
}

코드 분석

먼저 위의 코드 논리를 설명하세요. 클라이언트 acceptClient.php는 루프에서 데이터를 보내고 마지막으로 서버에 종료자를 보냅니다. Accept Server.php는 수락 차단 방법을 사용하여 소켓 연결을 받습니다. 그런 다음 종료자가 수신될 때까지 수신 데이터를 반복하고 결과 데이터(수신된 바이트 수)를 반환합니다. 클라이언트는 서버에서 반환된 데이터를 수신하여 로그에 기록합니다. 논리는 매우 간단하지만 분석할 가치가 있는 몇 가지 상황이 있습니다.

A> 기본적으로 php 소켓_client.php 테스트를 실행할 때 클라이언트는 10W를 인쇄하고 서버는 여러 R을 인쇄한 다음 수신된 데이터를 인쇄합니다. 소켓.log는 서버가 반환한 수신 결과 데이터를 기록하며, 그 효과는 다음과 같습니다.
PHP 네트워크 프로그래밍의 차단 모델 허용 소개
이 상황은 이해하기 쉬우므로 자세히 설명하지 않습니다. 그런 다음 telnet 명령을 사용하여 동시에 여러 클라이언트를 엽니다. 그림에 표시된 대로 서버는 한 번에 하나의 클라이언트만 처리합니다.
PHP 네트워크 프로그래밍의 차단 모델 허용 소개
다른 클라이언트는 이 뒤에 "대기"해야 합니다. IO를 차단하는 특성이 있습니다. 이 모델의 약점은 명백하며 효율성이 매우 낮습니다.

B> 소켓_client.php의 29번째 줄에 있는 주석 코드만 열고 php 소켓_client.php 테스트를 다시 실행하세요. 클라이언트는 W를 출력하고 서버도 R을 출력합니다. 그 후 두 프로그램 모두 중단됩니다. 그 이유는 논리를 분석한 후에 클라이언트가 종결자를 보내기 전에 서버에 데이터를 반환하기를 원하고 서버가 종결자를 수신하지 않았기 때문에 클라이언트에게도 종결자를 요청하기 때문이라는 것을 알 수 있습니다. 교착 상태를 유발합니다. W와 R 하나만 입력하는 이유는 fread가 기본적으로 차단되기 때문입니다. 이 교착 상태를 해결하려면 소켓_client.php의 17번째 줄에서 주석 코드를 열고 소켓에 대해 timeout을 0.1초로 설정해야 합니다. 다시 실행하면 W와 R이 0.1초마다 나타나는 것을 볼 수 있습니다. 그러면 정상적으로 종료되고 서버는 반환합니다. 수신 결과 데이터도 정상적으로 기록됩니다. fread가 기본적으로 차단되는 것을 볼 수 있습니다. 프로그래밍 시 특별한 주의가 필요합니다. 시간 초과가 설정되지 않으면 교착 상태가 쉽게 발생합니다.

C> 14줄의 주석만 열고 스크립트를 비차단으로 설정합니다. php 소켓_client.php 테스트를 실행합니다. 결과는 기본적으로 사례 A와 동일합니다. 유일한 차이점은 소켓.log가 반환을 기록하지 않는다는 것입니다. non-blocking을 실행하면 클라이언트가 서버의 응답 결과를 기다리지 않고 실행을 계속할 수 있기 때문입니다. 디버그가 실행될 때 읽기 데이터는 여전히 비어 있으므로 소켓.log도 비어 있습니다. 여기서는 차단 모드와 비차단 모드에서 실행되는 클라이언트의 차이점을 볼 수 있습니다. 물론 클라이언트가 결과 수락에 관심이 없는 경우 비차단 모드를 사용하여 최대 효율성을 얻을 수 있습니다.

D> php 소켓_client.php를 실행하면 위의 로직을 10번 연속으로 실행하게 됩니다. 여기에는 아무런 문제가 없지만 매우 이상한 점은 39-45줄의 코드를 사용하고 10개의 프로세스를 열면 된다는 것입니다. 동시에 서버 측의 무한 루프가 매우 이상해집니다! 나중에 조사 결과, popen으로 열린 프로세스에 의해 생성된 연결로 인해 fread 또는 소켓_read가 오류를 일으키고 빈 문자열을 직접 반환하여 무한 루프가 발생하는 한, PHP 소스 코드를 확인한 후 발견되었습니다. PHP의 popen 및 fread 함수는 C에 전혀 고유하지 않은 것으로 나타났으며, 여기에는 많은 양의 php_stream_* 구현 논리가 삽입되어 있으며 이는 내부 버그로 인한 소켓 연결 중단으로 인해 발생하는 것으로 추정됩니다. 해결 방법은 Socket_server.php에서 33줄의 코드를 여는 것입니다. 연결이 중단되면 루프에서 빠져나오지만 이렇게 하면 많은 데이터가 손실되므로 이 문제는 특별한 주의가 필요합니다!

관련 권장 사항:

PHP 및 Apache의 기본 응용 프로그램

PHP 정규 표현식이란 무엇입니까? PHP 정규식 사용 방법(코드 포함)

위 내용은 PHP 네트워크 프로그래밍의 차단 모델 허용 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么查找字符串是第几位php怎么查找字符串是第几位Apr 22, 2022 pm 06:48 PM

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.