Socket编程

linux系统中, 一切都是文件, socket也只是一个特殊的文件. 所有的文件都可以使用readwrite进行操作.

Socket server创建标准流程

  • create
  • bind
  • listen

不过php提供一个非常方便的函数, stream_socket_server, 一个函数搞定上面三个步骤.

PHP示例

$fd = stream_socket_server("tcp://0.0.0.0:8090", $errno, $errstr);

上述代码就创建了一个socket.

$conn = stream_socket_accept($fd);

上述代码调用stream_socket_accept, 会阻塞在这儿, 所谓阻塞, 就是等待事件的发生, 如果事件没有发生, 就会持续等待. 当有客户端有新请求发起时, 事件发生, 激活进程, 继续向下执行.

fwrite($conn, "HTTP/1.0 200 OK\r\nContent-Type: 2\r\n\r\nHi");
fclose($conn);

在文章开头时说了, socket也是文件, 所以可以使用诸如readwrite这样的函数.

综合示例

<?php
$fd = stream_socket_server("tcp://0.0.0.0:8090", $errno, $errstr);

//省略错误处理

while (true) {
    $conn = stream_socket_accept($fd);

    $pid = pcntl_fork();
    if ($pid == 0) {
        $message = "Hi";
        $len = strlen($message);
        fwrite($conn, "HTTP/1.0 200 OK\r\nContent-Length: $len\r\n\r\n$message");
        fclose($conn);
        exit(0);
    } elseif ($pid > 0) {
        continue;
    } else {
        printf("fork failed");
    }
}
  • 创建socket
  • 调用stream_socket_accept, 进程阻塞在这里.
  • 当有新请求发起时, fork一个子进程进行处理.
  • 子进程处理完毕后, 结束进程, 释放资源.

上述代码是同步阻塞的方式, 高并发的时候效率低下, 现在最流行的方式是异步非阻塞.

异步非阻塞要配合Event来实现.

stream_set_blocking($fd, 0);      //设置成非阻塞方式
<?php

$fd = stream_socket_server("tcp://0.0.0.0:9001", $errno, $errstr);

stream_set_blocking($fd, 0);

$event_base = new EventBase();

$event = new Event($event_base, $fd, Event::READ | Event::PERSIST, function ($fd) use (&$event_base) {
 $conn = stream_socket_accept($fd);

 fwrite($conn, "HTTP/1.0 200 OK\r\nContent-Length: 2\r\n\r\nHi");
 fclose($conn);
}, $fd);

$event->add();

$event_base->loop();    

为了利用CPU多核的优势, 往往会fork出好几个进程同时监听一个端口. 可以通过stream_context_set_option来进行设置.

stream_context_set_option($fd, 'socket', 'so_reuseport', 1);
posted @ 2018-04-07 23:24  setevn  阅读(170)  评论(0编辑  收藏  举报