PHP network socket and IO multiplexing for system programming

Source: Internet
Author: User
This article to share the content is about PHP implementation of System programming network socket and IO multiplexing, has a certain reference value, the need for friends can refer to

All along, PHP is rarely used for socket programming, after all, is a scripting language, efficiency will become a big bottleneck, but can not say that PHP can not be used for socket programming, and can not say that PHP socket programming performance is very low, such as a well-known PHP socket framework Workerman is the use of pure PHP development, and claims to have excellent performance, so in some circumstances, PHP socket programming may also be a skill.

PHP provides a series of methods similar to the C language socket library for us to invoke:

Socket_accept-accepts a connection on a socketsocket_bind-to the socket binding name socket_clear_error-clears the socket or error on the last error code Socket_close  -Close socket Resource socket_cmsg_space-calculate message buffer sizesocket_connect-Open a socket connection Socket_create_listen-opens a socket On port to accept connectionssocket_create_pair-creates a pair of indistinguishable sockets and stores them in an array socket_create-Create a socket (communication node) socket_get_option-gets socket options for the socketsocket_getopt-alias Socket_get_optionsoc  Ket_getpeername-queries the remote side of the given socket which may either result in Host/port or in a Unix filesystem Path, dependent on it typesocket_getsockname-queries the local side of the given socket which may either result in hos T/port or in a Unix filesystem path, dependent on its typesocket_import_stream-import a Streamsocket_last_error-return s the last error in the Socketsocket_listen-listens for a connection on a socketsocket_read-reads a maximum of length Bytes from a socketsocket_recv-receiving data from a connected socket socket_recvfrom-receives the whether or not it is Connection-orientedsocket_recvms G-read a Messagesocket_select-runs the Select () system call on the given arrays of sockets with a specified TIMEOUTSOC Ket_send-sends data to a connected socketsocket_sendmsg-send a messagesocket_sendto-sends a message to a socket, whe Ther it is connected or notsocket_set_block-sets blocking mode on a socket resourcesocket_set_nonblock-sets Nonblockin G mode for file descriptor fdsocket_set_option-sets socket options for the socketsocket_setopt-alias Socket_set_optionsoc Ket_shutdown-shuts down a socket for receiving, sending, or bothsocket_strerror-return a string describing a socket ER Rorsocket_write-write to a socket


For more details, see PHP's official manual for sockets: http://php.net/manual/zh/book.sockets.php


A simple example of a TCP server phptcpserver.php:

<?php$servsock = Socket_create (Af_inet, Sock_stream, sol_tcp);    Create a socketif (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))//Bind IP address and port {$errcode = Socket_last_error (); Fwrite (STDERR, "socket bind fail:".    Socket_strerror ($errcode)); Exit (-1);}    if (!socket_listen ($servsock, 128))//number of clients allowed to queue the connection {$errcode = Socket_last_error (); Fwrite (STDERR, "Socket Listen fail:".    Socket_strerror ($errcode)); Exit (-1);}  while (1) {$connsock = Socket_accept ($servsock);  Response Client Connection if ($connsock) {socket_getpeername ($connsock, $addr, $port); Get the client IP address and port that is connected to echo "Client connect Server:ip = $addr, port = $port".        Php_eol;  while (1) {$data = Socket_read ($connsock, 1024); Read data from client if ($data = = =) {//Client off Socket_close ($coNnsock); echo "Client Close".                Php_eol;            Break                } else {echo ' read from client: '. $data;  $data = Strtoupper ($data);  lowercase to uppercase Socket_write ($connsock, $data); Write back to the client}}}}socket_close ($servsock);


Start this server:


[Root@localhost php]# php phptcpserver.php


After this server has been blocked there, waiting for the client to connect, we can use the Telnet command to connect to this server:


[root@localhost ~]# telnet 127.0.0.1 8888Trying 127.0.0.1...Connected to 127.0.0.1.Escape character is ' ^] '. Ajdjajksdjkaasdaajdjajksdjkaasda Xiao-ming haha haha laugh xiao ming haha haha laugh xiao Ming efsfsdfsdf haha Ha Xiaomin efsfsdfsdf ha haha


Server-Side output:


[Root@localhost php]# php phptcpserver.php client Connect Server:ip = 127.0.0.1, port = 50398read from CLIENT:AJDJAJKSDJK Aasdaread from client: Xiao Ming haha lol read from client: Xiao Ming efsfsdfsdf ha ha haha


But in fact, this TCP server is problematic, it can only handle a client's connection and data transmission at a time, because after a client connects, the process is responsible for reading and writing client data, when the client does not transmit data, the TCP server is in a blocking read state, can no longer handle the connection request of other clients.


One way to solve this problem is to use a multi-process server, whenever a client connects, the server is dedicated to a child process and the client's data transmission, and the parent process is still listening to the client connection, but the cost of the process is expensive, this multi-process mechanism obviously does not support high concurrency.

Another solution is to use the IO multiplexing mechanism, which uses the Socket_select method provided by PHP to listen to multiple sockets, and if one of the socket states changes, such as from non-writable to writable and never readable to readable, this method returns So we can deal with this socket, handle client connections, read and write operations, and so on. See the introduction to the Socket_select in the PHP documentation

Socket_select-runs the Select () system call on the given arrays of sockets with a specified timeout description int socket_select ( Array & $read, array & $write, array & $except, int $tv _sec [, int $tv _usec = 0]) socket_select () accepts AR Rays of sockets and waits for them to change status. Those coming with BSD sockets background would recognize that those socket resource arrays is in fact the so-called file D Escriptor sets. Three independent arrays of socket resources is watched. You don't need to pass the every array to Socket_select (). You can leave it out and use an empty array or NULL instead. Also does not forget that those arrays is passed by reference and would be modified after Socket_select () returns. return value on SUCC ESS Socket_select () returns the number of the socket resources contained in the modified arrays, which could be zero if the time Out expires before anything interesting happens. On Error FALSE is returned. The error code can is retrieved with Socket_last_error ().


Under the general translation:

Socket_select---Executes a select () system call on a given set of sockets arrays, with a specific time-out.

Socket_select () accepts several sets of sockets arrays as parameters and listens for them to change state

These socket resource arrays, based on the BSD scokets, are actually a collection of file descriptors.

Three different arrays of socket resources are monitored simultaneously.

These three resource arrays are not mandatory, you can use an empty array or null as the parameter, do not forget that the three arrays are passed by reference, after the function returns, the values of these arrays will be changed.

The Socket_select () call successfully returns the total number of sockets in these three arrays, if timeout is set and there is no state change within timeout, this function returns 0, false on error, can be used Socket_last_ Error () Gets the fault code.



Before using Socket_select () Optimization phptcpserver.php code:

<?php$servsock = Socket_create (Af_inet, Sock_stream, sol_tcp);    Create a socketif (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))//Bind IP address and port {$errcode = Socket_last_error (); Fwrite (STDERR, "socket bind fail:".    Socket_strerror ($errcode)); Exit (-1);}    if (!socket_listen ($servsock, 128))//number of clients allowed to queue the connection {$errcode = Socket_last_error (); Fwrite (STDERR, "Socket Listen fail:".    Socket_strerror ($errcode)); Exit (-1);}  /* Three sockets array to listen for */$read _socks = Array (), $write _socks = Array (); $except _socks = NULL; Note that PHP does not support direct null as a reference, so define a variable $read_socks[] = $servsock; while (1) {/* These two arrays are changed, so use two temporary variables */$tmp _reads = $re    Ad_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 will block until the result returns foreach ($tmp _reads as $read) {if ($read = = $servsock) {/*  There is a new client connection request */$connsock = socket_accept ($servsock);  In response to a client connection, this does not cause blocking if ($connsock) {socket_getpeername ($connsock, $addr, $port); Get the remote client IP address and port echo "Client Connect Server:ip = $addr, port = $port".                Php_eol;                Add new connection Sokcet to monitor $read _socks[] = $connsock;            $write _socks[] = $connsock;  }} else {/* client transmits data */$data = Socket_read ($read, 1024); Reads the data from the client, at which point it is bound to read the array without blocking if ($data = = =) {//Remove the socket listener fore                Ach ($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); Get the remote client IP address and port echo "read from client # $addr: $port #".                $data;  $data = Strtoupper ($data);                    lowercase to uppercase if (In_array ($read, $tmp _writes)) {//If the client can write data back to the client                Socket_write ($read, $data); }}}}}socket_close ($servsock);



Now, this TCP server can support multiple clients connected at the same time, under test:


Server-side:


[Root@localhost php]# php phptcpserver.php client Connect Server:ip = 127.0.0.1, port = 50404read from client # 127.0.0.1 : 50404 # Hello worldclient Connect server:ip = 127.0.0.1, port = 50406read from client # 127.0.0.1:50406 # Hello Phpread From client # 127.0.0.1:50404 # Little Less home boss back read from client # 127.0.0.1:50404 # accent No change mane decay read from client # 127.0.0.1:50406 # old Yi Zhuang, read from client # 127.0.0.1:50406 # ning moved heart client closeclient Connect Server:ip = 127.0.0.1, port = 50408


Slightly modify the above server to return, return an HTTP response header and a simple HTTP response body, and then become one of the simplest HTTP server:




..... socket_getpeername ($read, $addr, $port); Get the remote client IP address and port echo "read from client # $addr: $port #".                $data;                $response = "http/1.1 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)) {//If the client can write the data back to the client Socket_write                    ($read, $response);  Socket_close ($read);                    Active shutdown Client Connection//Remove to the Socket listener foreach ($read _socks as $key + = $val)                    {if ($val = = $read) unset ($read _socks[$key]); } foreach ($write _socks as $key = + $val) {if ($val = = $re                    AD) unset ($write _socks[$key]);         }       }..... 





Restart the server and request the HTTP server with Curl impersonation:


[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 ~]#


Server-Side output:


Client Connect Server:ip = 127.0.0.1, port = 50450read from client # 127.0.0.1:50450 # get/http/1.1user-agent:curl/7.1 9.7 (X86_64-REDHAT-LINUX-GNU) libcurl/7.19.7 nss/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2host:  127.0.0.1:8888accept: */*client closeclient Connect Server:ip = 127.0.0.1, port = 50452read from client # 127.0.0.1:50452 # get/http/1.1user-agent:curl/7.19.7 (X86_64-REDHAT-LINUX-GNU) libcurl/7.19.7 nss/3.27.1 zlib/1.2.3 libidn/1.18 LIBSS H2/1.4.2host:127.0.0.1:8888accept: */*client closeclient Connect Server:ip = 127.0.0.1, port = 50454read from client # 1 27.0.0.1:50454 # get/http/1.1user-agent:curl/7.19.7 (X86_64-REDHAT-LINUX-GNU) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2host:127.0.0.1:8888accept: */*client closeclient Connect Server:ip = 127.0.0.1, port = 50456rea D from client # 127.0.0.1:50456 # get/http/1.1user-agent:curl/7.19.7 (X86_64-REDHAT-LINUX-GNU) libcurl/7.19.7 NSS/3.27. 1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2host:127.0.0.1: 8888Accept: */*client Close 



Such a high-concurrency HTTP server is developed, using the test software under the testing of concurrency capability:




See up to 5,000 more QPS, there is no small excitement about ^ ^.

PHP is the best language in the world that's all!



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.