This article to share the content is about PHP network programming of the Accept blocking model introduction, the content is very detailed, the need for friends can refer to, hope can help everyone.
The Accept blocking model is a relatively ancient model, but it contains a lot of interesting knowledge, such as blocking/non-blocking, lock, timeout retransmission ...
Service-Side Program acceptsever.php
<?phpset_time_limit (0); # set script Execution Time Unlimited class Socketserver {private static $socket; function Socketserver ($port) {global $errno, $errstr; if ($port < 1024x768) {die ("Port must is a number which bigger than 1024/n"); } $socket = Stream_socket_server ("tcp://0.0.0.0:{$port}", $errno, $ERRSTR); if (! $socket) Die ("$errstr ($errno)"); while ($conn = Stream_socket_accept ($socket,-1)) {//This setting does not time out only with static $id = 0; # process ID static $CT = 0; # The length of the received data $ct _last = $CT; $ct _data = "; # accepted data $buffer = '; # segment Read Data $id + +; echo "Client $id Come". Php_eol; # Continuous listening while (!preg_match (' {/r/n} ', $buffer)) {//not read to Terminator, continue reading//if (feof ($conn)) break;//Prevent P Open and fread bugs result in a dead loop $buffer = Fread ($conn, 1024); Echo ' R '. Php_eol; # Number of print reads $ct + = strlen ($buffer); $ct _data. = Preg_replace (' {/r/n} ', ' ', $buffer); } $ct _size = ($ct-$ct _last) * 8; echo "[$id]". __method__. ">". $ct _data. Php_eol; Fwrite ($conn, "Received $ct _size byte data./r/n"); Fclose ($conn); } fclose ($socket); }}new Socketserver (2000);
client program acceptclient.php
<?php# Log record function debug ($msg) {error_log ($msg, 3, './socket.log ');} if ($argv [1]) {$socket _client = stream_socket_client (' tcp://0.0.0.0:2000 ', $errno, $errstr, 30); /* Set script to non-blocking */# stream_set_blocking ($socket _client, 0); /* Set the script timeout time */# stream_set_timeout ($socket _client, 0, 100000); if (! $socket _client) {die ("$errstr ($errno)"); } else {# fill container $msg = Trim ($argv [1]); for ($i = 0; $i < $i + +) {$res = fwrite ($socket _client, "$msg ($i)"); Usleep (100000); Echo ' W '; Number of print writes # debug (fread ($socket _client, 1024)); A deadlock will occur because Fread will wait for fwrite ($socket _client, "/r/n") when the data is not read in blocking mode; Transfer Terminator # Log debug (Fread ($socket _client, 1024)); Fclose ($socket _client); }}else {//$PHARR = Array ();/for ($i = 0; $i < $i + +) {//$PHARR [$i] = Popen ("php". __file__. " ' {$i}:test ' ", ' r ');//}//foreach ($phArr as $ph) {//Pclose ($PH);/} for ($i = 0; $i <; $i + +) {System ("PHP". __file__. " ' {$i}:test '); # this equals PHP "current file" "Script parameter"}}
Code parsing
First of all, explain the above code logic: the client acceptclient.php loop send the data, the last send Terminator, the server accept server.php use the Accept blocking method to receive the socket connection, and then loop to receive the data until the Terminator is received, Returns the result data (bytes received) The client receives data returned by the server and writes to the log. Although the logic is simple, there are several things that are worth analyzing:
A> by default, run PHP socket_client.php test, the client hit 10 W, the service side of the output of a number of R is received data, Socket.log record the service side returned by the received results data, the effect is as follows:
The situation is easy to understand and not to repeat. Then, using the Telnet command to open more than one client at a time, you will notice that the server processes only one client at a time:
Others need to be "queued" at the back, which is the feature of blocking IO, a pattern that is clearly vulnerable and extremely inefficient.
B> only open the comment code of the 29th line of socket_client.php, run the PHP socket_client.php test client again to hit a W, the server also played a R, then two programs are stuck. This is why, after parsing the logic you will find that this is because the client is going to return data to the server before the Terminator is sent, and the server is going to end up with a terminator to the client because it does not receive the Terminator, causing a deadlock . Only one W and R are typed because Fread is blocked by default. To resolve this deadlock, you must open the comment code for the 17th line of socket_client.php, set a 0.1-second timeout for the socket, run again and you will see that a W and R appear normally after 0.1 seconds, and the received data returned by the server is are often recorded. Visible fread Default is blocked, we have to pay special attention when programming, if not set timeout, it is easy to deadlock.
c> only open 14 lines comment set script to be non-blocking, run PHP socket_client.php test, the results are basically the same as the case A, the only difference is that Socket.log does not record the return data, This is because when we do not block the client does not have to wait to receive the server response results can continue to execute, when the execution to debug, read the data is empty, so socket.log is also empty. Here you can see the difference between the client running in blocking and nonblocking mode, and of course the non-blocking mode can be used to get maximum efficiency when the client does not care about accepting the result.
D> running PHP socket_client.php is running 10 times the above logic, this is not a problem, but it is very strange that if you use 39-45 lines of code, with popen simultaneously open 10 processes to run, it will cause the server side of the dead loop, very strange! Later, the investigation found that as long as the connection created by the process opened with Popen will cause fread or socket_read error directly return empty string, resulting in a dead loop, after reviewing the PHP source code found that PHP popen and fread function is completely not C-native, A large number of php_stream_* implementation logic is inserted, the initial estimate is that one of the bugs caused by the Socket connection interruption caused by the solution is to open the socket_server.php 33 lines of code, if the connection is interrupted to jump out of the loop, However, there will be a lot of data loss, this problem needs special attention!