Автор оригинала: David Wong.
Вам следует попрактиковаться после того, как вы поймете концепцию, или вы станете гигантским ребенком
Если есть какой-либо урожай, пожалуйста Добавьте маленькую звездочку Если не будет урожая Оппозиция Никакой помощи Отчет Санлиан
- Хранилище кода
- Практика свул [чат-комната]
- Опыт работы в Интернете
Подготовка
- Вам нужно сначала понять все в целом [1], и понять основные принципы использования websocket на стороне сервера
- Клиент JS websocket прост в использовании
Воспользуйся
Command line 1 php src/websocket/run.php Command line 2 cd public && php -S localhost:8000 #Client, open more to check the effect Visit http: // localhost: 8000/
WebSocket
Официальный пример
$server = new swoole_websocket_server("0.0.0.0", 9501);
$server->on('open', function (swoole_websocket_server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
$server->on('message', function (swoole_websocket_server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$server->on('request', function (swoole_http_request $request, swoole_http_response $response) {
Global $server; // call external server
//$server - > connections traverses the FD of all websocket connected users and pushes to all users
foreach ($server->connections as $fd) {
$server->push($fd, $request->get['message']);
}
});
$server->start();Подробное объяснение:
Свул? Веб-карман? Сервер наследуется от swool? HTTP? Сервер
- Если установлен обратный вызов onrequest, сервер websocket также может одновременно выступать в качестве HTTP-сервера
- Обратный вызов onrequest не установлен, сервер websocket вернет страницу с ошибкой HTTP 400 после получения HTTP – запроса
- Если вы хотите запустить push всех веб-сайтов, получив HTTP, вам нужно обратить внимание на область действия. Для ориентированного на процесс, пожалуйста, используйте global для ссылки на сервер swool websocket. Для объектно-ориентированного вы можете установить сервер swool websocket в качестве свойства участника
функция onOpen(swoole_websocket_server
$svr, swoole_http_request$req);- Эта функция вызывается обратно, когда клиент websocket устанавливает соединение с сервером и завершает рукопожатие.
- $req-это объект HTTP-запроса, который содержит информацию о запросе на рукопожатие, отправленную клиентом
- В функции события onopen вы можете вызвать push для отправки данных клиенту или закрыть, чтобы закрыть соединение
- Обратный вызов события onopen является необязательным
функция onMessage(swoole_websocket_server
$сервер, swoole_websocket_frame$кадр)- Эта функция вызывается обратно, когда сервер получает фрейм данных от клиента.
- $кадр-это свул? Веб-карман? Объект фрейма, содержащий информацию о фрейме данных, отправленную клиентом
- Обратный вызов Onmessage Должен быть установлен , сервер не запустится без настройки
- Кадр ping, отправленный клиентом, не будет срабатывать при сообщении, и базовый уровень автоматически ответит на пакет pong
Свул? Веб-карман? Свойство рамки
$рамка->fd, идентификатор сокета клиента, используя$server->pushPush-данные$frame->данные, содержимое данных, может быть текстовым содержимым или двоичными данными, можно судить по значению кода операции$фрейм->код операцииДля определения типа кода операции websocket, пожалуйста, обратитесь к стандартному документу протокола websocket$frame->готовоУказывает, завершен ли фрейм данных или нет. Запрос websocket может быть отправлен в нескольких кадрах данных (нижний уровень реализовал автоматическое объединение кадров данных, теперь вам не нужно беспокоиться о неполном полученном кадре данных).
Пример сервера чата
Структура каталогов:
конфиг
- конфиг
src
websocket
- websocket
- websocket
- Websocketserver.php версия таблицы памяти
- Wsredisserver.php версия redis
Websocketserver.php версия таблицы памяти
createTable();
//Instantiation configuration
$this->config = Config::getInstance();
}
public function run()
{
$this->server = new \swoole_websocket_server(
$this->config['socket']['host'],
$this->config['socket']['port']
);
$this->server->on('open', [$this, 'open']);
$this->server->on('message', [$this, 'message']);
$this->server->on('close', [$this, 'close']);
$this->server->start();
}
public function open(\swoole_websocket_server $server, \swoole_http_request $request)
{
$user = [
'fd' => $request->fd,
'name' => $this->config['socket']['name'][array_rand($this->config['socket']['name'])] . $request->fd,
'avatar' => $this->config['socket']['avatar'][array_rand($this->config['socket']['avatar'])]
];
//Put in memory table
$this->table->set($request->fd, $user);
$server->push($request->fd, json_encode(
array_merge(['user' => $user], ['all' => $this->allUser()], ['type' => 'openSuccess'])
)
);
}
private function allUser()
{
$users = [];
foreach ($this->table as $row) {
$users[] = $row;
}
return $users;
}
public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
{
$this->pushMessage($server, $frame->data, 'message', $frame->fd);
}
/**
*Push message
*
* @param \swoole_websocket_server $server
* @param string $message
* @param string $type
* @param int $fd
*/
private function pushMessage(\swoole_websocket_server $server, string $message, string $type, int $fd)
{
$message = htmlspecialchars($message);
$datetime = date('Y-m-d H:i:s', time());
$user = $this->table->get($fd);
foreach ($this->table as $item) {
//Don't send it by yourself
if ($item['fd'] == $fd) {
continue;
}
$server->push($item['fd'], json_encode([
'type' => $type,
'message' => $message,
'datetime' => $datetime,
'user' => $user
]));
}
}
/**
*When the client is closed
*
* @param \swoole_websocket_server $server
* @param int $fd
*/
public function close(\swoole_websocket_server $server, int $fd)
{
$user = $this->table->get($fd);
$this - > pushmessage ($server, "{$user ['name ']} leave the chat room",'close', $FD);
$this->table->del($fd);
}
/**
*Create memory table
*/
private function createTable()
{
$this->table = new \swoole_table(1024);
$this->table->column('fd', \swoole_table::TYPE_INT);
$this->table->column('name', \swoole_table::TYPE_STRING, 255);
$this->table->column('avatar', \swoole_table::TYPE_STRING, 255);
$this->table->create();
}
}Wsredisserver.php версия redis
config = Config::getInstance();
// redis
$this->initRedis();
//Initialization, mainly because the server itself will not clear redis
foreach ($this->allUser() as $item) {
$this->client->hdel("{$this->key}:{$item['fd']}", ['fd', 'name', 'avatar']);
}
}
public function run()
{
$this->server = new \swoole_websocket_server(
$this->config['socket']['host'],
$this->config['socket']['port']
);
$this->server->on('open', [$this, 'open']);
$this->server->on('message', [$this, 'message']);
$this->server->on('close', [$this, 'close']);
$this->server->start();
}
public function open(\swoole_websocket_server $server, \swoole_http_request $request)
{
$user = [
'fd' => $request->fd,
'name' => $this->config['socket']['name'][array_rand($this->config['socket']['name'])] . $request->fd,
'avatar' => $this->config['socket']['avatar'][array_rand($this->config['socket']['avatar'])]
];
//Put it in redis
$this->client->hmset("{$this->key}:{$user['fd']}", $user);
//Push to everyone, including yourself
foreach ($this->allUser() as $item) {
$server->push($item['fd'], json_encode([
'user' => $user,
'all' => $this->allUser(),
'type' => 'openSuccess'
]));
}
}
private function allUser()
{
$users = [];
$keys = $this->client->keys("{$this->key}:*");
//All keys
foreach ($keys as $k => $item) {
$users[$k]['fd'] = $this->client->hget($item, 'fd');
$users[$k]['name'] = $this->client->hget($item, 'name');
$users[$k]['avatar'] = $this->client->hget($item, 'avatar');
}
return $users;
}
public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
{
$this->pushMessage($server, $frame->data, 'message', $frame->fd);
}
/**
*Push message
*
* @param \swoole_websocket_server $server
* @param string $message
* @param string $type
* @param int $fd
*/
private function pushMessage(\swoole_websocket_server $server, string $message, string $type, int $fd)
{
$message = htmlspecialchars($message);
$datetime = date('Y-m-d H:i:s', time());
$user['fd'] = $this->client->hget("{$this->key}:{$fd}", 'fd');
$user['name'] = $this->client->hget("{$this->key}:{$fd}", 'name');
$user['avatar'] = $this->client->hget("{$this->key}:{$fd}", 'avatar');
foreach ($this->allUser() as $item) {
//Don't send it by yourself
if ($item['fd'] == $fd) {
continue;
}
$is_push = $server->push($item['fd'], json_encode([
'type' => $type,
'message' => $message,
'datetime' => $datetime,
'user' => $user
]));
//Delete failed push
if (!$is_push) {
$this->client->hdel("{$this->key}:{$item['fd']}", ['fd', 'name', 'avatar']);
}
}
}
/**
*When the client is closed
*
* @param \swoole_websocket_server $server
* @param int $fd
*/
public function close(\swoole_websocket_server $server, int $fd)
{
$user['fd'] = $this->client->hget("{$this->key}:{$fd}", 'fd');
$user['name'] = $this->client->hget("{$this->key}:{$fd}", 'name');
$user['avatar'] = $this->client->hget("{$this->key}:{$fd}", 'avatar');
$this - > pushmessage ($server, "{$user ['name ']} leave the chat room",'close', $FD);
$this->client->hdel("{$this->key}:{$fd}", ['fd', 'name', 'avatar']);
}
/**
*Initialize redis
*/
private function initRedis()
{
$this->client = new Client([
'scheme' => $this->config['socket']['redis']['scheme'],
'host' => $this->config['socket']['redis']['host'],
'port' => $this->config['socket']['redis']['port'],
]);
}
}Wsredisserver.php версия redis
path = __DIR__ . '/../../config/';
}
//Singleton mode
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function offsetSet($offset, $value)
{
// castration
}
public function offsetGet($offset)
{
if (empty($this->config)) {
$this->config[$offset] = require $this->path . $offset . ".php";
}
return $this->config[$offset];
}
public function offsetExists($offset)
{
return isset($this->config[$offset]);
}
public function offsetUnset($offset)
{
// castration
}
//Prohibit cloning
final private function __clone(){}
}config/socket.php
'0.0.0.0',
'port' => 9501,
'redis' => [
'scheme' => 'tcp',
'host' => '0.0.0.0',
'port' => 6380
],
'avatar' => [
'./images/avatar/1.jpg',
'./images/avatar/2.jpg',
'./images/avatar/3.jpg',
'./images/avatar/4.jpg',
'./images/avatar/5.jpg',
'./images/avatar/6.jpg'
],
'name' => [
'Kobe',
'Curie',
'KD',
'KG',
'Jordan',
'Duncan',
'Green',
'Thompson',
'igodara',
'Maddie',
'Iverson',
'Kai Wan Yi',
'Paul'
]
];config/socket.php
run();
резюме
Полный пример: комната чата
После обучения я обнаружил, что так называемый чат в моей жизни точно такой же. Конечно, это всего лишь простая демонстрация, и многие функции не реализованы. Если вы хотите учиться дальше, вы можете перейти на GitHub, чтобы найти полный проект для углубленного изучения
Справочные ресурсы
- свул
- Простая комната чата, реализованная с помощью PHP + swool