A Preliminary Study of PHP socket-Tears Should Be Paid to End libevent (3)

  libevent, php, socket

Original address:https://t.ti-node.com/thread/ …

Compared with this period of time, everyone has seen that I left my job. First, I was lazy at home and didn’t bother to do anything. Second, I had a hard time trying to write some time and it all crashed into the data structure and algorithm.

Looking back today, continue the article here. How did you say that?

I have to finish the topic I chose with tears in my eyes!“(The picture has nothing to do with the text.Click here for details.)。

In fact, in the last libevent article (“Preliminary Study on PHP socket-Firmly Continue libevent (2)”), if you are good at summarizing, you can observe that we tried to use libevent to do at least two things:

  • Millisecond level timer
  • Signal monitoring tool

Everyone is code php, and likes to say something foreign: “I write about servers”. So, today’s first case is to use libevent to build a simple and crude http server:

<?php
$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();

Save the code as test.php and PHP http.php will run it. Open another terminal and use curl’s GET method to request the server. The effect is as follows:

This is a very, very simple http demo that can no longer be simple. For a complete http server, he still needs to complete the implementation of http protocol and the utilization of multi-core CPU. These, we will continue in-depth articles later began to refine rich.

I still remember that we implemented a rough online chat room by using select system call. Amateurs like select dare to mix up a chat room. Professionals can never counsel.

Countless specialties? ? ? ? ? ? ? ? ? ? ? ? ? ? ? To libevent!

La la la la, start code:

<?php
$host = '0.0.0.0';
$port = 9999;
$fd = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
socket_bind( $fd, $host, $port );
socket_listen( $fd );
// 注意,将“监听socket”设置为非阻塞模式
socket_set_nonblock( $fd );

// 这里值得注意,我们声明两个数组用来保存 事件 和 连接socket
$event_arr = []; 
$conn_arr = []; 

echo PHP_EOL.PHP_EOL."欢迎来到ti-chat聊天室!发言注意遵守当地法律法规!".PHP_EOL;
echo "        tcp://{$host}:{$port}".PHP_EOL;

$event_base = new EventBase();
$event = new Event( $event_base, $fd, Event::READ | Event::PERSIST, function( $fd ){
  // 使用全局的event_arr 和 conn_arr
  global $event_arr,$conn_arr,$event_base;
  // 非阻塞模式下,注意accpet的写法会稍微特殊一些。如果不想这么写,请往前面添加@符号,不过不建议这种写法
  if( ( $conn = socket_accept( $fd ) ) != false ){
    echo date('Y-m-d H:i:s').':欢迎'.intval( $conn ).'来到聊天室'.PHP_EOL;
    // 将连接socket也设置为非阻塞模式
    socket_set_nonblock( $conn );
    // 此处值得注意,我们需要将连接socket保存到数组中去
    $conn_arr[ intval( $conn ) ] = $conn;
    $event = new Event( $event_base, $conn, Event::READ | Event::PERSIST, function( $conn ) use( $event_arr ) { 
      global $conn_arr;
      $buffer = socket_read( $conn, 65535 );
      foreach( $conn_arr as $conn_key => $conn_item ){
        if( $conn != $conn_item ){
          $msg = intval( $conn ).'说 : '.$buffer;
          socket_write( $conn_item, $msg, strlen( $msg ) );
        }   
      }   
    }, $conn );
    $event->add();
    // 此处值得注意,我们需要将事件本身存储到全局数组中,如果不保存,连接会话会丢失,也就是说服务端和客户端将无法保持持久会话
    $event_arr[ intval( $conn ) ] = $event;
  }
}, $fd );
$event->add();
$event_base->loop();

Save the code as server.php, then PHP server.php will run it, and then open the other three terminals to connect to the chat room using telnet. The running effect is as follows:

Try to put a dynamic picture on it and see if it will work. The gif produced by myself is very large. I don’t know if the bandwidth is enough.

By the end of this article, the three axes that are the core of the Libevent series will not look like the following when you encounter these codes.