Dieser Artikel gibt Ihnen einen ersten Überblick über PHP-Sockets, indem wir einen einfachen Socket-Server erstellen.
Der chinesische Name für Socket heißt Socket. So etwas ist die „Kapselung“ von TCP/IP. In Wirklichkeit besteht das Netzwerk nur aus vier Schichten: Von oben nach unten sind es die Anwendungsschicht, die Transportschicht, die Netzwerkschicht und die Datenverbindungsschicht. Das am häufigsten verwendete HTTP-Protokoll ist ein Protokoll der Anwendungsschicht, und Socket kann einfach und grob als Teil der Transportschicht verstanden werden. Wenn es immer noch schwer zu verstehen ist, fügen Sie tcp://218.221.11.23:9999 grober hinzu. Sehen Sie es? Dies ist ein TCP-Socket.
Socket gibt uns die Möglichkeit, die Transportschicht und die Netzwerkschicht zu steuern und so eine stärkere Leistung und höhere Effizienz zu erzielen. Socket-Programmierung ist die am häufigsten verwendete und ausgereifte Lösung zur Lösung von Netzwerkservern mit hoher Parallelität. Jeder Serverprogrammierer sollte Kenntnisse im Zusammenhang mit der Socket-Programmierung beherrschen.
In PHP gibt es zwei Funktionssätze, die Sockets steuern können. Einer ist die Funktionsreihe socket_ und der andere ist die Funktionsreihe stream_. Socket_ ist eine Implementierung, die PHP erhält, indem es den Socket in der C-Sprache direkt kopiert, während das Stream_-System PHP verwendet, um ihn mithilfe des Stream-Konzepts zu kapseln. Im Folgenden werden die Funktionen der socket_*-Serie verwendet, um diese Artikelserie einfach zu starten.
Erstellen Sie zunächst den einfachsten Socket-Server:
<?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 );
Speichern Sie die Datei als server.php und führen Sie dann php server.php aus, um sie auszuführen. Wir können Telnet auf dem Client verwenden, ein anderes Terminal öffnen, Telnet 127.0.0.1 9999 ausführen und die Eingabetaste drücken. Die laufenden Ergebnisse lauten wie folgt:
Eine kurze Analyse des obigen Codes zur Veranschaulichung des Prozesses des TCP-Socket-Servers:
Im obigen Fall gibt es zwei große Mängel:
Nachdem ich die oben genannten Probleme analysiert habe, dachte ich an den zuvor erwähnten Multiprozess. Dann können wir einen untergeordneten Prozess forken, um die Anfrage des Kunden zu bearbeiten, sodass wir nach dem Akzeptieren des zweiten Clients einen untergeordneten Prozess forken können Wäre der Prozess zur Bearbeitung der Anfrage des zweiten Kunden nicht gelöst? OK! Lassen Sie uns den Code demonstrieren:
<?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 );
Speichern Sie den Code als server.php und führen Sie dann php server.php aus. Der Client verwendet weiterhin Telnet 127.0.0.1 9999, aber dieses Mal öffnen wir zwei Terminals, um Telnet auszuführen. Es ist wichtig zu beachten, dass nach der Verbindung des ersten Clients auch der zweite Client eine Verbindung herstellen kann. Die laufenden Ergebnisse lauten wie folgt:
Anhand des Zeitstempels der Client-Anfrage können Sie erkennen, dass der Server nun N Clients gleichzeitig bedienen kann. Aber denken Sie mal darüber nach: Was wäre, wenn 10.000 Kunden kommen würden, um eine Anfrage zu stellen? Zu diesem Zeitpunkt wird der Server 10.000 untergeordnete Prozesse bereitstellen, um jede Client-Verbindung zu verarbeiten, wodurch Menschen getötet werden. Fork selbst ist ein Systemaufruf, der Systemressourcen verschwendet. Selbst wenn das System 1.000 Forks aushalten kann, reichen die 1.000 untergeordneten Prozesse, die aus dem Fork kommen, aus, um einen Topf Systemspeicher zu verschwenden . Ist es am Ende in Ordnung? Der untergeordnete Prozess, der leicht abgezweigt werden kann, wird nach der Verarbeitung des aktuellen Clients geschlossen und muss für die nächste Anfrage erneut abgezweigt werden. Dies ist an sich eine Verschwendung und entspricht nicht den Anforderungen zu den vorherrschenden sozialistischen Werten. Bei einem böswilligen Angriff erhöht sich die Anzahl der Systemforks linear, bis das System abstürzt.
Daher schlagen wir noch einmal eine verbesserte Lösung vor. Wir können das Geschäftsvolumen abschätzen und dann beim Start des Dienstes eine feste Anzahl von untergeordneten Prozessen aufteilen. Jeder untergeordnete Prozess befindet sich in einer Endlosschleife und wird bei Annahme blockiert abgeschlossen ist, wird die Verbindung nur geschlossen, aber nicht zerstört, sondern wartet weiterhin auf die nächste Client-Anfrage. Auf diese Weise wird nicht nur die enorme Ressourcenverschwendung durch wiederholte Verzweigungen des Prozesses vermieden, sondern auch das System vor einem Absturz aufgrund unendlicher Verzweigungen durch eine feste Anzahl von untergeordneten Prozessen geschützt.
<?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 );
Speichern Sie die Datei als server.php und führen Sie php server.php aus. Verwenden Sie dann ps -ef grep phpserver |, um den Serverprozessstatus zu überprüfen:
Sie können sehen, dass der Masterprozess vorhanden ist. Darüber hinaus warten 10 Unterprozesse auf den Service. Er kann 10 Clients gleichzeitig bedienen. Versuchen wir es über Telnet 127.0.0.1 9999. Das laufende Ergebnis ist wie folgt:
Okay, die neue Reiseserie von PHP beginnt mit einer einfachen Einführung! Im nächsten Artikel werden einige tiefergehende theoretische Grundlagen beschrieben.
Empfohlenes Lernen: „PHP-Video-Tutorial“
Das obige ist der detaillierte Inhalt vonPHP-Socket-Lernen: Hier erfahren Sie, wie Sie einen einfachen Socket-Server erstellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!