PHPはシステムプログラミングのネットワークソケットとIOの多重化を実現します
この記事で共有する内容は、PHP でのシステム プログラミングのネットワーク ソケットと IO の多重化に関するものです。必要な方は参考にしてください。
長い間、PHP はほとんど使用されていませんでした。結局のところ、プログラミングはスクリプト言語であり、効率が大きなネックになります。ただし、PHP がソケット プログラミングに使用できないとは言えませんし、PHP のソケット プログラミングのパフォーマンスがそれほど低いとも言えません。たとえば、有名な PHP ソケット フレームワーク Workerman は純粋な PHP で開発されており、優れたパフォーマンスを誇るため、環境によっては PHP ソケット プログラミングのスキルを発揮できる場合もあります。
PHP は、C 言語ソケット ライブラリにあるものと同様の一連のメソッドを提供しており、これを呼び出すことができます:
socket_accept — Accepts a connection on a socket socket_bind — 给套接字绑定名字 socket_clear_error — 清除套接字或者最后的错误代码上的错误 socket_close — 关闭套接字资源 socket_cmsg_space — Calculate message buffer size socket_connect — 开启一个套接字连接 socket_create_listen — Opens a socket on port to accept connections socket_create_pair — Creates a pair of indistinguishable sockets and stores them in an array socket_create — 创建一个套接字(通讯节点) socket_get_option — Gets socket options for the socket socket_getopt — 别名 socket_get_option socket_getpeername — Queries the remote side of the given socket which may either result in host/port or in a Unix filesystem path, dependent on its type socket_getsockname — Queries the local side of the given socket which may either result in host/port or in a Unix filesystem path, dependent on its type socket_import_stream — Import a stream socket_last_error — Returns the last error on the socket socket_listen — Listens for a connection on a socket socket_read — Reads a maximum of length bytes from a socket socket_recv — 从已连接的socket接收数据 socket_recvfrom — Receives data from a socket whether or not it is connection-oriented socket_recvmsg — Read a message socket_select — Runs the select() system call on the given arrays of sockets with a specified timeout socket_send — Sends data to a connected socket socket_sendmsg — Send a message socket_sendto — Sends a message to a socket, whether it is connected or not socket_set_block — Sets blocking mode on a socket resource socket_set_nonblock — Sets nonblocking mode for file descriptor fd socket_set_option — Sets socket options for the socket socket_setopt — 别名 socket_set_option socket_shutdown — Shuts down a socket for receiving, sending, or both socket_strerror — Return a string describing a socket error socket_write — Write to a socket
詳細については、ソケットに関する PHP 公式マニュアルを確認してください: http ://php .net/manual/zh/book.sockets.php
単純な TCP サーバーの例 phptcpserver.php:
<?php $servsock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 创建一个socket if (FALSE === $servsock) { $errcode = socket_last_error(); fwrite(STDERR, "socket create fail: " . socket_strerror($errcode)); exit(-1); } if (!socket_bind($servsock, '127.0.0.1', 8888)) // 绑定ip地址及端口 { $errcode = socket_last_error(); fwrite(STDERR, "socket bind fail: " . socket_strerror($errcode)); exit(-1); } if (!socket_listen($servsock, 128)) // 允许多少个客户端来排队连接 { $errcode = socket_last_error(); fwrite(STDERR, "socket listen fail: " . socket_strerror($errcode)); exit(-1); } while (1) { $connsock = socket_accept($servsock); //响应客户端连接 if ($connsock) { socket_getpeername($connsock, $addr, $port); //获取连接过来的客户端ip地址和端口 echo "client connect server: ip = $addr, port = $port" . PHP_EOL; while (1) { $data = socket_read($connsock, 1024); //从客户端读取数据 if ($data === '') { //客户端关闭 socket_close($connsock); echo "client close" . PHP_EOL; break; } else { echo 'read from client:' . $data; $data = strtoupper($data); //小写转大写 socket_write($connsock, $data); //回写给客户端 } } } } socket_close($servsock);
このサーバーを起動します:
[root@localhost php]# php phptcpserver.php
その後、サーバーはそこでブロックされ、クライアントが接続するのを待ちます:
[root@localhost ~]# telnet 127.0.0.1 8888 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. ajdjajksdjkaasda AJDJAJKSDJKAASDA 小明哈哈哈哈笑 小明哈哈哈哈笑 小明efsfsdfsdf了哈哈哈 小明EFSFSDFSDF了哈哈哈
サーバーの出力:
[root@localhost php]# php phptcpserver.php client connect server: ip = 127.0.0.1, port = 50398 read from client:ajdjajksdjkaasda read from client:小明哈哈哈哈笑 read from client:小明efsfsdfsdf了哈哈哈
しかし、実際には、この TCP サーバーは一度に 1 つのクライアントの接続とデータ送信しか処理できないためです。接続すると、プロセスはクライアントの読み取りと書き込みを担当します。クライアントがデータを送信しない場合、TCP サーバーは読み取りブロック状態になり、他のクライアントからの接続要求を処理できなくなります。
この問題を解決する 1 つの方法は、クライアントが接続するたびに、サーバーはクライアントとのデータ送信を担当する子プロセスを開き、親プロセスは引き続きそのプロセスを監視します。しかし、このマルチプロセス メカニズムでは、明らかに高い同時実行性をサポートできません。
もう 1 つの解決策は、IO 多重化メカニズムを使用し、PHP が提供するsocket_select メソッドを使用することです。これは、複数のソケットのステータスが、書き込み不可から書き込み可能に、書き込み不可から読み取り不可になど変化した場合に監視できます。読み取り可能である場合、このメソッドは返されるため、ソケットの処理、クライアント接続の処理、読み取りおよび書き込み操作などが可能になります。 PHPドキュメントでsocket_selectの紹介を見てみましょう
socket_select — Runs the select() system call on the given arrays of sockets with a specified timeout 说明 int socket_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] ) socket_select() accepts arrays of sockets and waits for them to change status. Those coming with BSD sockets background will recognize that those socket resource arrays are in fact the so-called file descriptor sets. Three independent arrays of socket resources are watched. You do not need to pass every array to socket_select(). You can leave it out and use an empty array or NULL instead. Also do not forget that those arrays are passed by reference and will be modified after socket_select() returns. 返回值 On success socket_select() returns the number of socket resources contained in the modified arrays, which may be zero if the timeout expires before anything interesting happens. On error FALSE is returned. The error code can be retrieved with socket_last_error().
大まかに翻訳すると:
socket_select --- 指定されたソケット配列のセットに対してselect()システムコールを実行します。特定のタイムアウト。
socket_select() は、いくつかのソケット配列をパラメーターとして受け入れ、状態の変更をリッスンします
これらの BSD スコケットは、これらのソケット リソース配列が実際にはファイル記述子のコレクションであることを認識する機能に基づいています。
3 つの異なるソケット リソース配列が同時に監視されます。
これら 3 つのリソース配列は必須ではありません。関数が返された後、これらの配列の値が参照によって渡されることを忘れないでください。変化すること。
socket_select() は、これら 3 つの配列でステータスが変更されたソケットの合計数を正常に返します。タイムアウトが設定されており、タイムアウト内にステータスの変更がない場合、この関数は 0 を返します。エラーが発生した場合は、この関数は 0 を返します。 FALSE を返します。socket_last_error() を使用してエラー コードを取得します。 T phptcpserver.php コードの前に、socket_select() を使用して最適化します:
<?php $servsock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 创建一个socket if (FALSE === $servsock) { $errcode = socket_last_error(); fwrite(STDERR, "socket create fail: " . socket_strerror($errcode)); exit(-1); } if (!socket_bind($servsock, '127.0.0.1', 8888)) // 绑定ip地址及端口 { $errcode = socket_last_error(); fwrite(STDERR, "socket bind fail: " . socket_strerror($errcode)); exit(-1); } if (!socket_listen($servsock, 128)) // 允许多少个客户端来排队连接 { $errcode = socket_last_error(); fwrite(STDERR, "socket listen fail: " . socket_strerror($errcode)); exit(-1); } /* 要监听的三个sockets数组 */ $read_socks = array(); $write_socks = array(); $except_socks = NULL; // 注意 php 不支持直接将NULL作为引用传参,所以这里定义一个变量 $read_socks[] = $servsock; while (1) { /* 这两个数组会被改变,所以用两个临时变量 */ $tmp_reads = $read_socks; $tmp_writes = $write_socks; // int socket_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] ) $count = socket_select($tmp_reads, $tmp_writes, $except_socks, NULL); // timeout 传 NULL 会一直阻塞直到有结果返回 foreach ($tmp_reads as $read) { if ($read == $servsock) { /* 有新的客户端连接请求 */ $connsock = socket_accept($servsock); //响应客户端连接, 此时不会造成阻塞 if ($connsock) { socket_getpeername($connsock, $addr, $port); //获取远程客户端ip地址和端口 echo "client connect server: ip = $addr, port = $port" . PHP_EOL; // 把新的连接sokcet加入监听 $read_socks[] = $connsock; $write_socks[] = $connsock; } } else { /* 客户端传输数据 */ $data = socket_read($read, 1024); //从客户端读取数据, 此时一定会读到数组而不会产生阻塞 if ($data === '') { //移除对该 socket 监听 foreach ($read_socks as $key => $val) { if ($val == $read) unset($read_socks[$key]); } foreach ($write_socks as $key => $val) { if ($val == $read) unset($write_socks[$key]); } socket_close($read); echo "client close" . PHP_EOL; } else { socket_getpeername($read, $addr, $port); //获取远程客户端ip地址和端口 echo "read from client # $addr:$port # " . $data; $data = strtoupper($data); //小写转大写 if (in_array($read, $tmp_writes)) { //如果该客户端可写 把数据回写给客户端 socket_write($read, $data); } } } } } socket_close($servsock);
これで、この TCP サーバーは複数のクライアントの同時接続をサポートできるようになります。 テスト:
サーバー側:
[root@localhost php]# php phptcpserver.php client connect server: ip = 127.0.0.1, port = 50404 read from client # 127.0.0.1:50404 # hello world client connect server: ip = 127.0.0.1, port = 50406 read from client # 127.0.0.1:50406 # hello PHP read from client # 127.0.0.1:50404 # 少小离家老大回 read from client # 127.0.0.1:50404 # 乡音无改鬓毛衰 read from client # 127.0.0.1:50406 # 老当益壮, read from client # 127.0.0.1:50406 # 宁移白首之心 client close client connect server: ip = 127.0.0.1, port = 50408
上記のサーバーの戻り値を少し変更して、HTTP 応答ヘッダーと単純な HTTP 応答本文を返し、最も単純な HTTP サーバーに変換します:
.... socket_getpeername($read, $addr, $port); //获取远程客户端ip地址和端口 echo "read from client # $addr:$port # " . $data; $response = "HTTP/1.1 200 OK\r\n"; $response .= "Server: phphttpserver\r\n"; $response .= "Content-Type: text/html\r\n"; $response .= "Content-Length: 3\r\n\r\n"; $response .= "ok\n"; if (in_array($read, $tmp_writes)) { //如果该客户端可写 把数据回写给客户端 socket_write($read, $response); socket_close($read); // 主动关闭客户端连接 //移除对该 socket 监听 foreach ($read_socks as $key => $val) { if ($val == $read) unset($read_socks[$key]); } foreach ($write_socks as $key => $val) { if ($val == $read) unset($write_socks[$key]); } } .....
サーバーを再起動し、curl を使用して http サーバーのリクエストをシミュレートします:
[root@localhost ~]# curl '127.0.0.1:8888' ok [root@localhost ~]# curl '127.0.0.1:8888' ok [root@localhost ~]# curl '127.0.0.1:8888' ok [root@localhost ~]# curl '127.0.0.1:8888' ok [root@localhost ~]# curl '127.0.0.1:8888' ok [root@localhost ~]#
サーバー側の出力:
このような同時実行性の高い HTTP サーバーが開発されました。ストレス テスト ソフトウェアを使用して同時実行機能をテストします。
5,000 QPS を超えることに少し興奮していますか?^^。
PHP は世界で最高の言語です、それだけです!
以上がPHPはシステムプログラミングのネットワークソケットとIOの多重化を実現しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PHP 8.4 では、いくつかの新機能、セキュリティの改善、パフォーマンスの改善が行われ、かなりの量の機能の非推奨と削除が行われています。 このガイドでは、Ubuntu、Debian、またはその派生版に PHP 8.4 をインストールする方法、または PHP 8.4 にアップグレードする方法について説明します。

Visual Studio Code (VS Code とも呼ばれる) は、すべての主要なオペレーティング システムで利用できる無料のソース コード エディター (統合開発環境 (IDE)) です。 多くのプログラミング言語の拡張機能の大規模なコレクションを備えた VS Code は、

このチュートリアルでは、PHPを使用してXMLドキュメントを効率的に処理する方法を示しています。 XML(拡張可能なマークアップ言語)は、人間の読みやすさとマシン解析の両方に合わせて設計された多用途のテキストベースのマークアップ言語です。一般的にデータストレージに使用されます

文字列は、文字、数字、シンボルを含む一連の文字です。このチュートリアルでは、さまざまな方法を使用してPHPの特定の文字列内の母音の数を計算する方法を学びます。英語の母音は、a、e、i、o、u、そしてそれらは大文字または小文字である可能性があります。 母音とは何ですか? 母音は、特定の発音を表すアルファベットのある文字です。大文字と小文字など、英語には5つの母音があります。 a、e、i、o、u 例1 入力:string = "tutorialspoint" 出力:6 説明する 文字列「TutorialSpoint」の母音は、u、o、i、a、o、iです。合計で6元があります

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

あなたが経験豊富な PHP 開発者であれば、すでにそこにいて、すでにそれを行っていると感じているかもしれません。あなたは、運用を達成するために、かなりの数のアプリケーションを開発し、数百万行のコードをデバッグし、大量のスクリプトを微調整してきました。

静的結合(静的::) PHPで後期静的結合(LSB)を実装し、クラスを定義するのではなく、静的コンテキストで呼び出しクラスを参照できるようにします。 1)解析プロセスは実行時に実行されます。2)継承関係のコールクラスを検索します。3)パフォーマンスオーバーヘッドをもたらす可能性があります。

PHPの魔法の方法は何ですか? PHPの魔法の方法には次のものが含まれます。1。\ _ \ _コンストラクト、オブジェクトの初期化に使用されます。 2。\ _ \ _リソースのクリーンアップに使用される破壊。 3。\ _ \ _呼び出し、存在しないメソッド呼び出しを処理します。 4。\ _ \ _ get、dynamic属性アクセスを実装します。 5。\ _ \ _セット、動的属性設定を実装します。これらの方法は、特定の状況で自動的に呼び出され、コードの柔軟性と効率を向上させます。
