当前在线人数接口
接口开发
这一章我们将新增一个接口,用于返回服务器的在线人数信息
我们服务器目前使用的是Swoole WebSocket Server
,如果有看过Swoole
文档的童鞋,应该知道其实他继承自Swoole Http Server
,也就是说,它同样是支持普通的HTTP
请求的。
做题时间
- 请同学们为
Server
设置onRequest
回调,并在onRequest
回调中返回HelloWorld
。
Swoole Websocket Server:wiki.swoole.com/wiki/page/3…
Server
类:
<?php
...
class Server
{
...
public function __construct()
{
...
$this->ws->on('request', [$this, 'onRequest']);
...
}
...
public function onRequest($request, $response)
{
$response->end('HelloWorld');
}
}
...
请求成功获取到了HelloWorld
。
目前我们的请求通了,但是怎么获取到在线人数呢?
新的问题
不知道童鞋们有没有发现,游戏现在有一个严重的问题,就是有许多的操作都依赖于player_id
,如获取连接fd
、获取房间room_id
、移动玩家坐标。
虽然目前player_id
是前端随机生成的,但是还是会有重复的可能,当同一个player_id
再次连接服务器时,就会覆盖原有用户的一些信息,引发一些奇奇怪怪的bug,所以在客户端连接的时候需要对player_id
做在线检测,当player_id
已在线时,断开该连接。
综合上述来看,我们可以使用Redis
的Hash
结构,把每一个用户的player_id
保存起来,这样既可以做在线检测,又可以统计人数。
做题时间
- 在
DataCenter
中新增一个Hash
键的增删查方法,用于保存player_id
。 - 在服务端
onOpen()
回调中判断连接的player_id
是否已在线,在线则主动断开连接,不在线则放入Hash
表中。 - 当客户端断开时,删除
player_id
在线状态。 - 修改前端代码,获取
url
中的参数player_id
作为玩家player_id
,不存在时则自动生成。 - 当服务端主动断开时,判断是否
player_id
在线,进行弹框提示。
记得在
DataCenter
初始化的时候清理掉该键哦
DataCenter
类
<?php
...
class DataCenter
{
...
public static function setOnlinePlayer($playerId)
{
$key = self::PREFIX_KEY . ':online_player';
self::redis()->hSet($key, $playerId, 1);
}
public static function getOnlinePlayer($playerId)
{
$key = self::PREFIX_KEY . ':online_player';
return self::redis()->hGet($key, $playerId);
}
public static function delOnlinePlayer($playerId)
{
$key = self::PREFIX_KEY . ':online_player';
self::redis()->hDel($key, $playerId);
}
...
public static function setPlayerInfo($playerId, $playerFd)
{
...
self::setOnlinePlayer($playerId);
}
public static function delPlayerInfo($playerFd)
{
...
self::delOnlinePlayer($playerId);
}
...
public static function initDataCenter()
{
...
//清空在线玩家
$key = self::PREFIX_KEY . ':online_player';
self::redis()->del($key);
}
}
Server
类
<?php
...
class Server
{
...
/**
* @param $server \swoole_websocket_server
* @param $request
*/
public function onOpen($server, $request)
{
...
if (empty(DataCenter::getOnlinePlayer($playerId))) {
DataCenter::setPlayerInfo($playerId, $request->fd);
} else {
$server->disconnect($request->fd, 4000, '该player_id已在线');
}
}
...
}
...
index.html
:
...
<script>
var app = new Vue({
...
data: {
...
playerId: '',
...
},
created() {
this.initPlayerId()
...
},
...
methods: {
initPlayerId() {
var inputPlayerId = this.getUrlParam('player_id')
if (inputPlayerId !== '') {
this.playerId = inputPlayerId
} else {
this.playerId = 'player_' + Math.round(Math.random() * 1000)
}
},
getUrlParam(paramName) {
var url = document.location.toString();
var arrObj = url.split("?");
if (arrObj.length > 1) {
var arrPara = arrObj[1].split("&");
var arr;
for (var i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("=");
if (arr !== null && arr[0] === paramName) {
return arr[1];
}
}
return '';
}
else {
return "";
}
},
...
websocketclose(e) { //关闭
console.log('断开连接', e);
if (e.code === 4000) {
alert('该player_id已在线')
}
},
...
}
})
</script>
</body>
</html>
代码写完了,是时候试一下效果了,在浏览器中打开我们的前端页面,并在url
中附带参数?player_id=test001
拦截成功,接下来就要回归原题了。
做题时间
- 在
DataCenter
中新增lenOnlinePlayer()
方法,返回在线玩家数量。 - 在
onRequest
回调中返回在线玩家数量。 - 前端引入
jQuery
,通过Ajax
调用接口获取在线玩家数量并渲染在页面上。
小提示:onRequest回调只有一个,如何区别不同请求的action呢?
DataCenter
类:
<?php
...
class DataCenter
{
...
public static function lenOnlinePlayer()
{
$key = self::PREFIX_KEY . ':online_player';
return self::redis()->hLen($key);
}
...
}
Server
类:
<?php
...
class Server
{
...
public function onRequest($request, $response)
{
DataCenter::log("onRequest");
$action = $request->get['a'];
if ($action == 'get_online_player') {
$data = [
'online_player' => DataCenter::lenOnlinePlayer()
];
$response->end(json_encode($data));
}
}
}
...
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
...
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
...
</head>
<body>
<div id="app">
...
<div v-if="onlinePlayer">
当前在线玩家:{{onlinePlayer}}
</div>
...
</div>
<script>
var app = new Vue({
...
data: {
...
onlinePlayer: null,
},
created() {
...
this.getServerInfo()
},
...
methods: {
getServerInfo() {
var that = this;
$.ajax({
url: 'http://192.168.3.41:8812',
type: 'get',
dataType: 'json',
data: {
'a': 'get_online_player'
},
success: function (result) {
that.onlinePlayer = result.online_player
},
error: function () {
}
})
},
...
}
})
</script>
</body>
</html>
Homework
请童鞋们尝试新增一个“改名”按钮,通过在玩家ID的输入框中输入姓名后,点击按钮进行改名,可以参考master
分支的前端代码。
本章对应Github Commit
:扩展二:当前在线人数接口