이 글은 여러분에게 PHP 소켓에 대한 초기 탐색을 제공할 것입니다. 간단한 소켓 서버를 만들어서 소켓에 대해 알아보겠습니다.
소켓의 중국 이름은 소켓이라고 합니다. 이런 것이 TCP/IP의 "캡슐화"입니다. 실제로 네트워크에는 실제로 애플리케이션 계층, 전송 계층, 네트워크 계층 및 데이터 링크 계층의 4개 계층만 있습니다. 가장 일반적으로 사용되는 http 프로토콜은 애플리케이션 계층에 속하는 프로토콜이며, 소켓은 간단하고 대략적으로 전송 계층의 것으로 이해될 수 있습니다. 그래도 이해하기 어렵다면 tcp://218.221.11.23:9999 를 좀 더 대략적으로 추가해 볼까요? 이것은 TCP 소켓입니다.
Socket은 전송 계층과 네트워크 계층을 제어할 수 있는 기능을 제공하여 더 강력한 성능과 더 높은 효율성을 얻습니다. 소켓 프로그래밍은 동시성 네트워크 서버를 해결하기 위해 가장 일반적으로 사용되는 성숙한 솔루션입니다. 모든 서버 프로그래머는 소켓 프로그래밍 관련 기술을 마스터해야 합니다.
PHP에는 소켓을 제어할 수 있는 두 가지 함수 세트가 있습니다. 하나는 소켓_ 함수 시리즈이고, 다른 하나는 스트림_ 함수 시리즈입니다. Socket_은 C언어에서 소켓을 PHP가 직접 복사하여 구현한 반면, stream_ 시스템은 PHP를 사용하여 스트림 개념을 사용하여 캡슐화합니다. 소켓_* 시리즈 함수를 사용하여 이 기사 시리즈를 간단하게 시작해 보겠습니다.
먼저 가장 간단한 소켓 서버를 만드세요:
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 进入while循环,不用担心死循环死机,因为程序将会阻塞在下面的socket_accept()函数上 while( true ){ // 此处将会阻塞住,一直到有客户端来连接服务器。阻塞状态的进程是不会占据CPU的 // 所以你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 向客户端发送一个helloworld $msg = "helloworld\r\n"; socket_write( $connection_socket, $msg, strlen( $msg ) ); socket_close( $connection_socket ); } socket_close( $listen_socket );
파일을 server.php로 저장한 후 php server.php를 실행하여 실행하세요. 클라이언트에서 telnet을 사용할 수 있습니다. 다른 터미널을 열고 telnet 127.0.0.1 9999를 실행하고 Enter를 누릅니다. 실행 결과는 다음과 같습니다.
TCP 소켓 서버의 프로세스를 설명하기 위해 위 코드를 간략하게 분석합니다.
위의 경우 두 가지 큰 결함이 있습니다.
위의 문제를 분석한 후 앞서 언급한 다중 프로세스를 생각했습니다. 그런 다음 accpet이 요청을 받은 후 클라이언트의 요청을 처리하기 위해 하위 프로세스를 포크할 수 있으므로 두 번째 클라이언트를 수락한 후 하위 프로세스를 포크할 수 있습니다. 두 번째 고객의 요청을 처리하는 과정을 거치면 문제가 해결되지 않을까요? 좋아요! 코드를 살펴보겠습니다.
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 进入while循环,不用担心死循环死机,因为程序将会阻塞在下面的socket_accept()函数上 while( true ){ // 此处将会阻塞住,一直到有客户端来连接服务器。阻塞状态的进程是不会占据CPU的 // 所以你不用担心while循环会将机器拖垮,不会的 $connection_socket = socket_accept( $listen_socket ); // 当accept了新的客户端连接后,就fork出一个子进程专门处理 $pid = pcntl_fork(); // 在子进程中处理当前连接的请求业务 if( 0 == $pid ){ // 向客户端发送一个helloworld $msg = "helloworld\r\n"; socket_write( $connection_socket, $msg, strlen( $msg ) ); // 休眠5秒钟,可以用来观察时候可以同时为多个客户端提供服务 echo time().' : a new client'.PHP_EOL; sleep( 5 ); socket_close( $connection_socket ); exit; } } socket_close( $listen_socket );
코드를 server.php로 저장한 다음 php server.php를 실행합니다. 클라이언트는 여전히 telnet 127.0.0.1 9999를 사용하지만 이번에는 telnet을 실행하기 위해 두 개의 터미널을 엽니다. 첫 번째 클라이언트가 연결된 후 두 번째 클라이언트도 연결할 수 있다는 점을 관찰하는 것이 중요합니다. 실행 결과는 다음과 같습니다.
클라이언트 요청의 타임스탬프를 수신하면 서버가 이제 N 클라이언트에 동시에 서비스를 제공할 수 있음을 알 수 있습니다. 그런데 생각해 보세요. 10,000명의 고객이 요청을 하러 온다면 어떨까요? 이때 서버는 각 클라이언트 연결을 처리하기 위해 10,000개의 하위 프로세스를 포크하여 사람을 죽일 것입니다. 포크 자체는 시스템 리소스를 낭비하는 시스템 호출입니다. 시스템이 1,000개의 포크를 견딜 수 있더라도 포크에서 나오는 1,000개의 하위 프로세스는 시스템 메모리를 소모하기에 충분합니다. 결국, 포크하기 쉬운 자식 프로세스는 현재 클라이언트를 처리한 후 닫히게 되며, 다음 요청을 위해 다시 포크해야 하는 것 자체가 낭비이고 부합하지 않습니다. 주류 사회주의 가치를 지향합니다. 악의적인 공격이 있는 경우 시스템이 충돌할 때까지 시스템 포크 수가 선형적으로 증가합니다.
그래서 저희는 다시 한 번 향상된 솔루션을 제안합니다. 비즈니스 볼륨을 추정한 다음 서비스가 시작될 때 고정된 수의 하위 프로세스를 분기할 수 있습니다. 각 하위 프로세스는 무한 루프에 있으며 클라이언트 연결이 중단되면 고객 요청이 처리됩니다. 완료되면 연결은 닫히지만 삭제되지는 않고 다음 클라이언트 요청을 계속 기다립니다. 이러한 방식으로 프로세스의 반복적인 포크로 인한 막대한 리소스 낭비를 피할 수 있을 뿐만 아니라 고정된 수의 하위 프로세스를 통해 무한한 포크로 인해 시스템이 충돌하는 것을 방지합니다.
<?php $host = '0.0.0.0'; $port = 9999; // 创建一个tcp socket $listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); // 将socket bind到IP:port上 socket_bind( $listen_socket, $host, $port ); // 开始监听socket socket_listen( $listen_socket ); // 给主进程换个名字 cli_set_process_title( 'phpserver master process' ); // 按照数量fork出固定个数子进程 for( $i = 1; $i <= 10; $i++ ){ $pid = pcntl_fork(); if( 0 == $pid ){ cli_set_process_title( 'phpserver worker process' ); while( true ){ $conn_socket = socket_accept( $listen_socket ); $msg = "helloworld\r\n"; socket_write( $conn_socket, $msg, strlen( $msg ) ); socket_close( $conn_socket ); } } } // 主进程不可以退出,代码演示比较粗暴,为了不保证退出直接走while循环,休眠一秒钟 // 实际上,主进程真正该做的应该是收集子进程pid,监控各个子进程的状态等等 while( true ){ sleep( 1 ); } socket_close( $connection_socket );
파일을 server.php로 저장하고 php server.php를 실행한 다음 ps -ef | grep phpserver | grep -v grep을 사용하여 서버 프로세스 상태를 확인하세요.
마스터 프로세스가 존재하는 것을 볼 수 있으며, 동시에 10명의 클라이언트에게 서비스를 제공할 수 있는 10개의 하위 프로세스가 있습니다. telnet 127.0.0.1 9999로 한번 해보자. 실행 결과는 다음과 같다.
자, php의 새로운 여정 시리즈는 간단한 소개부터 시작한다! 다음 기사에서는 좀 더 심오한 이론적 기초를 설명할 것입니다.
추천 학습: "PHP 비디오 튜토리얼"
위 내용은 PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!