ホームページ  >  記事  >  バックエンド開発  >  PHP ソケットの学習: 単純なソケット サーバーを作成します。

PHP ソケットの学習: 単純なソケット サーバーを作成します。

青灯夜游
青灯夜游転載
2023-02-01 19:13:319837ブラウズ

この記事では、初めて PHP ソケットを探索し、単純なソケット サーバーを作成してソケットについて学習します。お役に立てば幸いです。

PHP ソケットの学習: 単純なソケット サーバーを作成します。

#ソケットの中国語名はソケットといいますが、これが TCP/IP の「カプセル化」です。実際のネットワークは 4 層しかなく、上から順に、アプリケーション層、トランスポート層、ネットワーク層、データリンク層となっています。最もよく使われるhttpプロトコルはアプリケーション層に属するプロトコルであり、ソケットは簡単かつ大まかにトランスポート層のものとして理解できます。それでもわかりにくい場合は、tcp://218.221.11.23:9999 をもう少しざっくり追加するとわかりますか?これはTCPソケットです。

ソケットを使用すると、トランスポート層とネットワーク層を制御できるため、より強力なパフォーマンスと効率が得られます。ソケット プログラミングは、同時実行性の高いネットワーク サーバーを解決するために最も一般的に使用され、成熟したソリューションです。サーバー プログラマーはソケット プログラミング関連のスキルを習得する必要があります。

PHP には、ソケットを制御できる 2 つの関数セットがあります。1 つは、socket_ シリーズの関数で、もう 1 つは、stream_ シリーズの関数です。 Socket_ は C 言語でソケットを直接コピーして PHP によって実装されますが、stream_ はストリームの概念を使用してカプセル化して PHP によって実装されます。この一連の記事を簡単に開始するには、socket_* シリーズ関数を使用してみましょう。

最初に最も単純なソケット サーバーを作成します:

<?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として保存し、phpserver.phpを実行して実行します。クライアントで Telnet を使用できるので、別の端末を開き、telnet 127.0.0.1 9999 を実行して Enter を押します。実行結果は次のとおりです。

PHP ソケットの学習: 単純なソケット サーバーを作成します。

上記のコードを簡単に分析して、tcp ソケット サーバーのプロセスを説明します。

  • 1まず、プロトコル ファミリ (またはアドレス ファミリ)、ソケット タイプ、および特定のプロトコルに従ってソケットを作成します。
  • 2. 次に、前の手順で作成したソケットを ip:port にバインドします。
  • 3. 3 番目に、モニタリング linten を有効にします。
  • 4. 4 番目に、サーバー コードを終了せずに無限ループに入れます。クライアント接続がない場合、プログラムは受け入れ時にブロックされます。接続が入った場合にのみ下方向に実行され、再びループします。クライアントにサービスを提供し、永続的なサービスを提供します。

上記の場合には、2 つの大きな欠陥があります:

  • 1. 一度にサービスを提供できるクライアントは 1 つだけです。最初のクライアントがサービスを受けている場合、クライアントが helloworld を送信している間に 2 番目のクライアントが接続すると、2 番目のクライアントはしばらく待機する必要があります。
  • 2. 攻撃を受けやすく、サービス妨害が発生します。

上記の問題を分析した後、前述のマルチプロセスを思いつき、accpet がリクエストを受け取った後に子プロセスをフォークしてクライアントのリクエストを処理することができます。クライアントは、2 番目のクライアントのリクエストを処理するために子プロセスをフォークします。問題は解決されませんか?わかりました!コードをデモしましょう:

<?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として保存し、phpserver.phpを実行します。クライアントは引き続きtelnet 127.0.0.1 9999を使用しますが、今回はtelnetを実行するために2つの端末を開きます。最初のクライアントが接続した後、2 番目のクライアントも接続できることに注意することが重要です。実行結果は次のとおりです。

PHP ソケットの学習: 単純なソケット サーバーを作成します。

# クライアント要求のタイムスタンプを受信すると、サーバーが同時に N 個のクライアントにサービスを提供できることがわかります。しかし、考えてみてください。10,000 人のクライアントが依頼に来たらどうなるでしょうか?現時点では、サーバーは各クライアント接続を処理するために 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として保存した後、phpserver.phpを実行し、ps -ef | grep phpserver | grep -v grepを使用してサーバープロセスのステータスを確認します。

PHP ソケットの学習: 単純なソケット サーバーを作成します。

マスタープロセスが存在することがわかります。さらに、サービスを待っているサブプロセスが 10 個あり、同時に 10 個のクライアントにサービスを提供できます。 Telnet 127.0.0.1 9999 を通して試してみましょう。実行結果は次のとおりです:

PHP ソケットの学習: 単純なソケット サーバーを作成します。

さて、PHP の新しい旅シリーズは、簡単な紹介から始まります。次の記事では、さらに深い理論的基礎について説明します。

推奨学習: 「PHP ビデオ チュートリアル

以上がPHP ソケットの学習: 単純なソケット サーバーを作成します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。