PHP 네트워크 프로그래밍의 차단 모델 허용 소개

不言
풀어 주다: 2023-04-03 13:32:02
원래의
2150명이 탐색했습니다.
이 기사의 내용은 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
php
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿