The content shared with you in this article is about network Socket and IO multiplexing of system programming in PHP. It has certain reference value. Friends in need can refer to it
Always Since then, PHP has rarely been used for socket programming. After all, it is a scripting language, and efficiency will become a big bottleneck. However, it cannot be said that PHP cannot be used for socket programming, nor can it be said that PHP's socket programming performance is so low. For example, Workerman, a well-known PHP socket framework, is developed in pure PHP and claims to have excellent performance, so in some environments, PHP socket programming may also be able to show off its skills.
PHP provides a series of methods similar to those in the C language socket library for us to call:
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
For more details, please check PHP’s official manual on sockets: http://php.net/manual/zh/book.sockets.php
A simple TCP server example 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);
Start this server:
##
[root@localhost php]# php phptcpserver.php
After this The server has been blocked there, waiting for the client to connect. We can use the telnet command to connect to the server:
##[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了哈哈哈
One way to solve this problem is to use a multi-process server. Whenever a client connects, the server opens a child process specifically responsible for communicating with the client. The client's data is transmitted, and the parent process still monitors the client's connection, but the cost of starting the process is expensive. This multi-process mechanism obviously cannot support high concurrency.
Another solution is to use the IO multiplexing mechanism and use the socket_select method provided by PHP. It can monitor multiple sockets. If the status of one of the sockets changes, , such as from unwritable to writable, from unreadable to readable, this method will return, so that we can process the socket, handle client connections, read and write operations, etc. Let’s look at the introduction to socket_select in the php documentation
##
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().
##roughly translated:
socket_select --- Execute the select() system call on the given socket arrays, using a specific timeout.
socket_select() accepts several socket arrays as parameters and listens for them to change state
These are based on BSD scokets and can identify these socket resource arrays In fact, it is a collection of file descriptors.
Three different socket resource arrays will be monitored at the same time.
These three resource arrays are not required. You can use an empty array or NULL as a parameter. Don’t forget that these three arrays are passed by reference. When the function returns Later, the values of these arrays will be changed.
socket_select() The successful call returns the total number of sockets whose status has changed in these three arrays. If timeout is set and there is no status change within the timeout, this function will return 0. Returns FALSE when an error occurs. You can use socket_last_error() to obtain the error code.
##Use socket_select() to optimize phptcpserver.php code:
<?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);
Now, this TCP server can support multiple clients connecting at the same time. Test:
Server side:
[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
Slightly modify the above server return to return an HTTP response header and a simple HTTP response body, thus turning it into the simplest HTTP server :
##
.... 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]); } } .....
Restart the server and use curl to simulate a request to the http server:
##
[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 = 50450 read from client # 127.0.0.1:50450 # GET / HTTP/1.1 User-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.2 Host: 127.0.0.1:8888 Accept: */* client close client connect server: ip = 127.0.0.1, port = 50452 read from client # 127.0.0.1:50452 # GET / HTTP/1.1 User-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.2 Host: 127.0.0.1:8888 Accept: */* client close client connect server: ip = 127.0.0.1, port = 50454 read from client # 127.0.0.1:50454 # GET / HTTP/1.1 User-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.2 Host: 127.0.0.1:8888 Accept: */* client close client connect server: ip = 127.0.0.1, port = 50456 read from client # 127.0.0.1:50456 # GET / HTTP/1.1 User-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.2 Host: 127.0.0.1:8888 Accept: */* client close
Such a high-concurrency HTTP server has been developed. Use stress testing software to test the concurrency capability:
Are you excited to see more than 5,000 QPS?^^.
PHP is the best language in the world that's all !
The above is the detailed content of PHP realizes network Socket and IO multiplexing of system programming. For more information, please follow other related articles on the PHP Chinese website!