Лабораторная работа № 10. Работа с потоками. Класс TThread
На этом занятии мы с вами познакомися с потоками на примере простого приложения. Замечу сразу, что это занятие является только первым знакомством - дело в том, что многие просто не знают, как использовать потоки или боятся их. Но на самом деле ничего страшного или особо сложного в потоках нет.
Запускайте Delphi. Итак, наша программа будет представлять из себя форму с двумя edit'ами и кнопкой. Добавьте их на форму, после чего форма будет выглядеть приблизительно так:

При нажатии на кнопку будут осуществляться некоторые долгие вычисления. Если бы мы не использовали потоки, то, пока эти вычисления не закончатся, делать мы ничего бы не смогли. Надо было бы ждать. Но, так как потоки у нас будут, то во время долгих вычислений можно будет что-нибудь вводить во второй edit (он, собственно, только для этого и существует). В первый же edit наш поток будет выводить некоторые промежуточные результаты своей работы.
Добавьте в программу еще один модуль (File, New, Unit):

Внесите в окно кода нового модуля следующий код:
unit Unit2;
interface
uses
Classes;
type
TMyThread = class(TThread) //Новый класс
private
answer:Integer;
protected
procedure ShowResult;
procedure Execute; override;
end;
implementation
uses
SysUtils, Unit1;
//Процедура для вывода информации из потока
procedure TMyThread. ShowResult;
begin
Form1.Edit1.Text:=IntToStr(answer);
end;
//Длинная процедура
procedure TMyThread. Execute;
var
i:Integer;
begin
for i:=1 to 10000 do
begin
answer:=answer+1;
Synchronize(ShowResult);
end;
end;
end.
Немного комментария по коду. В нашем модуле мы вводим новый класс TMyThread как потомок TThread. В экземпляре нашего класса и будет выполняться отдельный поток программы. В классе есть процедура ShowResult для вывода информации из работающего потока в основной поток (форму) нашей программы. Кроме того, в классе есть наша версия метода Execute из родительского класса TThread. Обратите внимание, что в нашей реализации Execute мы пишем
...
Synchronize(ShowResult);
...
Тем самым наш поток что-то отправляет в основной поток программы (в данном случае значение переменной answer). Делаем мы это посредством вызова Synchronize, в котором в качестве параметра указываем имя нужной процедуры.
Теперь переходим к нашему основному модулю Unit1. Во-первых, добавьте в секцию uses ссылку на Unit2:
...
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Unit2;
...
Во-вторых, напишите обработчик для нажатия кнопки:
procedure TForm1.Button1Click(Sender: TObject);
var
MyThread: TMyThread;
begin
MyThread:=TMyThread. Create(False);
end;
Тут мы создаем второй поток для нашего приложения. Параметр False означает, что метод Execute для нашего потока вызовется немедленно.
Запускайте программу. Нажимайте на кнопку. В первом edit'е замелькают промежуточные результаты работы второго потока. Во время его работы вы можете вводить информацию во второй edit - т. е. работа одного потока не мешает работе другого.
Работа с сетью. Класс TServerSocket
Компонент TServerSocket расположен на вкладке Internet:

Перенесите его на форму. Добавьте еще кнопку (по ней наш сервер начнет слушать сеть) и текстовое поле (в нем будут показываться полученные сервером от клиента данные). Наша форма будет выглядеть приблизительно так:

Начинаем задавать свойства. Для серверного сокета устанавливаем порт в 3000. Можно установить и другой - это не принципиально. Важно только, чтобы порт не оказался занятым кем-нибудь другим. Этот же номер порта мы будем использовать и на клиенте. Далее измените свойство Caption для кнопки на "Слушать" и свойство Text для текстового поля Edit1 установите в "" (пустую строку).
В коде для кнопки пишем
procedure TForm1.Button1Click(Sender: TObject);
begin
ServerSocket1.Active:=true;
end;
Т. е. при нажатии на кнопку сервер начинает слушать сеть на 3000 порту.
Далее пишем обработчик для полученных данных. Для этого в Инспекторе Объектов выбираем из combobox'а ServerSocket1, переходим на вкладочку Events и в списке событий делаем двойной щелчок на правой части строки OnClientRead:

В созданную заготовку пишем следующий код:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Edit1.Text:=Socket. ReceiveText;
end;
В этом коде мы пишем полученные от клиента данные в текстовое поле. Для этого мы используем передаваемый в наш обработчик параметр Socket типа TCustomWinSocket.
Добавим еще код для показа того, что клиент подсоединился к нашему серверу. Для этого мы создаем аналогично предыдущей заготовку для события OnClientConnect нашего сервера и добавляем в нее следующий код:
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Form1.Caption:='Клиент подсоединится';
end;
Т. е. при подсоединении клиента к нашему серверу у формы поменятся заголовок.
Работа с сетью. Класс TClientSocket
На прошлом уроке мы сделали одну чась нашего сетевого приложения - сервер на основе компонента TServerSocket. Это занятие мы посвятим созданию второй части нашего приложения. Естественно, что это будет клиент и для него мы будем использовать компонент TClientSocket.
Создайте новый проект на Delphi. На форме разместите компонент TClientSocket с вкладки Internet Палитры компонентов:

Изменим для него ряд свойств, а именно свойство Address установите в 127.0.0.1, свойство Host - в localhost, Port - 3000. Вообще говоря эти параметры задают компьютер, к которому будет подсоединяться наш клиент. Задавать его можно либо по IP-адресу (мы тут написали 127.0.0.1 - это IP-адрес нашего компьютера), либо по имени (localhost - это универсальное имя для нашего компьютера). Если у вас есть доступ к сети, то можете указать и что-нибудь другое (разумеется там должен быть запущен наш сервер). В качестве порта мы должны указать тоже значение, которое мы задали для сервера на прошлом уроке.
Добавьте еще на форму две кнопки. Для первой измените ее заголовок на "Подсоединиться" (по ней произойдет подсоединение к нашему серверу) и для второй - на "Отправить" (по ней мы будем отправлять некоторые данные на сервер). Добавьте на форму также одно текстовоее поле - введенная в нем информация и будет отправляться на сервер. Свойство Text для текстового поля установите в "" (пустую строку).
Наша форма должна выглядеть приблизительно так:

В обработчике для первой кнопки (с заголовком "Подсоединиться") пишем:
procedure TForm1.Button1Click(Sender: TObject);
begin
ClientSocket1.Active:=true;
end;
В этом коде мы просто делаем наш сокет активным. Так как у него установлены свойства Address, Host и Port, то произойдет соединение с соответствующим сервером.
В обработчике для второй кнопки пишем:
procedure TForm1.Button2Click(Sender: TObject);
begin
ClientSocket1.Socket. SendText(Edit1.Text);
end;
В этом коде мы отсылаем данные из текстового поля Edit1.
Все! Можно приступать к испытаниям. Для этого сначала запустите написанный на прошлом уроке сервер и нажмите в нем на кнопку "Слушать". После этого запустите клиента и нажмите в нем на кнопку "Подсоединиться". Обратите внимание, что у сервера поменяется заголовок:

Теперь в текстовом поле клиента введите некоторый текст и нажмите на кнопку "Отправить". Этот текст передастся на сервер и покажется там в текстовом поле:

Задание
Написать приложение, реализующее обмен сообщениями между двумя компьютерами в сети. Одно и то же приложение должно уметь и отправлять и принимать сообщения.


