базовый курс

ГРУППА КУРСА

Создание чата

В этой теме мы рассмотрим, как создать чат с использованием долгих опросов. Здесь показан только основной принцип, на котором основан чат, без функций, которые не связаны напрямую с обменом сообщениями. В дальнейшем Вы сможете самостоятельно эти функции добавить.

Авторизация

Начнём создавать чат со страницы авторизации. Обычно авторизация производится с главной страницы сайта. Сделаем самый простой вариант - без регистрации. Пользователь выбирает ник и заходит в чат. Вот код страницы:

HTML код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<title>Вход в чат</title>
<meta charset="utf-8">
</head>
<body>
<div>Авторизация</div>

<form action="chat.php" method="post">
  <input name="nick">
  <input type="submit" value="Войти">
</form>
</body>
</html>

Форма запускает файл chat.php. Это основная страница чата. Форма отправляет ник, введённый пользователем.

Страница чата

Страница чата называется chat.php. Это не просто страница, а серверный скрипт. Он получает от формы ник пользователя и выводит его на страницу, а также записывает ник в сессию, чтобы указывать его в сообщениях.

chat.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
header('Content-type: text/html; charset=utf-8');
$nick = $_POST['nick'];
session_start();
$_SESSION['nick'] = $nick;
?>
<!DOCTYPE html>
<html>
<head>
<title>Чат</title>
<meta charset="utf-8">
</head>
<body>
<div><?php echo $nick; ?></div>
<input id="user_mesage" size="50">
<input id="button" type="button" value="Отправить">
<div id="chat"></div>
</body>
</html>

Комментарии:

3 - получаем ник пользователя из формы и записываем в переменную $nick

5 - записываем ник в сессиию

14 - выводим ник на страницу

15 - поле для ввода сообщений

16 - кнопка отправки

17 - блок с сообщениями чата

Отправка сообщений

Создадим функцию отправки сообщений. Она не имеет отношения к технологии COMET. Это просто добавление текста в базу данных. Добавим на страницу следующий скрипт:

JavaScript:

19
20
21
22
23
24
25
26
27
28
29
30
31
32
var userMesage = document.getElementById('user_mesage');
var button = document.getElementById('button');
var chat = document.getElementById('chat');
var id = 0;

button.onclick = SendMesage;
function SendMesage ()
  {
  var xhr = new XMLHttpRequest();
  xhr.open('POST','sendmesage.php');
  xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
  xhr.send('mesage=' + userMesage.value);
  userMesage.value = '';
  }

Скрипт достаточно простой. Если Вы прочитали учебник по технологии AJAX, то его код должен быть для Вас понятен. Поясню лишь отдельные моменты. В строке 22 создана переменная id, она пока не используется. В строке 30 в тело запроса записывается текст из поля для ввода, а затем в строке 31 это поле очищается, чтобы можно было писать в нём новое сообщение.

Страница отправляет данные в файл sendmesage.php. Вот его код:

sendmesage.php:

+
1
2
<?php
header('Content-type: text/html; charset=utf-8');
3
4
5
6
7
8
9
10
session_start();
$nick = $_SESSION['nick'];
$mesage = $_POST['mesage'];

$db = mysqli_connect('localhost', 'root', '', 'mybase');
$query = "INSERT INTO chat (user, text)
  VALUES ('$nick', '$mesage')";
$result = mysqli_query($db, $query);

Скрипт берёт ник пользователя из сессии, сообщение получает от страницы и записывает эти данные в базу данных. Сообщение отправляется в таблицу, которая называется chat, поэтому нужно создать эту таблицу. В ней должны быть следующие поля: id, user и text.

Получение сообщений

Получение сообщений сделаем при помощи долгих опросов. Страница будет отправлять запрос на сервер и ждать ответ сервера, а при получении ответа сразу отправлять новый запрос. Добавим на страницу такой код:

JavaScript:

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function TakeMesage ()
  {
  var xhr = new XMLHttpRequest();
  xhr.open('POST','takemesage.php');
  xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
  xhr.send('id=' + id); 
  xhr.onreadystatechange=function ()
    {
    if (xhr.readyState == 4)
      {
      if (xhr.status == 200)
        {
        var an = xhr.responseText;
        if (an)
        Inchat(an);
        }
      TakeMesage();
      }
    }
  }
TakeMesage();

Поясню как работает этот скрипт. Страница отправляет обычный запрос. То, что ожидание ответа происходит длительное время, никак не влияет на код. В запросе страница отправляет id последнего полученного ею сообщения. При первом запросе id равен нулю, это установлено в строке 22. Затем id приходит вместе с ответом и записывается в переменную id при обработке ответа. Мы рассмотрим её позднее. Ответ сервера записывается в переменную an. Если ответ не пустой, то запускается функция обработки ответа. В строке 49 происходит рекурсия, то есть функция TakeMesage() вызывает сама себя и отправляется новый запрос на сервер. Этот вызов должен происходить независимо от того, успешно прошёл запрос или нет. Поэтому функция вызывается не при условии успешного запроса, а при условии завершения запроса. В строке 53 функция TakeMesage() вызывается первый раз. В дальнейшем вызов происходит в строке 49, но чтобы функция начала работать её нужно вызвать как-то иначе.

Сообщения, полученные в ответе сервера нужно вывести на страницу чата. Это делает функция Inchat(). Она работает так: ответ сервера в формате JSON переводится в объект. Для каждого сообщения создаётся тэг <p> и сообщение из объекта помещается в тэг. Затем тэг добавляется в болк с сообщениями. Вот код этой функции:

JavaScript:

54
55
56
57
58
59
60
61
62
63
64
65
66
function Inchat (an)
  {
  var mesages = JSON.parse(an);
  
  for (var i=0; i<mesages.length; i++)
    {
    var mesage = document.createElement('p');
    mesage.innerHTML = mesages[i].user + ': ' + mesages[i].text;
    chat.appendChild(mesage);
    }
  
  id = mesages[mesages.length-1].id;
  }

Комментарии:

56 - переводим ответ сервера в массив. Каждый элемент массива - это объект, содержащий одно сообщение

58 - цикл для всех сообщений

60 - создаём тэг <p>

61 - записываем в тэг автора и текст очередного сообщения

62 - добавляем сообщение в блок

65 - в переменную id записываем id последнего сообщения. Она будет использоваться при отправке следующего запроса.

Страница запускает на сервере файл takemesage.php. Этот скрипт берёт id последнего полученного страницей сообщения и проверяет, появились ли новые. Если сообщений не появилось, то скрипт ждёт 2 секунды, а затем производит новую проверку. Если сообщения добавились, то скрипт отправляет их и работа скрипта завершается. Вот этот скрипт:

takemesage:

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$db = mysqli_connect('localhost', 'root', '', 'mybase');
$current_id = $_POST['id'];
$start = time();
$f = true;

while ($f)
  {
  $query = "SELECT * FROM chat WHERE id > $current_id";
  $result = mysqli_query($db, $query);
  if ($result && mysqli_num_rows($result))
    {
    $mesages = mysqli_fetch_all($result, MYSQLI_ASSOC);
    echo json_encode($mesages);
    $f = false;
    }
  else
    {
    sleep(2);
    if (time() >= $start + 27)
    $f = false;
    }
  }

Комментарии:

3 - подключаемся к базе данных

4 - узнаём от страницы id последнего полученного ею сообщения и записываем в переменную $current_id

6 - время запуска скрипта

6 - флаг отстуствия новых записей. Пока он установлен, будут производится проверки на новые данные

8 - цикл проверки новых записей. Будет выполняться, пока установлен флаг

10 - запрос на выборку записей, у которых id больше текущего

11 - отправляем запрос

12 - если запрос выполнен и в выборке есть данные

14 - записываем в переменную $mesages все записи выборки

15 - переводим массив $mesages в формат JSON и отправляем ответ сервера

16 - снимаем флаг отсутствия новых сообщений. Следующей итерации цикла не будет, скрипт выполнит оставшиеся строки и закончит работу.

18 - если новых сообщений не появилось

20 - скрипт ждёт 2 секунды

21 - 22 - проверка времени работы скрипта. Это рассмотрено далее

Если новых сообщений нет долгое время, то скрипт не будет работать бесконечно. На серверах есть ограничение на максимальное время выполнения скрипта. Оно может быть разное, но обычно не меньше 30 секунд. Можно установить это время в скрипте, но на некоторых хостингах эта функция не работает. Когда время работы превышено, скрипт возвращает сообщение об ошибке.

Чтобы этого не происходило, нужно вовремя завершить выполнение скрипта. В строке 5 определяется время запуска скрипта. В стоках 21, 22 производится проверка. Если скрипт выполняется более 27 секунд, то флаг отстутсвия данных снимается, цикл прекращается и скрипт завершает работу. Ответ сервера будет без данных. Получив ответ, страница отправит новый запрос и скрипт запустится снова.

Как и при реализации частых запросов, я не делал проверку наличия записей в таблице. Поэтому перед первым запуском чата добавьте в базу данных хотя бы две записи.

Рассмотренный в данной теме пример чата является очень упрощённым. На практике чат должен содержать несколько важных функций. В том числе, нужно решить сколько сообщений нужно выводить при входе в чат. Ведь к моменту входа пользователя, в чате может быть очень много сообщений. Если это обычный чат, то можно показать несколько последних сообщений. Если это личная переписка, то должен быть доступ ко всем сообщениям, но выводить их нужно так, чтобы пользователю было удобно их смотреть. Кроме того, нужно создать обычные функции, такие как регистрация пользователей, обращание к конкретному пользователю и другие возможности.