文件结构
>ls -lR
total 0
drwxrwxrwx 1 root root 512 Jan 14 16:19 client
drwxrwxrwx 1 root root 512 Jan 15 10:42 public
drwxrwxrwx 1 root root 512 Jan 15 10:21 server
drwxrwxrwx 1 root root 512 Jan 15 01:12 test
./client:
total 8
-rwxrwxrwx 1 root root 367 Jan 14 10:13 tcp_client.php
-rwxrwxrwx 1 root root 745 Jan 14 17:08 tcp_client_2.php
-rwxrwxrwx 1 root root 367 Jan 14 10:13 udp_client.php
-rwxrwxrwx 1 root root 911 Jan 14 17:05 udp_client_2.php
./public:
total 0
-rwxrwxrwx 1 root root 226 Jan 15 10:42 index.html
./server:
total 16
-rwxrwxrwx 1 root root 6360 Jan 17 07:37 http_server.php
-rwxrwxrwx 1 root root 730 Jan 14 10:09 tcp_server.php
-rwxrwxrwx 1 root root 665 Jan 14 16:35 udp_server.php
定义常量
php
define('DS', DIRECTORY_SEPARATOR);
defined('SERVER_PATH') or define('SERVER_PATH', __DIR__ . DS);
defined('FILES_PATH') or define('FILES_PATH', dirname(realpath(SERVER_PATH)) . DS);
defined('PUBLIC_PATH') or define('PUBLIC_PATH', FILES_PATH . "public" . DS);
定义监听
php
$address = "0.0.0.0";
$part = "80";
$http = new Server($address, $part);
$http->set(['task_worker_num' => 1, 'worker_num' => 1]);
$app = new Application();
$http->set([
'document_root' => PUBLIC_PATH,
'enable_static_handler' => true,
'http_autoindex' => true,
'http_index_files' => ['index.html', 'index.txt'],
]);
// $http->on('request', [$app, "OnRequest"]);
$http->on('request', function ($request, $reponse) use ($http) {
$app = new Application();
$parseInfo = $app->parsePathInfo($request);
if (1 == $parseInfo['isdetach']) {
$reponse->detach();
$http->task(strval($reponse->fd));
} else {
$app->OnRequest($request, $reponse, $parseInfo);
}
});
$http->on('task', function ($serv, $task_id, $worker_id, $data) {
$resp = Response::create($data);
$resp->write("task");
$resp->end();
$isWritable = empty($resp->isWritable()) ? 0 : 1;
var_dump('isWritable:' . $isWritable);
echo "async task\n";
});
$http->start();
解析请求数据,解析方法在Application::parsePathInfo中。
请求路径第一个数据为detach则使用$reponse->detach()创建task,在task线程中执行逻辑。
使用task进程,必须设置worker进程后开启该进程,默认不开启worker进程。
定义Application类
php
class Application {
private function setReponseHeader($request, $reponse) {
$reponse->header('Content-Type', 'application/json; charset=utf-8');
$reponse->header('access-control-allow-credentials', 'true');
$reponse->header('access-control-allow-methods', 'GET,HEAD,POST,OPTIONS');
$reponse->header('access-control-allow-headers', 'content-type,Authorization');
$reponse->header('Access-Control-Allow-Private-Network', 'true');
$origin = empty($request->header['origin']) ? '*' : $request->header['origin'];
$reponse->header('access-control-allow-origin', $origin);
}
public function OnRequest($request, $reponse, $parseInfo = null) {
$this->doredirect($request, $reponse);
$param = $this->getparam($request);
try {
$this->setReponseHeader($request, $reponse);
if (empty($parseInfo)) {
$parseInfo = $this->parsePathInfo($request);
}
$data = $this->execFunction($parseInfo['controller'], $parseInfo['function'], $param);
$reponse->end($data);
} catch (\Throwable $th) {
$code = $th->getCode();
$msg = $th->getMessage();
$data = backjson($code, $msg);
$reponse->end($data);
}
}
public function parsePathInfo($request) {
$uri = $request->server['request_uri'];
$uri_arr = array_values(array_filter(explode("/", $uri)));
$isdetach = 0;
if (2 == count($uri_arr)) {
$c = ucfirst($uri_arr[0]) . "Controller";
$f = $uri_arr[1];
if (!class_exists($c)) {
throw new \Exception("class " . $c . " not found", UseError::$SYSTEM_ERR);
}
if (!method_exists($c, $f)) {
throw new \Exception("function " . $c . "::" . $f . " not found", UseError::$SYSTEM_ERR);
}
$data = [
'uri' => $uri,
'isdetach' => $isdetach,
'controller' => $c,
'function' => $f,
];
return $data;
} else {
if ('detach' == $uri_arr[0]) {
$isdetach = 1;
$c = ucfirst($uri_arr[1]) . "Controller";
$f = $uri_arr[2];
}
$data = [
'uri' => $uri,
'isdetach' => $isdetach,
'controller' => $c,
'function' => $f,
];
return $data;
}
throw new \Exception("request_uri error", UseError::$SYSTEM_ERR);
}
private function doredirect($request, $reponse) {
$uri = $request->server['request_uri'];
if ('/baidu' == $uri) {
$reponse->redirect("http://www.baidu.com/", 301);
}
}
private function getparam($request) {
$method = $request->getMethod();
if ('GET' == $method) {
$param = $request->get;
} elseif ("POST" == $method) {
$param = $request->post;
}
return $param;
}
public function execFunction($controller, $function, $param = []) {
$object = new $controller();
$reflection = new ReflectionClass($object);
$method = $reflection->getMethod($function);
$neddparams = $method->getParameters();
if (count($neddparams) > 0) {
$name_arr = [];
foreach ($neddparams as $key => $value) {
$name = $value->getName();
$isOptional = $value->isOptional();
$name_arr[] = $name;
if (!isset($param[$name]) && !$isOptional) {
throw new \Exception("param " . $name . " is null", UseError::$SYSTEM_ERR);
}
}
foreach ($param as $key => $value) {
if (!in_array($key, $name_arr)) {
unset($param[$key]);
}
}
} else {
$param = [];
}
$reault = $method->invokeArgs($object, $param);
return $reault;
}
}
Application类中判断请求路径是否符合跳转条件,类似于反代理。
反代理解析Controller类。
IndexController类
php
Class IndexController {
public function hello() {
$data = [
"msg" => "hello word",
];
return backjson(200, "success", $data);
}
public function test($name) {
$data = [
"msg" => "hello " . $name,
];
return backjson(200, "success", $data);
}
}
定义Error类
php
class UseError {
public static $SYSTEM_ERR = 500;
}