Cet article vous apporte des connaissances pertinentes sur php+socket, qui présente principalement le multiplexage IO et comment php+socket implémente le serveur Web ? Les amis intéressés peuvent jeter un œil ci-dessous. J'espère que cela sera utile à tout le monde.
multiplexage
Article précédent Une communication serveur-client simple est obtenue via des sockets natifs, mais lorsqu'il y a plusieurs clients Lorsque le client est connecté, le serveur ne peut traiter que la demande du premier client, mais ne peut pas servir les clients suivants. La raison de cette situation est que le modèle IO est bloquant et qu'un seul client peut être utilisé en même temps. Pour y accéder, il existe deux solutions principales. résoudre ce problème :
Multi-processus, c'est-à-dire démarrer plusieurs processus sur le serveur pour surveillerselect
et epoll
. Dans les logiciels courants, Apache
utilise le modèle select
. , et nginx
utilise le modèle epoll
. Il existe un modèle select
intégré à PHP, et la fonction correspondante est socket_select
. Le multiplexage est la base de l'implémentation d'un serveur http
socket_select
intégrée pour implémenter le modèle select
. Sa syntaxe est la suivante : socket_select( array &$read, array &$write, array &$except, int $seconds [, int $microseconds = 0]): int|false
readselect
与 epoll
,常见的软件中,Apache
使用了 select
模型,nginx
则使用 epoll
模型。在 php 中内置了 select
模型,对应的函数为 socket_select
,多路复用是实现 http 服务器的基础
在前文中我们介绍了 php 原生 socket 内置了 socket_select
函数实现了 select
模型,其语法如下:
<?php // 创建套接字 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 设置 ip 被释放后立即可使用 socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true); // 绑定ip与端口 socket_bind($socket, 0, 8888); // 开始监听 socket_listen($socket); $sockets[] = $socket; while (true) { $tmp_sockets = $sockets; socket_select($tmp_sockets, $write, $except, null); foreach ($tmp_sockets as $sock) { // 如果当前套接字等于 socket_create 创建的套接字,说明是有新的连接或有新的断开连接 if ($sock == $socket) { $conn_sock = socket_accept($socket); $sockets[] = $conn_sock; socket_getpeername($conn_sock, $ip, $port); echo '请求ip: ' . $ip . '端口: ' . $port . PHP_EOL; } else { // 否则说明是之前连接的客户端发来消息 $msg = socket_read($sock, 10240); socket_write($sock, strtoupper($msg)); echo $msg; } } }
read
服务端监听的套接字资源,当他有变化(即收到新的消息或有客户端连接、断开)时,socket_select
函数才会返回(否则继续阻塞),同时修改该变量为当前发生事件(收到消息或有客户端连接、断开)的套接字资源列表,并继续向下执行。
write
监听是否有客户端写数据,传入 null
则代表不关心是否有写变化
except
套接字内要排除的元素,传入 null
是 「监听」 全部
seconds
秒和微秒一起构成超时参数。如果传入 null
则会阻塞,为 0 非阻塞,如果是 >0 则为最大阻塞时间
microseconds
我们在 上篇文章 简单实现了 socket 服务端监听与客户端的连接,接下来我们在服务端监听代码的基础上通过多路复用优化代码:
<?php // 创建套接字 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 设置 ip 被释放后立即可使用 socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true); // 绑定ip与端口 socket_bind($socket, 0, 8888); // 开始监听 socket_listen($socket); $sockets[] = $socket; while (true) { $tmp_sockets = $sockets; socket_select($tmp_sockets, $write, $except, null); foreach ($tmp_sockets as $sock) { if ($sock == $socket) { $conn_sock = socket_accept($socket); $sockets[] = $conn_sock; } else { $msg = socket_read($sock, 10240); var_dump($msg); if ($msg == '') return; $output = '<h1>this is php worker</h1>'; $len = strlen($output); $response = "HTTP/1.1 200 OK\r\n"; $response .= "content-type: text/html\r\n"; $response .= "server: php socket\r\n"; $response .= "Content-Length: {$len}\r\n\r\n"; $response .= $output; socket_write($sock, $response); } } }
在本示例中 socket_select
函数会阻塞当前进程,当 $tmp_sockets
数组内的 socket 资源有新的客户端连接或断开或收到新消息时,会将 $tmp_sockets
数组修改为当前活跃的 socket 资源,随后通过遍历该数组处理业务逻辑
使用socket实现简易http服务器
http 协议是在 socket 的基础上规定了指定的数据格式,所以我们只需在 socket_write
时按照格式发送数据,浏览器就可正常响应请求
GET / HTTP/1.1 Host: 124.222.**.**:8888 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: jenkins-timestamper-offset=-28800000; _ga=GA1.1.1403944751.1652010033; _ga_2GM6102E19=GS1.1.1652802985.7.1.1652803014.0
在服务端运行此示例,随后在浏览器访问 ip:8888
,可以看到如下:
同时服务端会输出如下内容:
rrreee该内容即为用户端请求原始数据,可解析此数据并根据请求做出响应,比如使用 file_get_content
socket_select. La fonction
peut revenir (sinon elle continuera à bloquer), et en même temps modifier la variable dans la liste des ressources de socket de l'événement en cours (message reçu ou connexion ou déconnexion client), et continuer à s'exécuter vers le bas . Surveillez si le client écrit des données. En passant null
signifie que vous ne vous souciez pas de savoir s'il y a des modifications en écriture
. sauf code>🎜🎜Les éléments à exclure dans le socket, passer <code>null
c'est "écouter" Toutes les 🎜🎜🎜🎜secondes
🎜🎜secondes et microsecondes ensemble constituent le paramètre timeout . Si null
est transmis, il bloquera, et s'il vaut 0, il sera non bloquant. S'il vaut >0, ce sera le temps de blocage maximum🎜🎜🎜🎜socket_select
La fonction bloquera le processus en cours. Lorsque $tmp_sockets a une nouvelle connexion ou déconnexion client ou reçoit un nouveau message, le tableau $tmp_sockets
être modifié en ressource de socket actuellement active, puis traiter la logique métier en parcourant le tableau🎜🎜🎜 🎜🎜Utilisez socket pour implémenter un simple serveur http🎜🎜🎜Le protocole http spécifie le format de données spécifié en fonction du socket, nous n'avons donc besoin d'envoyer des données qu'en fonction du format lorsque socket_write
, et le navigateur fonctionnera normalement. En réponse à la requête 🎜rrreee🎜Exécutez cet exemple sur le serveur, puis accédez à ip:8888
dans le navigateur, vous pouvez voir ce qui suit : 🎜🎜🎜 🎜 En même temps, le serveur affichera le contenu suivant : 🎜rrreee 🎜Ce contenu est la demande du client Données brutes, vous pouvez analyser ces données et répondre en fonction de la demande, par exemple en utilisant file_get_content
pour lire le contenu du fichier spécifié et renvoyez-le au navigateur.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!