>  기사  >  백엔드 개발  >  PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

青灯夜游
青灯夜游앞으로
2023-02-01 19:13:319838검색

이 글은 여러분에게 PHP 소켓에 대한 초기 탐색을 제공할 것입니다. 간단한 소켓 서버를 만들어서 소켓에 대해 알아보겠습니다.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

소켓의 중국 이름은 소켓이라고 합니다. 이런 것이 TCP/IP의 "캡슐화"입니다. 실제로 네트워크에는 실제로 애플리케이션 계층, 전송 계층, 네트워크 계층 및 데이터 링크 계층의 4개 계층만 있습니다. 가장 일반적으로 사용되는 http 프로토콜은 애플리케이션 계층에 속하는 프로토콜이며, 소켓은 간단하고 대략적으로 전송 계층의 것으로 이해될 수 있습니다. 그래도 이해하기 어렵다면 tcp://218.221.11.23:9999 를 좀 더 대략적으로 추가해 볼까요? 이것은 TCP 소켓입니다.

Socket은 전송 계층과 네트워크 계층을 제어할 수 있는 기능을 제공하여 더 강력한 성능과 더 높은 효율성을 얻습니다. 소켓 프로그래밍은 동시성 네트워크 서버를 해결하기 위해 가장 일반적으로 사용되는 성숙한 솔루션입니다. 모든 서버 프로그래머는 소켓 프로그래밍 관련 기술을 마스터해야 합니다.

PHP에는 소켓을 제어할 수 있는 두 가지 함수 세트가 있습니다. 하나는 소켓_ 함수 시리즈이고, 다른 하나는 스트림_ 함수 시리즈입니다. Socket_은 C언어에서 소켓을 PHP가 직접 복사하여 구현한 반면, stream_ 시스템은 PHP를 사용하여 스트림 개념을 사용하여 캡슐화합니다. 소켓_* 시리즈 함수를 사용하여 이 기사 시리즈를 간단하게 시작해 보겠습니다.

먼저 가장 간단한 소켓 서버를 만드세요:

<?php
$host = &#39;0.0.0.0&#39;;
$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를 누릅니다. 실행 결과는 다음과 같습니다.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

TCP 소켓 서버의 프로세스를 설명하기 위해 위 코드를 간략하게 분석합니다.

  • 1 먼저 프로토콜 계열(또는 주소 계열)에 따라 소켓 유형을 나타냅니다. 소켓을 생성하기 위한 특정 프로토콜이 있습니다.
  • 2. 둘째, 이전 단계에서 생성한 소켓을 ip:port에 바인딩합니다.
  • 3.셋째, 리슨을 켜세요.
  • 4. 넷째, 서버 코드가 종료되지 않고 무한 루프에 들어가도록 합니다. 클라이언트 연결이 없을 때 프로그램은 연결이 들어올 때만 아래쪽으로 실행된 다음 다시 루프를 수행하여 지속적인 서비스를 제공합니다. 클라이언트.

위의 경우 두 가지 큰 결함이 있습니다.

  • 1. 한 번에 하나의 클라이언트에만 서비스를 제공할 수 있습니다. 첫 번째 클라이언트에게 helloworld를 보내는 동안 두 번째 클라이언트가 연결되면 두 번째 클라이언트는 반드시 잠시 기다리십시오.
  • 2. 공격을 받아 서비스 거부를 일으키기 쉽습니다.

위의 문제를 분석한 후 앞서 언급한 다중 프로세스를 생각했습니다. 그런 다음 accpet이 요청을 받은 후 클라이언트의 요청을 처리하기 위해 하위 프로세스를 포크할 수 있으므로 두 번째 클라이언트를 수락한 후 하위 프로세스를 포크할 수 있습니다. 두 번째 고객의 요청을 처리하는 과정을 거치면 문제가 해결되지 않을까요? 좋아요! 코드를 살펴보겠습니다.

<?php
$host = &#39;0.0.0.0&#39;;
$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().&#39; : a new client&#39;.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을 실행하기 위해 두 개의 터미널을 엽니다. 첫 번째 클라이언트가 연결된 후 두 번째 클라이언트도 연결할 수 있다는 점을 관찰하는 것이 중요합니다. 실행 결과는 다음과 같습니다.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

클라이언트 요청의 타임스탬프를 수신하면 서버가 이제 N 클라이언트에 동시에 서비스를 제공할 수 있음을 알 수 있습니다. 그런데 생각해 보세요. 10,000명의 고객이 요청을 하러 온다면 어떨까요? 이때 서버는 각 클라이언트 연결을 처리하기 위해 10,000개의 하위 프로세스를 포크하여 사람을 죽일 것입니다. 포크 자체는 시스템 리소스를 낭비하는 시스템 호출입니다. 시스템이 1,000개의 포크를 견딜 수 있더라도 포크에서 나오는 1,000개의 하위 프로세스는 시스템 메모리를 소모하기에 충분합니다. 결국, 포크하기 쉬운 자식 프로세스는 현재 클라이언트를 처리한 후 닫히게 되며, 다음 요청을 위해 다시 포크해야 하는 것 자체가 낭비이고 부합하지 않습니다. 주류 사회주의 가치를 지향합니다. 악의적인 공격이 있는 경우 시스템이 충돌할 때까지 시스템 포크 수가 선형적으로 증가합니다.

그래서 저희는 다시 한 번 향상된 솔루션을 제안합니다. 비즈니스 볼륨을 추정한 다음 서비스가 시작될 때 고정된 수의 하위 프로세스를 분기할 수 있습니다. 각 하위 프로세스는 무한 루프에 있으며 클라이언트 연결이 중단되면 고객 요청이 처리됩니다. 완료되면 연결은 닫히지만 삭제되지는 않고 다음 클라이언트 요청을 계속 기다립니다. 이러한 방식으로 프로세스의 반복적인 포크로 인한 막대한 리소스 낭비를 피할 수 있을 뿐만 아니라 고정된 수의 하위 프로세스를 통해 무한한 포크로 인해 시스템이 충돌하는 것을 방지합니다.

<?php
$host = &#39;0.0.0.0&#39;;
$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( &#39;phpserver master process&#39; );
// 按照数量fork出固定个数子进程
for( $i = 1; $i <= 10; $i++ ){
  $pid = pcntl_fork();
  if( 0 == $pid ){
    cli_set_process_title( &#39;phpserver worker process&#39; );
    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을 사용하여 서버 프로세스 상태를 확인하세요.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

마스터 프로세스가 존재하는 것을 볼 수 있으며, 동시에 10명의 클라이언트에게 서비스를 제공할 수 있는 10개의 하위 프로세스가 있습니다. telnet 127.0.0.1 9999로 한번 해보자. 실행 결과는 다음과 같다.

PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.

자, php의 새로운 여정 시리즈는 간단한 소개부터 시작한다! 다음 기사에서는 좀 더 심오한 이론적 기초를 설명할 것입니다.

추천 학습: "PHP 비디오 튜토리얼"

위 내용은 PHP 소켓 학습: 간단한 소켓 서버를 만드는 방법을 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제