이 기사는 주로 IO 멀티플렉싱을 소개하는 php+socket에 대한 관련 지식과 php+socket이 웹 서버를 구현하는 방법을 제공합니다. 관심 있는 친구들은 아래를 살펴보시면 모두에게 도움이 되길 바랍니다.
multiplexing
이전글 간단한 서버-클라이언트 통신은 네이티브 소켓을 통해 이루어지지만, 클라이언트가 여러 개인 경우 클라이언트가 연결되면 서버는 첫 번째 클라이언트의 요청만 처리할 수 있지만 후속 클라이언트에 서비스를 제공할 수 없는 이유는 IO 모델이 차단되고 동시에 하나의 클라이언트만 사용할 수 있기 때문입니다. 액세스하려면 두 가지 주요 솔루션이 있습니다. 이 문제 해결:
다중 프로세스, 즉 서버에서 여러 프로세스를 시작하여 모니터링select
와 epoll
이라는 두 가지 모델로 나뉩니다. 일반적인 소프트웨어에서 Apache
는 select
모델을 사용합니다. , nginx
는 epoll
모델을 사용합니다. PHP에는 select
모델이 내장되어 있으며 해당 함수는 socket_select
입니다. 멀티플렉싱은 먼저 http 서버
select
모델을 구현하는 내장 socket_select
함수가 있음을 소개했습니다. 해당 구문은 다음과 같습니다. 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
소켓_선택
함수는 반환할 것이며(그렇지 않으면 계속 차단됩니다) 동시에 현재 이벤트(수신된 메시지 또는 클라이언트 연결 또는 연결 끊김)의 소켓 리소스 목록에 대한 변수를 수정하고 계속 실행합니다. 하향의. 클라이언트가 데이터를 쓰는지 여부를 모니터링합니다. null
을 전달하면 쓰기 변경 사항이 있는지 상관하지 않는다는 뜻입니다
Except code>🎜🎜 소켓에서 제외될 요소, <code>null
전달은 "수신"입니다. 모두 🎜🎜🎜🎜초
🎜🎜초와 마이크로초를 함께 구성합니다. 시간 초과 매개변수. null
이 전달되면 차단되고, 0이면 차단되지 않습니다.>0이면 최대 차단 시간이 됩니다🎜🎜🎜🎜마이크로초
🎜🎜🎜socket_select
함수는 $tmp_sockets
배열의 소켓 리소스에 새로운 클라이언트 연결 또는 연결 끊김이 있거나 새 메시지를 수신할 때 $tmp_sockets
배열을 차단합니다. 현재 활성 소켓 리소스로 수정한 다음 배열을 순회하여 비즈니스 로직을 처리합니다🎜🎜socket_write
, 그러면 브라우저가 정상적으로 작동합니다. 🎜rrreee🎜 요청에 대한 응답으로 이 예제를 서버에서 실행한 후 브라우저에서 ip:8888
에 액세스하면 다음을 볼 수 있습니다. : 🎜🎜 🎜🎜 동시에 서버는 다음 콘텐츠를 출력합니다: 🎜rrreee 🎜이 콘텐츠는 클라이언트 요청 원시 데이터입니다. 이 데이터를 구문 분석하고 file_get_content
를 사용하여 요청에 따라 응답할 수 있습니다. 지정된 파일 내용을 브라우저로 반환합니다🎜🎜🎜추천 학습: "🎜PHP 비디오 튜토리얼🎜" 🎜 🎜🎜위 내용은 PHP+Socket 시리즈의 IO 다중화 및 웹서버 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!