Created
December 1, 2016 07:05
-
-
Save Clarence-pan/9a7f53efbaa44ab940e515520d85b289 to your computer and use it in GitHub Desktop.
一个简单的模拟Apache(httpd)的程序,通过PHP写的哟~ 性能还是很好的,比node.js有时候都快。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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