首页 Order Swoole4 正文

Swoole4 [异步风格] HTTP服务器

金鹏头像 金鹏 Swoole4 2021-12-26 13:12:39 0 1093
导读:Http\ServerHttp\Server继承自Server,所以Server提供的所有API和配置项都可以使用,进程模型也是一致的。请参考Server章节。内置...

Http\Server

Http\Server 继承自 Server,所以 Server 提供的所有 API 和配置项都可以使用,进程模型也是一致的。请参考 Server 章节。

内置 HTTP 服务器的支持,通过几行代码即可写出一个高并发,高性能,异步 IO 的多进程 HTTP 服务器。

$http = new Swoole\Http\Server("127.0.0.1", 9501);
$http->on('request', function ($request, $response) {
    $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
});
$http->start();

通过使用 Apache bench 工具进行压力测试,在 Inter Core-I5 4核 + 8G内存的普通 PC 机器上,Http\Server 可以达到近 11万QPS

远远超过 PHP-FPMGolangNode.js 自带 Http 服务器。性能几乎接近与 Nginx 的静态文件处理。

ab -c 200 -n 200000 -k http://127.0.0.1:9501/
  • 使用 HTTP2 协议

    • 使用 SSL 下的 HTTP2 协议必须安装 openssl, 且需要高版本 openssl 必须支持 TLS1.2ALPNNPN

    • 编译时需要使用 --enable-http2 开启

./configure --enable-openssl --enable-http2

设置 HTTP 服务器的 open_http2_protocoltrue

$server = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$server->set([
    'ssl_cert_file' => $ssl_dir . '/ssl.crt',
    'ssl_key_file' => $ssl_dir . '/ssl.key',
    'open_http2_protocol' => true,
]);


  • Nginx + Swoole 配置

由于 Http\ServerHTTP 协议的支持并不完整,建议仅作为应用服务器,用于处理动态请求,并且在前端增加 Nginx 作为代理。

server {
    root /data/wwwroot/;
    server_name local.swoole.com;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
        proxy_set_header X-Real-IP $remote_addr;
        if (!-e $request_filename) {
             proxy_pass http://127.0.0.1:9501;
        }
    }
}



通过读取 $request->header['x-real-ip'] 来获取客户端的真实 IP


方法

on()

注册事件回调函数。

Server 的回调 相同,不同之处是:

  • Http\Server->on 不接受 onConnect/onReceive 回调设置

  • Http\Server->on 额外接受 1 种新的事件类型 onRequest

$http_server->on('request', function(\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
     $response->end("<h1>hello swoole</h1>");
});


在收到一个完整的 HTTP 请求后,会回调此函数。回调函数共有 2 个参数:

  • $requestHTTP 请求信息对象,包含了 header/get/post/cookie 等相关信息

  • $responseHTTP 响应对象,支持 cookie/header/statusHTTP 操作

onRequest 回调函数返回时底层会销毁 $request$response 对象





start()

启动 HTTP 服务器

启动后开始监听端口,并接收新的 HTTPWebSocket 请求。

Swoole\Http\Server->start();



Http\Request

HTTP 请求对象,保存了 HTTP 客户端请求的相关信息,包括 GETPOSTCOOKIEHeader 等。

请勿使用 & 符号引用 Http\Request 对象

HTTP 请求的头部信息。类型为数组,所有 key 均为小写。

Swoole\Http\Request->header: array
  • 示例

echo $request->header['host']; 
echo $request->header['accept-language'];


数组的 key 全部为小写,并且与 PHP$_SERVER 数组保持一致

  • 示例

echo $request->server['request_time'];
key说明
query_string请求的 GET 参数,如:id=1&cid=2 如果没有 GET 参数,该项不存在
request_method请求方法,GET/POST
request_uriGET 参数的访问地址,如 /favicon.ico
path_inforequest_uri
request_timerequest_time 是在 Worker 设置的,在 SWOOLE_PROCESS 模式下存在 dispatch 过程,因此可能会与实际收包时间存在偏差。尤其是当请求量超过服务器处理能力时,request_time 可能远滞后于实际收包时间。可以通过 $server->getClientInfo 方法获取 last_time 获得准确的收包时间。
request_time_float请求开始的时间戳,以微秒为单位,float 类型,如 1576220199.2725
server_protocol服务器协议版本号,HTTP 是:HTTP/1.0HTTP/1.1HTTP2 是:HTTP/2
server_port服务器监听的端口
remote_port客户端的端口
remote_addr客户端的 IP 地址
master_time连接上次通讯时间




get

HTTP 请求的 GET 参数,相当于 PHP 中的 $_GET,格式为数组。

Swoole\Http\Request->get: array
  • 示例

// 如:index.php?hello=123 
echo $request->get['hello']; // 获取所有GET参数 var_dump($request->get);
  • 注意

为防止 HASH 攻击,GET 参数最大不允许超过 128




post

HTTP 请求的 POST 参数,格式为数组

Swoole\Http\Request->post: array
  • 示例

echo $request->post['hello'];
  • 注意

-POSTHeader 加起来的尺寸不得超过 package_max_length 的设置,否则会认为是恶意请求
-POST 参数的个数最大不超过 128



HTTP 请求携带的 COOKIE 信息,格式为键值对数组。

Swoole\Http\Request->cookie: array
  • 示例

echo $request->cookie['username'];

files

上传文件信息。

类型为以 form 名称为 key 的二维数组。与 PHP$_FILES 相同。最大文件尺寸不得超过 package_max_length 设置的值。请勿使用 Swoole\Http\Server 处理大文件上传。

Swoole\Http\Request->files: array
  • 示例

Array
(
    [name] => facepalm.jpg // 浏览器上传时传入的文件名称
    [type] => image/jpeg // MIME类型
    [tmp_name] => /tmp/swoole.upfile.n3FmFr // 上传的临时文件,文件名以/tmp/swoole.upfile开头
    [error] => 0
    [size] => 15476 // 文件尺寸
)
  • 注意

$request 对象销毁时,会自动删除上传的临时文件



getContent()

Swoole 版本 >= v4.5.0 可用,在低版本可使用别名 rawContent (此别名将永久保留,即向下兼容)

获取原始的 POST 包体。

用于非 application/x-www-form-urlencoded 格式的 HTTP POST 请求。返回原始 POST 数据,此函数等同于 PHPfopen('php://input')

Swoole\Http\Request->getContent(): string

有些情况下服务器不需要解析 HTTP POST 请求参数,通过 http_parse_post 配置,可以关闭 POST 数据解析。



getData()

获取完整的原始 Http 请求报文。包括 Http HeaderHttp Body

Swoole\Http\Request->getData(): string

create()

创建一个 Swoole\Http\Request 对象。

Swoole 版本 >= v4.6.0 可用

Swoole\Http\Request->create(array $options): Swoole\Http\Request|false
  • 参数

    • 功能:可选参数,用于设置 Request 对象的配置

    • array $options

参数默认值说明
parse_cookietrue设置是否解析 Cookie
parse_bodytrue设置是否解析 Http Body
parse_filestrue设置上传文件解析开关
enable_compressiontrue设置是否启用压缩
compression_level1设置压缩级别,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多

parse()

解析 HTTP 请求数据包,会返回成功解析的数据包长度。

Swoole 版本 >= v4.6.0 可用

Swoole\Http\Request->parse(string $data): int|false

isCompleted()

获取当前的 HTTP 请求数据包是否已到达结尾。

Swoole 版本 >= v4.6.0 可用

  • 示例

use Swoole\Http\Request;

$data = "GET /index.html?hello=world&test=2123 HTTP/1.1\r\n";
$data .= "Host: 127.0.0.1\r\n";
$data .= "Connection: keep-alive\r\n";
$data .= "Pragma: no-cache\r\n";
$data .= "Cache-Control: no-cache\r\n";
$data .= "Upgrade-Insecure-Requests: \r\n";
$data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\n";
$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n";
$data .= "Accept-Encoding: gzip, deflate, br\r\n";
$data .= "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\r\n";
$data .= "Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\r\n";

/** @var Request $req */
$req = Request::create(['parse_cookie' => false]);
var_dump($req);

var_dump($req->isCompleted());
var_dump($req->parse($data));

var_dump($req->parse("\r\n"));
var_dump($req->isCompleted());

var_dump($req);
// 关闭了解析cookie,所以会是null
var_dump($req->cookie);


getMethod()

获取当前的 HTTP 请求的请求方式。

Swoole 版本 >= v4.6.2 可用

var_dump($request->server['request_method']); var_dump($request->getMethod());



Http\Response

HTTP 响应对象,通过调用此对象的方法,实现 HTTP 响应发送。

Response 对象销毁时,如果未调用 end 发送 HTTP 响应,底层会自动执行 end("");

请勿使用 & 符号引用 Http\Response 对象

header()

设置 HTTP 响应的 Header 信息【别名 setHeader

Swoole\Http\Response->header(string $key, string $value, bool $format = true);
  • 参数

    • 功能:是否需要对 Key 进行 HTTP 约定格式化【默认 true 会自动格式化】

    • 默认值true

    • 其它值:无

    • 功能HTTP 头的 value

    • 默认值:无

    • 其它值:无

    • 功能HTTP 头的 Key

    • 默认值:无

    • 其它值:无

    • string $key

    • string $value

    • bool $format

  • 返回值

    • 设置失败,返回 false

    • 设置成功,没有任何返回值

  • 注意

    -header 设置必须在 end 方法之前  -$key 必须完全符合 HTTP 的约定,每个单词首字母大写,不得包含中文,下划线或者其他特殊字符
    -$value 必须填写
    -$ucwords 设为 true,底层会自动对 $key 进行约定格式化
    - 重复设置相同 $keyHTTP 头会覆盖,取最后一次

Swoole 版本 >= v4.6.0 时,支持重复设置相同 $keyHTTP 头,并且 $value 支持多种类型,如 arrayobjectintfloat,底层会进行 toString 转换,并且会移除末尾的空格以及换行。

$response->header('content-type', 'image/jpeg', true);

$response->header('Content-Length', '100002 ');
$response->header('Test-Value', [
    "a\r\n",
    'd5678',
    "e  \n ",
    null,
    5678,
    3.1415926,
]);
$response->header('Foo', new SplFileInfo('bar'));

trailer()

Header 信息附加到 HTTP 响应的末尾,仅在 HTTP2 中可用,用于消息完整性检查,数字签名等。

Swoole\Http\Response->trailer(string $key, string $value, bool $ucwords = true);
  • 参数

    • 功能:是否需要对 Key 进行 HTTP 约定格式化【默认 true 会自动格式化】

    • 默认值true

    • 其它值:无

    • 功能HTTP 头的 value

    • 默认值:无

    • 其它值:无

    • 功能HTTP 头的 Key

    • 默认值:无

    • 其它值:无

    • string $key

    • string $value

    • bool $ucwords

  • 返回值

    • 设置失败,返回 false

    • 设置成功,没有任何返回值

  • 注意

    重复设置相同 $keyHttp 头会覆盖,取最后一次。

  • 示例

$response->trailer('grpc-status', 0); 
$response->trailer('grpc-message', '');



设置 HTTP 响应的 cookie 信息。别名 setCookie。此方法参数与 PHPsetcookie 一致。

Swoole\Http\Response->cookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain  = '', bool $secure = false , bool $httponly = false, string $samesite = '', string $priority = '');
  • 注意

    -cookie 设置必须在 end 方法之前
    -$samesite 参数从 v4.4.6 版本开始支持,$priority 参数从 v4.5.8 版本开始支持
    -Swoole 会自动会对 $value 进行 urlencode 编码,可使用 rawCookie() 方法关闭对 $value 的编码处理
    -Swoole 允许设置多个相同 $keyCOOKIE



rawCookie()

设置 HTTP 响应的 cookie 信息

rawCookie() 的参数和上文的 cookie() 一致,只不过不进行编码处理

status()

发送 Http 状态码。别名 setStatusCode()

Swoole\Http\Response->status(int $http_status_code, string $reason): bool
  • 参数

    • 功能:状态码原因

    • 默认值:无

    • 其它值:无

    • 功能:设置 HttpCode

    • 默认值200

    • 其它值:无

    • int $http_status_code

    • string $reason

  • 提示

    • 如果只传入了第一个参数 $http_status_code 必须为合法的 HttpCode,如 200502301404 等,否则会设置为 200 状态码

    • 如果设置了第二个参数 $reason$http_status_code 可以为任意的数值,包括未定义的 HttpCode,如 499

    • 必须在 $response->end() 之前执行 status 方法



gzip()

此方法在 4.1.0 或更高版本中已废弃,请移步 http_compression;在新版本中使用 http_compression 配置项取代了 gzip 方法。
主要原因是 gzip() 方法未判断浏览器客户端传入的 Accept-Encoding 头,如果客户端不支持 gzip 压缩,强行使用会导致客户端无法解压。
全新的 http_compression 配置项会根据客户端 Accept-Encoding 头,自动选择是否压缩,并自动选择最佳的压缩算法。

启用 Http GZIP 压缩。压缩可以减小 HTML 内容的尺寸,有效节省网络带宽,提高响应时间。必须在 write/end 发送内容之前执行 gzip,否则会抛出错误。

Swoole\Http\Response->gzip(int $level = 1);
  • 参数

    • 功能:压缩等级,等级越高压缩后的尺寸越小,但 CPU 消耗更多。

    • 默认值:1

    • 其它值1-9

    • int $level

调用 gzip 方法后,底层会自动添加 Http 编码头,PHP 代码中不应当再行设置相关 Http 头;jpg/png/gif 格式的图片已经经过压缩,无需再次压缩

gzip 功能依赖 zlib 库,在编译 swoole 时底层会检测系统是否存在 zlib,如果不存在,gzip 方法将不可用。可以使用 yumapt-get 安装 zlib 库:

sudo apt-get install libz-dev




redirect()

发送 Http 跳转。调用此方法会自动 end 发送并结束响应。

Swoole\Http\Response->redirect(string $url, int $http_code = 302): void
  • 参数

    • 功能:状态码【默认为 302 临时跳转,传入 301 表示永久跳转】

    • 默认值302

    • 其它值:无

    • 功能:跳转的新地址,作为 Location 头进行发送

    • 默认值:无

    • 其它值:无

    • string $url

    • int $http_code

  • 示例

$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE);

$http->on('request', function ($req, Swoole\Http\Response $resp) {
    $resp->redirect("http://www.baidu.com/", 301);
});

$http->start();



write()

启用 Http Chunk 分段向浏览器发送相应内容。

关于 Http Chunk 可以参考 Http 协议标准文档。

Swoole\Http\Response->write(string $data): bool
  • 参数

    • 功能:要发送的数据内容【最大长度不得超过 2M,受 buffer_output_size 配置项控制】

    • 默认值:无

    • 其它值:无

    • string $data

  • 提示

    • 使用 write 分段发送数据后,end 方法将不接受任何参数,调用 end 只是会发送一个长度为 0Chunk 表示数据传输完毕。




sendfile()

发送文件到浏览器。

Swoole\Http\Response->sendfile(string $filename, int $offset = 0, int $length = 0): bool
  • 参数

    • 功能:发送数据的尺寸

    • 默认值:文件的尺寸

    • 其它值:无

    • 功能:上传文件的偏移量【可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传】

    • 默认值0

    • 其它值:无

    • 功能:要发送的文件名称【文件不存在或没有访问权限 sendfile 会失败】

    • 默认值:无

    • 其它值:无

    • string $filename

    • int $offset

    • int $length

  • 提示

    • 底层无法推断要发送文件的 MIME 格式因此需要应用代码指定 Content-Type

    • 调用 sendfile 前不得使用 write 方法发送 Http-Chunk

    • 调用 sendfile 后底层会自动执行 end

    • sendfile 不支持 gzip 压缩

  • 示例

$response->header('Content-Type', 'image/jpeg'); 
$response->sendfile(__DIR__.$request->server['request_uri']);



end()

发送 Http 响应体,并结束请求处理。

Swoole\Http\Response->end(string $html): bool
  • 参数

    • 功能:要发送的内容

    • 默认值:无

    • 其它值:无

    • string $html

  • 提示

    • end 只能调用一次,如果需要分多次向客户端发送数据,请使用 write 方法

    • 客户端开启了 KeepAlive,连接将会保持,服务器会等待下一次请求

    • 客户端未开启 KeepAlive,服务器将会切断连接

    • end 要发送的内容,由于受到 output_buffer_size 的限制,默认为 2M,如果大于这个限制则会响应失败,并抛出如下错误:

解决方法为:使用 sendfilewrite 或调整 output_buffer_size

WARNING finish (ERRNO 1203): The length of data [262144] exceeds the output buffer size[131072], please use the sendfile, chunked transfer mode or adjust the output_buffer_size



    detach()

    分离响应对象。使用此方法后,$response 对象销毁时不会自动 end,与 Http\Response::createServer->send 配合使用。

    Swoole\Http\Response->detach(): bool
  • 示例

    某些情况下,需要在 Task 进程中对客户端发出响应。这时可以利用 detach 使 $response 对象独立。在 Task 进程可以重新构建 $response,发起 Http 请求响应。

    • 跨进程响应

$http = new Swoole\Http\Server("0.0.0.0", 9501);

$http->set(['task_worker_num' => 1, 'worker_num' => 1]);

$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) {
    $resp->detach();
    $http->task(strval($resp->fd));
});

$http->on('finish', function () {
    echo "task finish";
});

$http->on('task', function ($serv, $task_id, $worker_id, $data) {
    var_dump($data);
    $resp = Swoole\Http\Response::create($data);
    $resp->end("in task");
    echo "async task\n";
});

$http->start();
  • 发送任意内容

某些特殊的场景下,需要对客户端发送特殊的响应内容。Http\Response 对象自带的 end 方法无法满足需求,可以使用 detach 分离响应对象,然后自行组装 HTTP 协议响应数据,并使用 Server->send 发送数据。

$http = new Swoole\Http\Server("0.0.0.0", 9501);

$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) {
   $resp->detach();
   $http->send($resp->fd, "HTTP/1.1 200 OK\r\nServer: server\r\n\r\nHello World\n");
});

$http->start();



create()

构造新的 Swoole\Http\Response 对象。

使用此方法前请务必调用 detach 方法将旧的 $response 对象分离,否则可能会造成对同一个请求发送两次响应内容。

Swoole\Http\Response::create(int $fd): Swoole\Http\Response

调用成功返回一个新的 Http\Response 对象,调用失败返回 false

  • 参数

    • 功能:参数为需要绑定的连接 $fd【调用 Http\Response 对象的 endwrite 方法时会向此连接发送数据】

    • 默认值:无

    • 其它值:无

    • int $fd

  • 示例

$http = new Swoole\Http\Server('0.0.0.0', 9501);

$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) {
    $resp->detach();
    $resp2 = Swoole\Http\Response::create($req->fd);
    $resp2->end("hello world");
});

$http->start();



isWritable()

判断 Swoole\Http\Response 对象是否已结束 (end) 或已分离 (detach)。

Swoole\Http\Response->isWritable(): bool

Swoole 版本 >= v4.6.0 可用

  • 示例

use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;

$http = new Server('0.0.0.0', 9501);

$http->on('request', function (Request $req, Response $resp) {
    var_dump($resp->isWritable()); // true
    $resp->end('hello');
    var_dump($resp->isWritable()); // false
    $resp->setStatusCode(403); // http response is unavailable (maybe it has been ended or detached)
});

$http->start();





配置选项

upload_tmp_dir

设置上传文件的临时目录。目录最大长度不得超过 220 字节

$server->set([
     'upload_tmp_dir' => '/data/uploadfiles/', 
]);

http_parse_post

针对 Request 对象的配置,设置 POST 消息解析开关,默认开启

  • 设置为 true 时自动将 Content-Type为x-www-form-urlencoded 的请求包体解析到 POST 数组。

  • 设置为 false 时将关闭 POST 解析。

$server->set([
     'http_parse_post' => false, 
]);

针对 Request 对象的配置,关闭 Cookie 解析,将在 header 中保留未经处理的原始的 Cookies 信息。默认开启

$server->set([
     'http_parse_cookie' => false, 
]);

http_parse_files

针对 Request 对象的配置,设置上传文件解析开关。默认开启

$server->set([
     'http_parse_files' => false,
]);

http_compression

针对 Response 对象的配置,启用压缩。默认为开启。

-http-chunk 不支持分段单独压缩,若使用 write 方法,将会强制关闭压缩。
-http_compressionv4.1.0 或更高版本可用

$server->set([
     'http_compression' => false, 
]);

目前支持 gzipbrdeflate 三种压缩格式,底层会根据浏览器客户端传入的 Accept-Encoding 头自动选择压缩方式。

依赖:

gzipdeflate 依赖 zlib 库,在编译 Swoole 时底层会检测系统是否存在 zlib

可以使用 yumapt-get 安装 zlib 库:

sudo apt-get install libz-dev

br 压缩格式依赖 googlebrotli 库,安装方式请自行搜索 install brotli on linux,在编译 Swoole 时底层会检测系统是否存在 brotli

http_compression_level

压缩级别,针对 Response 对象的配置

$level 压缩等级,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多。默认为 1, 最高为 9

document_root

配置静态文件根目录,与 enable_static_handler 配合使用。

此功能较为简易,请勿在公网环境直接使用

$server->set([
     'document_root' => '/data/webroot/example.com', // v4.4.0以下版本, 此处必须为绝对路径
     'enable_static_handler' => true, 
]);
  • 设置 document_root 并设置 enable_static_handlertrue 后,底层收到 Http 请求会先判断 document_root 路径下是否存在此文件,如果存在会直接发送文件内容给客户端,不再触发 onRequest 回调。

  • 使用静态文件处理特性时,应当将动态 PHP 代码和静态文件进行隔离,静态文件存放到特定的目录

enable_static_handler

开启静态文件请求处理功能,需配合 document_root 使用 默认 false

http_autoindex

开启 http autoindex 功能 默认不开启

http_index_files

配合 http_autoindex 使用,指定需要被索引的文件列表

$server->set([
     'document_root' => '/data/webroot/example.com',     
     'enable_static_handler' => true,     
     'http_autoindex' => true,     
     'http_index_files' => ['indesx.html', 'index.txt'], 
]);

static_handler_locations

设置静态处理器的路径。类型为数组,默认不启用。

Swoole 版本 >= v4.4.0 可用

$server->set([
     'static_handler_locations' => ['/static', '/app/images'], 
]);
  • 类似于 Nginxlocation 指令,可以指定一个或多个路径为静态路径。只有 URL 在指定路径下才会启用静态文件处理器,否则会视为动态请求。

  • location 项必须以 / 开头

  • 支持多级路径,如 /app/images

  • 启用 static_handler_locations 后,如果请求对应的文件不存在,将直接返回 404 错误

open_http2_protocol

启用 HTTP2 协议解析【默认值:false

需要编译时启用 --enable-http2 选项

compression_min_length

设置开启压缩的最小字节,超过该选项值才开启压缩。

Swoole 版本 >= v4.6.3 可用

$server->set([
     'compression_min_length' => 128, 
]);


本文地址:https://www.jinpeng.work/?id=73
若非特殊说明,文章均属本站原创,转载请注明原链接。
广告3

欢迎 发表评论:

  • 请填写验证码

日历

«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930

控制面板

您好,欢迎到访网站!
  查看权限
广告2

退出请按Esc键