14. PHP socket 初探 - 含着泪也要磕完 libevent(三)
优质
小牛编辑
157浏览
2023-12-01
其实在上一篇 libevent 文章中如果你总结能力很好的话,可以观察出来我们尝试利用 libevent 做了至少两件事情:
- 毫秒级别定时器
- 信号监听工具
大家都是码 php 的,也喜欢把自己说的洋气点儿:我是写服务器的。所以,今天的第一个案例就是拿 libevent 来构建一个简单粗暴的 http 服务器:
$host = '0.0.0.0';
$port = 9999;
$listen_socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
socket_bind( $listen_socket, $host, $port );
socket_listen( $listen_socket );
echo PHP_EOL.PHP_EOL."Http Server ON : http://{$host}:{$port}".PHP_EOL;
// 将服务器设置为非阻塞,此处概念可能略拐弯,建议各位查阅一下手册
socket_set_nonblock( $listen_socket );
// 创建事件基础体,还记得航空母舰吗?
$event_base = new EventBase();
// 创建一个事件,还记得歼15舰载机吗?我们将“监听socket”添加到事件监听中,触发条件是read,也就是说,一旦“监听socket”上有客户端来连接,就会触发这里,我们在回调函数里来处理接受到新请求后的反应
$event = new Event( $event_base, $listen_socket, Event::READ | Event::PERSIST, function( $listen_socket ){
// 为什么写成这样比较执拗的方式?因为,“监听socket”已经被设置成了非阻塞,这种情况下,accept是立即返回的,所以,必须通过判定accept的结果是否为true来执行后面的代码。一些实现里,包括workerman在内,可能是使用@符号来压制错误,个人不太建议这>样做
if( ( $connect_socket = socket_accept( $listen_socket ) ) != false){
echo "有新的客户端:".intval( $connect_socket ).PHP_EOL;
$msg = "HTTP/1.0 200 OK\r\nContent-Length: 2\r\n\r\nHi";
socket_write( $connect_socket, $msg, strlen( $msg ) );
socket_close( $connect_socket );
}
}, $listen_socket );
$event->add();
$event_base->loop();
将代码保存为 test.php,然后 php http.php 运行起来。再开一个终端,使用 curl 的 GET 方式去请求服务器,效果如下:
这是一个非常非常简单地不能再简单的 http demo 了,对于一个完整的 http 服务器而言,他还差比较完整的 http 协议的实现、多核 CPU 的利用等等。这些,我们会放到后面继续深入的文章中开始细化丰富。
还记得我们使用 select 系统调用实现了一个粗暴的在线聊天室,select 这种业余的都敢出来混个聊天室,专业的绝对不能怂。