我的基本PHP套接字服务器是否需要优化?

时间:2009-06-22 11:35:59

标签: php optimization sockets

和很多人一样,我可以用PHP做很多事情。我不断面临的一个问题是,其他人可以做得更干净,更有条理,更有条理。这也会导致更快的执行时间和更少的错误。

我刚刚编写了一个基本的PHP套接字服务器(真正的核心),并且问我是否可以在开始扩展核心之前告诉我应该做些什么。我不是在询问加密数据,身份验证或多线程等改进。

我更想知道诸如“我是否应该采用更面向对象的方式(使用PHP5)”这样的问题?“或者”是脚本运行良好的一般结构,还是应该做一些事情?不同?”。基本上,“这是套接字服务器的核心应该如何工作?”

事实上,我认为如果我只是向您展示这里的代码,很多人会立即看到改进的空间。请你好好告诉我。谢谢!

#!/usr/bin/php -q
<?
// config
$timelimit = 180; // amount of seconds the server should run for, 0 = run indefintely
$address = $_SERVER['SERVER_ADDR']; // the server's external IP
$port = 9000; // the port to listen on
$backlog = SOMAXCONN; // the maximum of backlog  incoming connections that will be queued for processing

// configure custom PHP settings
error_reporting(1); // report all errors
ini_set('display_errors', 1); // display all errors
set_time_limit($timelimit); // timeout after x seconds
ob_implicit_flush(); // results in a flush operation after every output call

//create master IPv4 based TCP socket
if (!($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) die("Could not create master socket, error: ".socket_strerror(socket_last_error()));

// set socket options (local addresses can be reused)
if (!socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)) die("Could not set socket options, error: ".socket_strerror(socket_last_error()));

// bind to socket server
if (!socket_bind($master, $address, $port)) die("Could not bind to socket server, error: ".socket_strerror(socket_last_error()));

// start listening
if (!socket_listen($master, $backlog)) die("Could not start listening to socket, error: ".socket_strerror(socket_last_error()));

//display startup information
echo "[".date('Y-m-d H:i:s')."] SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n"; //max connections is a kernel variable and can be adjusted with sysctl
echo "[".date('Y-m-d H:i:s')."] Listening on ".$address.":".$port.".\n";
$time = time(); //set startup timestamp

// init read sockets array 
$read_sockets = array($master);

// continuously handle incoming socket messages, or close if time limit has been reached
while ((!$timelimit) or (time() - $time < $timelimit)) {
    $changed_sockets = $read_sockets;
    socket_select($changed_sockets, $write = null, $except = null, null);
    foreach($changed_sockets as $socket) {
        if ($socket == $master) {
            if (($client = socket_accept($master)) < 0) {
                echo "[".date('Y-m-d H:i:s')."] Socket_accept() failed, error: ".socket_strerror(socket_last_error())."\n";
                continue;
            } else {
                array_push($read_sockets, $client);
                echo "[".date('Y-m-d H:i:s')."] Client #".count($read_sockets)." connected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
            }
        } else {
            $data = @socket_read($socket, 1024, PHP_NORMAL_READ); //read a maximum of 1024 bytes until a new line has been sent
            if ($data === false) { //the client disconnected
                $index = array_search($socket, $read_sockets);
                unset($read_sockets[$index]);
                socket_close($socket);
                echo "[".date('Y-m-d H:i:s')."] Client #".($index-1)." disconnected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
            } else {
                if ($data = trim($data)) { //remove whitespace and continue only if the message is not empty
                    switch ($data) {
                        case "exit": //close connection when exit command is given
                            $index = array_search($socket, $read_sockets);
                            unset($read_sockets[$index]);
                            socket_close($socket);
                            echo "[".date('Y-m-d H:i:s')."] Client #".($index-1)." disconnected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
                            break;
                        default: //for experimental purposes, write the given data back
                            socket_write($socket, "\n you wrote: ".$data);
                    }
                }
            }
        }
    }
}
socket_close($master); //close the socket
echo "[".date('Y-m-d H:i:s')."] SERVER CLOSED.\n";
?>

2 个答案:

答案 0 :(得分:0)

一件小事,我个人创建了一个输出函数,而不仅仅是使用echo,这样很容易将其关闭,更改格式等。例如

function log($message = '')
{
    echo '['.date('Y-m-d H:i:s').']'.$message;
}

然后你可以使用:

log("SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n");

而不是

echo "[".date('Y-m-d H:i:s')."] SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n";

哦,一定要使用===而不是==否则你可能会得到一些奇怪的结果。

答案 1 :(得分:0)

我会将你的switch $ data移动到一个函数中,因为这可能会扩展。

此外,由于您看起来使用的是基于文本的协议,因此可能需要使用/ strtok来获取第一级命令并根据有效命令数组进行检查。也可以有一个数组来描述要调用的内部函数,并使用call_user_func_array来分派调用。