Skip to content

Instantly share code, notes, and snippets.

@Clarence-pan
Created December 1, 2016 07:05
Show Gist options
  • Save Clarence-pan/9a7f53efbaa44ab940e515520d85b289 to your computer and use it in GitHub Desktop.
Save Clarence-pan/9a7f53efbaa44ab940e515520d85b289 to your computer and use it in GitHub Desktop.
一个简单的模拟Apache(httpd)的程序,通过PHP写的哟~ 性能还是很好的,比node.js有时候都快。
<?php
// 运行:
// php simple-httpd-helloworld.php [port]
// 默认端口号是80,可以通过命令行第一个参数指定端口号。
// 注意:需要支持pcntl和socket(不能在windows下跑 -- 可以在windows下的bash on ubuntu on windows中跑)
declare(ticks = 1);
// write something
$response = <<<TEXT
HTTP/1.0 200 OK
Server: My-PHP-Server
Cache-Control: no-cache
Connection: close
Content-Type: text/html
<!doctype html>
<html>
<head><title>Hello</title></head>
<body>
<p>Hello world!</p>
</body>
</html>
TEXT;
$response = str_replace("\n", "\r\n", $response);
function main()
{
global $role;
global $isRunning;
global $argv;
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$role = 'master';
write_log("Pid: " . posix_getpid());
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
pcntl_signal(SIGCHLD, 'sig_handler');
write_log("Creating socket...");
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
report_socket_last_error();
return __LINE__;
}
$host = '127.0.0.1';
$port = count($argv) > 1 ? $argv[1] : '80';
write_log("Binding socket to $host:$port...");
$ret = socket_bind($socket, $host, $port);
if ($ret === false) {
report_socket_last_error($socket);
socket_close($socket);
return __LINE__;
}
write_log("Listening socket...");
$ret = socket_listen($socket);
if ($ret === false) {
report_socket_last_error($socket);
socket_close($socket);
return __LINE__;
}
foreach (range(1, 10) as $slaveId) {
// fork...
$pid = pcntl_fork();
if ($pid < 0) {
write_log("Warning: Failed to fork slave${slaveId}!");
} else if ($pid == 0) {
$role = 'slave' . $slaveId;
return run_slave($socket);
} else {
write_log("Forked a process: pid = " . $pid);
}
}
assert($role === 'master', 'Only master can reach here!');
// master:
write_log("master is sleeping...");
$isRunning = true;
while ($isRunning) {
usleep(100);
}
socket_close($socket);
return 0;
}
function run_slave($socket)
{
global $isRunning;
write_log("Slave is running. PID=" . posix_getpid());
write_log("Set no blocking...");
socket_set_nonblock($socket); // no block is not better...
write_log("Accepting socket...");
$isRunning = true;
while ($isRunning) {
$connection = socket_accept($socket);
if ($connection === false) {
usleep(10);
continue;
}
if ($connection < 0) {
report_socket_last_error($socket);
socket_close($socket);
return __LINE__;
}
process_request_and_close($connection); // => 10process, 10concurrent 1000requests => 2591.82qps
}
socket_close($socket);
return 0;
}
/**
* Signal handler
*/
function sig_handler($sig)
{
global $isRunning;
switch ($sig) {
case SIGTERM:
case SIGINT:
write_log("Got SIGTEM/SIGINT, quiting");
$isRunning = false;
//exit();
break;
case SIGCHLD:
pcntl_waitpid(-1, $status);
break;
}
}
function write_log($msg)
{
global $role;
echo date('Y-m-d H:i:s') . " [$role]: {$msg}\n";
}
function report_socket_last_error($socket = null)
{
$lastError = $socket ? socket_last_error($socket) : socket_last_error();
$errorMsg = socket_strerror($lastError);
write_log("Error: $lastError $errorMsg");
}
function process_request_and_close($connection)
{
global $response;
socket_set_block($connection);
$read = socket_read($connection, 4 * 1024);
// write_log("Read from socket: " . $read);
socket_write($connection, $response, strlen($response));
socket_close($connection);
}
// run main process
exit(main());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment