базовый курс

ГРУППА КУРСА

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

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

Авторизация

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

HTML код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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="usermesage" 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('usermesage');
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
$db=mysqli_connect('localhost', 'root', '', 'mybase');
session_start();
$nick=$_SESSION['nick'];
$mesage=$_POST['mesage'];
$query="INSERT INTO chat (user, text) VALUES ('$nick', '$mesage')";
$result=mysqli_query($db, $query);
mysqli_close($db);

Скрипт берёт ник пользователя из сессии, сообщение получает от страницы и записывает эти данные в базу данных. Сообщение отправляется в таблицу, которая называется 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
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;
        inchat(an);
        }
      takemesage();
      }
    }
  }
takemesage();

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

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

JavaScript:

53
54
55
56
57
58
59
60
61
62
63
64
function inchat (an)
  {
  var obj=JSON.parse(an);
  var number_mesages=obj.user.length;
  for (var i=0; i<number_mesages; i++)
    {
    var mesage=document.createElement('p');
    mesage.innerHTML=obj.user[i]+': '+obj.mesage[i];
    chat.appendChild(mesage);
    }
  id=obj.id;
  }

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

55 - переводим ответ сервера в объект

56 - определяем количество полученных сообщений. Для этого узнаём длину массива с пользователями. Можно было использовать длину массива с сообщениями, ведь массивы имеют одинаковые размеры.

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

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

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

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

63 - id последнего полученного сообщения находится в свойстве id объекта, полученного от сервера. Записываем его в переменную id. Она будет использоваться при отправке следующего запроса.

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

takemesage:

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$db=mysqli_connect('localhost', 'root', '', 'mybase');
class Chats
  {
  var $id;
  var $user;
  var $mesage;
  }
$chat = new Chats;
$chat->user = array();
$chat->mesage = array();

$current_id=$_POST['id'];
$f=1;
while ($f)
  {
  $query="SELECT MAX(id) FROM chat";
  $result=mysqli_query($db, $query);
  $max_id=mysqli_fetch_row($result);
  if ($max_id[0]>$current_id)
    {
    $query="SELECT * FROM chat WHERE id > $current_id";
    $result=mysqli_query($db, $query);
    for ($i=0; $i<mysqli_num_rows($result); $i++)
      {
      $row=mysqli_fetch_assoc($result);
      $chat->user[]=$row['user'];
      $chat->mesage[]=$row['text'];
      }
    $chat->id=$row['id'];
    $an=json_encode($chat);
    echo $an;
    $f=0;
    }
  else
  sleep(2);
  }
mysqli_close($db);

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

4 - создаём класс объектов. Позже будет создан объект, в который будет помещён ответ сервера

6, 7, 8 - свойства класса

10 - создаём объект с названием $chat

11, 12 - свойства user и mesage делаем массивами, потому что может быть несколько новых сообщений. И каждое сообщение будет в отдельном элементе массива.

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

15 - ставим флаг отстуствия новых записей

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

18, 19, 20 - запрос на получение максимального id в тавлице

21 - если максимальный id больше, чем текущий id, значит появились новые записи

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

25 - цикл для всех записей выборки

27 - помещаем запись выбоки в массив с именем $row

28 - записываем автора очередного сообщения в свойство объекта $chat. Это свойство является массивом.

29 - записываем текст сообщения в другое свойство объекта $chat. Оно также является массивом.

31 - в свойство id записываем id сообщения. Так как это делается после завершения цикла, то это будет id последней записи в выборке.

32 - переводим объект в формат JSON и записываем в переменную $an

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

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

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

37 - скрипт ждёт 2 секунды, после чего будет произведена новая проверка

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

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