Delphi: сетевое программирование. Разработка приложения "клиент-сервер" в Delphi

Откроем Delphi и по команде File/Save Project As… сохраним проект в той же папке, где мы сохранили базу данных. Адрес папки C:\БДIBExpert . Сохраним pas файл под именем TelSprav1.pas , а dpr файл TelSprav.dpr . В этом окне в свойстве Caption напишем «Телефонный справочник », в свойстве Name пишем «TelefonSprav». Кинем на форму компонент MainMenu и дважды щёлкнем по нему, появится окно (рис. 50).

Рисунок 50

В свойстве Caption напишем «Файл », в свойстве Name пишем «Fille ». Щёлкнем кнопкой мыши по синему прямоугольнику, снизу появится другой прямоугольник (рис. 51).

Рисунок 51

Выделим его. В свойстве Caption напишем «Закрыть », в свойстве Name пишем «Zakrit ». Выделим прямоугольник справа. В свойстве Caption напишем «Редактирование », в свойстве Name пишем «Redaktirov » и так далее. В результате у нас должны получится примерно вот такие подменю (рис. 52, рис 53, рис 54).

Рисунок 52

Рисунок 53

Рисунок 54

Теперь бросим на форму компонент ToolBar , с вкладки Win32. Свойство Align выставим в alTop. В свойстве EdgeBorders выставим всё в True. Данный компонент позволяем нам ставить на нём кнопки. В этом и заключается его основная роль. Щёлкнем по нему правой кнопкой мыши и в появившемся подменю выберем NewButton (рис. 55).

Рисунок 55

Появится кнопка, для которой нужно будет выбрать картинку. Но сначала в свойстве Name пишем «Dob ». Чтобы отобразить картинку надо, с этой же вкладки, бросить компонент ImageList и дважды щёлкнуть по нему. Появится диалоговое окно (рис. 56), в котором нажатием кнопки Add… нужно выбрать соответствующие картинки размером 16х16. После того как все картинки выбраны, нажмём кнопку OK.

Рисунок 56

Выделим компонент ToolBar1 и в свойстве Imeges в ниспадающем меню выберем ImageList1. На кнопке сразу появится картинка. Если картинка должна быть другая, то нужно выделить кнопку и в свойстве ImageIndex в ниспадающем меню выбрать подходящую.

Нажмём правой кнопкой мыши по компоненту ToolBar1 и в появившемся подменю выберем NewSeparator (рис. 55). Около кнопки появится разделитель. Не будем для него изменять свойство Name, т.к. это всего лишь разделитель между кнопками. Лучше изменим его ширину Width. Сделаем его равным 5. Точно так же создаём ещё три кнопки. Во второй кнопке в свойстве Name пишем «Red ». В третьей «Ydali ». В четвёртой «Exiit ». Между кнопками ставим разделитель NewSeparator. Поставим ещё один разделитель шириной равной примерно 50. Бросим около разделителя компонент Label и в свойстве Caption пишем Фамилия. Рядом с компонентом Label1 поставим компонент Edit, в свойстве Name пишем «Familiya ». Между компонентами, Label и Edit , роль разделителя играет пробел в набранном слове Фамилия. Бросим ещё пару компонентов Label и Edit. У последнего в свойстве Name выставим «Telefon ». У обоих компонентов Edit надо очистить свойство Text. Рядом с компонентом для ввода телефона (Edit) расположим компонент Button, в свойстве Name введём Naiti.

Теперь с вкладки Data Controls надо бросить на форму компонент DBGrid. Растяните его по всему окну, свойство Align выставим в alClient. У нас должно поучиться нечто подобное (рис. 57).

Рисунок 57

Внешний вид главного окна построен.

Компоненты для доступа к базе данных мы разместим в отдельном модуле. Выполним команду File/New/Other… и в появившемся окне (рис. 58) выберем DataModule, нажмём кнопку OK. Сохраним в той же папке, с таким же именем DataModule1.pas и DataModule.dpr.

Рисунок 58

В свойстве Name пишем название короче DM . Вот в этом окне и будут размещаться компоненты для доступа к БД. С вкладки InterBase разместим следующие компоненты: IBDatabase, IBStoredProc, IBQuery, IBTransaction. Оставим без изменения их названия, т.к. мы их в окно бросили первыми. Они будут расположены с цифрой 1 в конце. С вкладки Data Access бросим компонент DataSource в ниспадающем списке DataSet выберем IBQuery1.

Сейчас распишем, какую роль играет каждый компонент:

  • IBDatabase - центральный компонент для соединения с базой данных. Один компонент может быть связан только с одной базой данных в конкретный момент времени;
  • IBStoredProc – компонент предназначен для работы с хранимыми процедурами. Позволяет выполнять их, подавать входные данные и получать результат выполнения (выходные данные);
  • IBQuery - позволяет отправлять запросы к базе данных и получать результат их выполнения. Компонент является полным аналогом компонента TQuery. Основное свойство SQL;
  • IBTransaction - отвечает за работу всех транзакций для связанной с данным компонентом базой данных;
  • DataSource - Является связующим звеном между наборами данных, представленных компонентами TIBQuery и TIBStoredProc, и визуальными компонентами отображения и управления данными.
Не будем расписывать каждое из свойств во всех этих компонентах. В процессе создания БД, нам в любом случае придётся, познакомится со свойством, которое мы будем использовать.

Итак, щёлкнем правой кнопкой мыши по компоненту IBDatabase, вылезет контекстное меню (рис. 59).

Рисунок 59

В котором надо выбрать пункт Database Editor… Появится окно Database Component Editor (рис. 60).

Рисунок 60

В разделе Connection выберем Local, т.к. мы пока создаём локальную баз данных. В поле Database нужно ввести имя нашей БД. Можно конечно набрать всё ручками, но удобнее нажать кнопку Browse. Если сейчас залезть в папку с нашей базой данных (TELEFONSPRAVOCHNIK.FDB), то она в этой папке отображаться не будет. Для того чтобы выбрать эту БД необходимо в строке Тип файлов выбрать AllFiles(*.*). Вот теперь все файлы, из этой папки, отобразятся. Выберем файл БД (TELEFONSPRAVOCHNIK.FDB) и щёлкнем OK.

В разделе Database Parameters в поле User Name введём логин – SYSDBA, в поле PassWord вводим пароль – masterkey. Логин и пароль должны быть обязательно такими же, какими вводились при создании БД в IBExpert. Управлять, конечно, мы будем базой данных с помощью программы написанной в Delphi.

В ниспадающем списке Character Set выберем Win1251. Уберём галочку Login Prompt, чтобы каждый раз не вводить пароль. Справа в окне Settings отображается всё, что мы обозначили и ввели.

Теперь нужно нажать кнопку Test. Прежде чем нажать кнопку OK, надо протестировать БД. Появится окно (рис. 61).

Рисунок 61

Если появилось с сообщением об ошибке, то одна из причин может быть – несовместимость с программой InterBase. Для устранения этой ошибки необходимо надо удалить программу InterBase, если она установлена. И обязательно нужно вручную удалить файл библиотеки gds32.dll по адресу C:\WINDOWS\system32.

После этих удалений придётся удалить и FireBird, т.к. мы удалили библиотеку, IBExpert не сможет уже нормально работать. После всех этих действий необходимо переустановить FireBird. Причин ошибки несколько, но эта самая распространённая.

Выделим компонент IBQuery1 и в свойстве Database выберем IBDatabase1. Свойство Database указывает компонент TIBDatabase, связанный с данным набором данных. В свойстве Transaction выбираем IBTransaction1. Свойство Transaction отвечает за выбор транзакции.

Основное свойство компонента TQuery SQL, имеющего тип TStrings . Этот список строк, содержащих запросы SQL, который покажет, с какой таблицей или таблицами будет проводиться работа. Но далее во время выполнения приложения свойство SQL может формироваться программно методами, обычными для класса TStrings : Clear – очистка, Add – добавление строки и т.д.

Итак в свойстве SQL щёлкнем кнопку с тремя точками и в появившемся окне введём SQL запрос (рис. 62).

Рисунок 62

Запрос звучит так – выбрать все поля (SELECT * ) из таблицы TELEPHON_SPRAVOCCHNIK (FROM TELEPHON_SPRAVOCCHNIK) и отсортировать (по возрастанию) по полю KEY (ORDER BY KEY). Нажимаем кнопку OK.

Выделим компонент IBTransaction1 и в свойстве DefaultDatabase выберем IBDatabase1 .

Выделим компонент IBStoredProc1, в свойстве Database выберем IBDatabase1 . В свойстве StoredProcName из ниспадающего списка выберем нашу процедуру TEL_SPRAV_PROC написанную в IBExpert.

Выделим IBQuery1, свойство Active выставим в True .

Подключим к данному модулю (DataModule1) главный модуль TelSprav1 , выполнив команду File/Use Unit…, в появившемся окне (рис. 63.)

Рисунок 63

выберем TelSprav1 и нажмём кнопку Use Unit. После слова implementation в данном модуле вылезет раздел uses с именем главного модуля TelSprav1. Вот теперь модуль DataModule1 знает о существовании главного модуля TelSprav1.

Перейдём в главный модуль TelSprav1 и теперь к нему подключим модуль DataModule1. Сейчас эти два модуля знают о существовании друг друга.

Выделим сетку DBGrid1 и в ниспадающем меню свойства DataSource выбираем DM.DataSource1 . В нашей сетке появятся имена полей БД.

Теперь создадим окно для добавления данных и их редактирования. Выполним команду File/New/Form. Первым делом сохраним форму по команде File/Save Project As… В поле Имя файла модуля введём DobavRedakt1.pas , а Имя формы - DobavRedakt.dpr . В свойстве Name ведём имя формы DobRed. Кинем на форму необходимые, для ввода и редактирования данных, компоненты:

  1. TEdit - для ввода фамилии. В свойстве Name пишем Familiya. В свойстве Text очистим поле ввода;
  2. TEdit - для ввода имени. В свойстве Name пишем Imya. В свойстве Text очистим поле ввода;
  3. TDateTimePicker – для ввода даты. В свойстве Name пишем VData. В ниспадающем списке свойства DateMode выберем dmUpDown, если хотите, можете выбрать другое значение;
  4. TEdit - для ввода номера телефона. В свойстве Name пишем NomerTelefona. В свойстве Text очистим поле ввода;
  5. TEdit - для ввода электронного адреса. В свойстве Name пишем EMail. В свойстве Text очистим поле ввода;
  6. TCheckBox – признак принадлежности сотового телефона. Если во время исполнения программы поставить галочку, то сотовый телефон есть у данного человека. В свойстве Caption пишем Мобильник. В свойстве Name пишем Mobilnik;
  7. TComboBox – ниспадающий список для выбора города. В свойстве Name пишем Gorod. В свойстве Text очистим поле ввода;
  8. TButton – кнопка для подтверждения добавления данных. В свойстве Name пишем Dobavit. В свойстве Caption пишем OK;
  9. TButton – кнопка для подтверждения редактирования данных. В свойстве Name пишем Redact. В свойстве Caption пишем OK.
Мы кинули на форму 2 одинаковые кнопки, потому что, нажатие отдельной кнопки, и будет определять какое действие должно произвестись над данными. Конечно это способ нерациональный, но зато эффективный.

Сохраним форму. Закроем проект и снова его откроем. После открытия проекта нам нужно увидеть вкладку DobavRedakt1 (Рис. 64).

Рисунок 64

Выполним команду Project/Add to Project… и в появившемся окне (рис. 65).

Рисунок 65

Выберем файл DobavRedakt1.pas и нажмём кнопку OK. По этой же команде выберем файл DataModule1.pas , если у нас нет вкладки DataModule1. Данная команда применяется только в том случае, когда в окне Project Manager отсутствуют необходимые нам pas и dpr файлы. Окно Project Manager вылезет по команде View/Project/Manager…

Подключим модуль редактирования DobavRedakt1 к главному модулю TelSprav1. Так же и к модулю редактирования подключим главный модуль и DataModule1.

Ну всё! Необходимые, на данном этапе, окна созданы. Переходим непосредственно к программированию.

В обработчике события OnClick (главного модуля) для кнопки Dobavit пишем следующий код:

procedure TTelefonSprav.DabavitClick(Sender: TObject);
begin
  {если была нажата кнопка "Dobavit", то делаем
   переменную "ExiStsDobRed" активной}

     ExiStsDobRed:=true;
  //по нажатию кнопки "Dabavit" - "Tag=1"
     tag:=1;
  //делаем доступной кнопку "Dob"
     DobRed.Dobav.Visible:=true;
  //недоступной кнопку “red”
     DobRed.Redact.Visible:=false;
  //открываем окно
     DobRed.ShowModal;
end;

Объявим глобальную переменную ExiStsDobRed в разделе Var . Выглядеть это будет, в модуле, вот так.

var
  TelefonSprav: TTelefonSprav;
  ExiStsDobRed: boolean=false;

Данная переменная показывает признак открытия окна Добавления и Редактирования данных. Эта переменная логического типа и равна по умолчанию False. При нажатии на кнопку "Dobavit" переменная ExiStsDobRed равна True.

У каждого компонента есть свойство Tag, которое можно использовать, в качестве глобальной переменной целого типа, по своему усмотрению. Присваиваем 1 свойству Tag, именно по этому значению будет выходить именно та кнопка которая необходима. Например: мы нажали на кнопку Dobav, сработает событие описанное выше.

Как я уже упоминал у нас в окне добавления и редактирования данных находятся 2 кнопки Dobav, Redact (рис. 66).

Рисунок 66

На рисунке отображены две кнопки, ну а в реальности отображается только одна. Так как в четвёртой строчке кода мы кнопку Redact делаем невидимой. В третьей строчке делаем доступной кнопку Dobav. За эти действия отвечает свойство Visible. В последней строчке показываем окно для Добавления или Редактирования данных.

Итак, сначала о компоненте сервера IdTCPServer (закладка Indy Servers ). Для использования возможностей сервера этот компонент нужно поместить на форму (компонент неотображаемыи). При настройке компонента полезными являются следующие его свойства:

  • Active - активизирует или деактивизирует сервер (по умолчанию False );
  • Bindings - настраивает серверные сокеты (присоединяет их к определенно му порту компьютера, позволяет задавать диапазон IP-адресов и портов клиентов при помощи диалогового окна настройки свойства Binding ;
  • ListenQueue - численное значение, ограничивающее максимальное количество запросов на установление соединения от клиентов в очереди;
  • MaxConnections - позволяет ограничить максимальное количество клиентов, присоединенных к серверу;

Рассмотрим несколько подробнее настройку серверных гнезд с использованием свойства Bindings . Так, на рис. 1 показано, как при помощи диалогового окна свойства Binding настроить сервер на обслуживание клиентов с любыми IP-адресами, при этом серверный сокет присоединяется к порту 12340.

Рис. 1. Настройка свойства Binding .

На этом настройку сервера можно и завершить (хотя здесь используются далеко не все возможности компонента IdTCPServer ). Основная же работа сервера при обработке запросов клиентов может реализоваться в обработчике события OnExecute . В этот обработчик передается ссылка на объект TIdContext - поток, ассоциированный с клиентом, присоединенным к серверу. Посредством этого объекта (а точнее, его свойства Connection ) можно получать и отправлять данные, а также получать и устанавливать множество полезных параметров соединения. Первый пример использования объекта TIdContext при обработке запроса клиента приведен в листинге 1.

Теперь рассмотрим, как сконфигурировать клиент (IdTCPClient - закладка Indy Clients ), чтобы он был способен взаимодействовать с нашим сервером. Чтобы использовать компонент ТСР-клиента, достаточно поместить его на форму (компонент также неотображаемый).

После этого как минимум нужно настроить следующие его свойства (остальные упоминаются по мере необходимости в приведенных далее примерах):

  • Host - имя или IP-адрес компьютера, на котором запущен сервер;
  • Port - номер порта, к которому присоединен серверный сокет.

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

Простой обмен данными

В начале работы с описанными в предыдущем разделе компонентами IdTCPServer и IdTCPClient рассмотрим создание несложного клиент-серверного приложения, клиентская и серверная части которого выполняют следующие функции.

  • Клиентское приложение соединяется с сервером и отправляет ему введенную пользователем строку, ждет ответа, выводит полученный от сервера текст, отсоединяется от сервера.
  • Серверное приложение принимает строку от клиентского приложения и посылает ответ (также текстовый), после чего разрывает соединение. Плюс к этому ведется подсчет количества обслуженных клиентов и запоминается IP-адрес компьютера, с которого пришел последний запрос.

Реализация как серверного, так и клиентского приложений в нашем случае предельно проста. Проект серверного приложения называется SimpleServer . Внешний вид формы сервера (во время работы приложения) представлен на рис. 2.

Рис. 2. Внешний вид простого сервера

Текстовое поле (Edit ) с количеством обработанных запросов имеет имя txtCount , а текстовое поле с адресом последнего обслуженного компьютера названо txtFrom . Вся работа сервера заключается в обработке события Execute для компонента IdTCPServer , помещенного на форму (присоедините этот компонент к порту 12340 и установите значение свойства Active = True ) (листинг 1).

Листинг 1. Реализация простого сервера

Procedure TForm1.FormCreate(Sender: TObject); begin section:= TCriticalSection.Create; end; procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var strText: String; begin //Принимаем от клиента строку strText:= AContext.Connection.Socket.ReadLn; //Отвечаем AContext.Connection.Socket.WriteLn("Took the line: " + strText); //Обновим сведения на форме сервера (сервер многопоточный, //поэтому используем синхронизацию section.Enter; Inc(processed, 1); txtCount.Text:= IntToStr(processed); txtFrom.Text:= AContext.Connection.Socket.Binding.PeerIP; section.Leave; //Закрываем соединение с пользователем AContext.Connection.Disconnect; end;

При ответе клиенту сервер только повторяет принятую от него строку с добавлением текста "Принял: " в начало строки.

Анализируя листинг 1 , можно заметить, что даже в рассматриваемом простейшем сервере пришлось применить синхронизацию при обновлении внешнего вида формы при помощи критической секции (необходимо дополнительно добавить имя модуля SyncObjs в секцию uses ).

Теперь рассмотрим реализацию клиентской части (проект SimpleClient ). Внешний вид клиентского приложения приведен на рис. 2.

Рис. 2. Внешний вид клиента

Естественно, что для работы клиентского приложения на форму помещен экземпляр компонента IdTCPClient (его имя - IdTCPClient1 ). Свойству Port этого компонента нужно присвоить значение 12340. Текстовое поле (Edit ) для ввода строки, подлежащей отправке не сервер, имеет имя txtMessage . Текстовое поле (Edit ), в которое вводится имя или адрес сервера, названо txtServer . Поле со строками ответов (Memo ) имеет имя txtResults .

Вся работа клиентского приложения выполняется при нажатии кнопки Обработать . Текст соответствующего обработчика приведен в листинге 2 .

Листинг 2. Реализация простого клиента

Procedure TForm1.Button1Click(Sender: TObject); begin //Соединяемся с сервером и посылаем ему введенную команду IdTCPClient1.Host:= txtServer.Text; IdTCPClient1.Connect; IdTCPClient1.Socket.WriteLn(txtMessage.Text); txtMessage.Text:= ""; //Ожидаем ответ и закрываем соединение txtResults.Lines.Append(IdTCPClient1.Socket.ReadLn); IdTCPClient1.Disconnect; end;

Все, теперь можно запускать сервер и клиенты (на произвольном количестве компьютеров) и понаблюдать за результатами их работы. Только не забудьте запустить сервер до того, как будете обращаться к нему с помощью программы-клиента.

Исходный код . Выполнен на Delphi XE.

Слежение за компьютером по сети (IdTCPServer, IdTCPClient)

Теперь рассмотрим более интересный пример использования сетевых компонентов IdTCPServer и IdTCPCLient , который может быть полезен для людей, имеющих отношение к администрированию компьютеров сети.

Серверная программа предварительно запускается на наблюдаемом компьютере. В этом примере программа-сервер позволяет клиентской программе получать следующие сведения о компьютере, на котором она (программа-сервер) запущена:

  • разрешение монитора;
  • глубину цвета для монитора;
  • полноразмерную копию экрана;
  • копию экрана, уменьшенную (или увеличенную) до заданных размеров.

Для получения указанных сведений про грамма-клиент должна послать серверу следующие строковые значения:

  • get_screen_width - для получения ширины и get_screen_height -для получения высоты экрана в пикселах;
  • get_screen_colors - для получения значения установленной для монитора глубины цвета (бит на точку);
  • get_screen - для получения полноразмерной копии экрана;
  • get_screen: X, Y - для получения копии экрана, приведенной к размеру Х х Y .

Сначала рассмотрим реализацию сервера (проект SpyServer ). Весь код, обеспечивающий работу сервера, помещен в модуле Unit1.pas формы Form1 . Обработчик запросов клиентов - главная процедура для сервера - приводится в листинге 3.

Листинг 3. Обработчик клиентских запросов

Procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var strText: String; width, height, i: Integer; dc: HDC; begin //принимаем от клиента строку strText:= AThread.Connection.ReadLn; //определяем, что нужно выполнить if strText = "get_screen_height" then //возвратим высоту экрана AThread.Connection.WriteInteger(Screen.Height) else if strText = "get_screen_width" then //возвратим ширину экрана AThread.Connection.WriteInteger(Screen.Width) else if strText = "get_screen_colors" then begin //возвратим количество бит на точку dc:= GetDC(0); AThread.Connection.WriteInteger(GetDeviceCaps(dc, BITSPIXEL)); ReleaseDC(0, dc) end else if strText = "get_screen" then //возвратим полноразмерную копию экрана SendScreen(Screen.Width, Screen.Height, AThread.Connection) else begin //строка вида "get_screen:x, y" //Определим значени высоты и ширины переданные пользователем strText:= Copy(strText, 12, Length(strText) - 11); i:= Pos(",", strText); //положение запятой width:= StrToInt(Copy(strText, 1, i - 1)); height:= StrToInt(Copy(strText, i+1, Length(strText) - i)); //возвратим копию экрана SendScreen(width, height, AThread.Connection); end; end;

Используемая в листинге 3 процедура SendScreen, отправляющая клиенту копию экрана, приведена в листинге 4.

Листинг 4. Снятие копии экрана

//процедура снимает копию экрана, приводит полученное //изображение к заданному размеру и отправляет //преобразованное изображение клиентской программе procedure SendScreen(width1: Integer; height1: Integer; Connection: TIdTCPServerConnection); var ScreenCopy: TCanvas; gr: TBitmap; stream: TMemoryStream; rcDest, rcSource: TRect; begin rcDest:= Rect(0,0,width1,height1); //конечный размер изображения rcSource:= Screen.DesktopRect; //исходный размер изображения //создаем канву и присоединяем ее к контексту Рабочего стола ScreenCopy:= TCanvas.Create; ScreenCopy.Handle:= GetDC(0); //создаем объект для хранения копии экрана и копируем изображение gr:= TBitmap.Create; gr.Height:= height1; gr.Width:= width1; gr.Canvas.CopyRect(rcDest, ScreenCopy, rcSource); ReleaseDC(0, ScreenCopy.Handle); //сохраняем изображение в поток данных stream:= TMemoryStream.Create; gr.SaveToStream(stream); //отправляем изображение клиенту Connection.WriteStream(stream, true, true); stream.Clear; stream.Free; gr.Free; end;

Как можно увидеть, даже самая сложная операция рассматриваемого сервера - копирование изображения - реализуется довольно просто благодаря наличию такого стандартного класса, как TMemoryStream .

Компонент IdTCPServer (с именем IdTCPServer1 ) в этом примере присоединен к порту 12341 (не забудьте также установить свойство Active = True ).

Теперь о реализации клиентского приложения (проект SpyClient ). Внешний вид формы (Form1 ) клиента во время работы приводится на рис. 3 (видно, что пользователь наблюдаемого компьютера только что проиграл в игру Сапер).

Рис. 3

Описания, имена и значения настроенных вручную свойств самых важных компонентов формы клиента приведены в таблице 1.

Таблица 1. Основные компоненты формы клиента слежения и их свойства

Работа клиентского приложения начинается с соединения с сервером. Код, отвечающий за эту опреацию, приведен в листинге 5.

Листинг 5.Соединение с сервером

Procedure TForm1.cmbConnectClick(Sender: TObject); begin if cmbConnect.Caption = "Подключиться" then begin if txtServer.Text = "" then //не введено имя сервера MessageDlg("Введите имя машины-сервера в текстовом поле", mtInformation, , 0) else begin //подключаемся к серверу IdTCPClient1.Host:= txtServer.Text; try IdTCPClient1.Connect; except MessageDlg("Не удается соединиться с указанным сервером", mtError, , 0); Exit; end; end end else //отключаемся от сервера IdTCPClient1.Disconnect; end;

Если соединение с сервером произошло успешно, то выполняется обработчик TForm1.IdTCPClient1Connected , подготавливающий приложение-клиент к периодическим запросам данных с сервера (листинг 6).

Листинг 6. Действия выполняемые при соединении с сервером

Procedure TForm1.IdTCPClient1Connected(Sender: TObject); begin txtServer.Enabled:= False; cmbConnect.Caption:= "Отключиться"; //начинаем периодически запрашивать данные с сервера Timer1.Enabled:= True; //выполним первый запрос, не дожидаясь срабатыввания таймера Timer1Timer(nil); end;

При отсоединении от сервера также выполняются действия, прекращающие периодические запросы данных и переводящие клиент в состояние ожидания подключения (первоначальное состояние программы) (листинг 7).

Листинг 7. Действия при отсоединении от сервера

Procedure TForm1.IdTCPClient1Disconnected(Sender: TObject); begin txtServer.Enabled:= True; cmbConnect.Caption:= "Подключиться"; Timer1.Enabled:= False; end;

Самой сложной частью клиентского приложения является обработка данных, присылаемых сервером. Клиентское приложение запрашивает данные по таймеру и обрабатывает полученные данные так, как показано в листинге 8.

Procedure TForm1.Timer1Timer(Sender: TObject); var stream: TMemoryStream; begin //запрашаваем у сервера данные о наблюдаемом компьютере with (IdTCPClient1) do begin //...разрешение WriteLn("get_screen_width"); WriteLn("get_screen_height"); lblResolution.Caption:= IntToStr(ReadInteger) + "x" + IntToStr(ReadInteger); //...глубина цвета WriteLn("get_screen_colors"); lblColors.Caption:= IntToStr(ReadInteger); //...копия экрана //.....1-й вариант - копирование экрана без сжатия //WriteLn("get_screen"); //.....2-й вариант - сжатие на стороне сервера WriteLn("get_screen:" + IntToStr(imgScreen.Width) + "," + IntToStr(imgScreen.Height)); //....получаем данные stream:= TMemoryStream.Create; ReadStream(stream); stream.Position:= 0; //....формируем изображение imgScreen.Picture.Bitmap.LoadFromStream(stream); stream.Clear; stream.Free; end; end;

В тексте листинга 8 создано большое количество комментариев, поэтому дополнительно пояснять его нет смысла. Остановимся лишь на том, зачем в процедуре TForm1.Timer1Timer предусмотрено два варианта получения изображения с сервера.

Все дело в том, что сжатие (в нашем примере разрешение экрана наблюдаемого компьютера больше размера компонента imgScreen ) на стороне сервера требует от компьютера, на котором запущено серверное приложение, большего процессорного времени на снятие копии экрана. Это снижает нагрузку на сеть при передаче изображения, а также экономит ресурсы компьютера-клиента. Но качество сжатого изображения в этом случае получается несколько хуже, чем когда мы предоставляем компоненту Image возможность масштабировать изображение самостоятельно.

Если же не использовать сжатие изображения на сервере, возрастает нагрузка на сеть при передаче полноразмерной копии экрана, а вся работа по сжатию изображения возлагается на компонент imgScreen (то есть дополнительно тратится процессорное время на компьютере клиента). При большом разрешении экрана наблюдаемого компьютера (или при наблюдении сразу за несколькими компьютерами) машина клиента, если она недостаточно мощная, может начать весьма ощутимо "тормозить". Качество сжатого изображения при этом получается более высоким.

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

Исходный код . Выполнен на Delphi 7.

Итак создадим новую БД, назовем ее Test и сохраним ее в папке C:\ClientServer\Server\Data (процесс создания новой БД в Аксесе я, по понятным, причинам опускаю, и приведу только структуру таблиц - входящих в нашу базу).

1. Таблица первая, назовем ее First

2. Таблица вторая, а назовем ее уже по хитрому - Second

Ну вот, как бы, с базой и все.

Программа

2.1. Приложение сервер.

Созданим новое приложение и сохраним его под именем Server в папке C:\ClientServer\Server. Добавим в приложение Remote Data Module с вкладки Multitier репозитария (Рис. 1).

Рис. 1 Репозитарий с выделенным Remote Data Module.

При добавлении RDM мастер добавления запросит параметры данного модуля - там введем имя нашего сервера "Test".


Все остальное оставим без изменений. После нажатия кнопки "Ок" в проекте появится форма подобная обычному дата модулю с именем Test. Сохраним ее под именем RDMFrm.pas .

Выложим на нее компоненты ADOConnection (одна штука), ADOTable и DataSetProvider (по две штуки). Компоненты ADOTable и DataSetProvider обзовем - adotFirst, adotSecond, dspFirst и dspSecond соответственно. По двойному клику на компоненте ADOConnection вывалится мастер Connection String. Выбераем пункт "Use Connection String" и нажимаем кнопочку Build. В появившемся окне "Свойства связи с данными" выберем пункт "Microsoft Jet 4.0 OLE DB Provider" и нажмем кнопку "Далее". На владке "Подключение" введем путь к БД, нажмем кнопочку "Проверить подключение" и получим сообщение что проверка подключения выполнена. После чего закроем мастер путем нажатия на кнопке ОК. Еще у компонента ADOConnection свойство есть LoginPrompt, которое установим в False и Connected - True. У компонетов ADOTable свойство Connection установим равным ADOConnection путем выбора из выпадающего списка. Свойство TableName установим в First и Second соответсвенно. Свойство CursorType - ctDynamic, свойство TableDirect - True. Вызвать FieldsEditor и добавить туда все поля.

У компонентов DataSetProvider:

Вот на этом процесс создания примитивного MIDAS сервера можно считать завершенным. Чтобы зарегистрировать сервер в подсистеме DCOM нужно запустить приложение с параметром /regserver - Server.exe /regserver.
И последний штрих, это приложение СокетСервер от Борланда, которое находится в папке Delphi?/Bin/scktsvr.exe. Для того чтобы клиент мог видеть сервер нужно запустить scktsvr.exe (если у вас установлена Win NT/w2k/XP то можно это приложение зарегистрировать как службу, для этого его нужно запустить с параметром scktsvr.exe /install)

2.2. Приложение клиент

Создадим новое приложение, и сохраним его в папке C:\ClientServer\Client под именем Client. Разместим на главной форме по два компонента ClientDataSet(обзовем их cdsFirst, cdsSecond), DataSource(обзовем их dsFirst, dsSecond), DBGrid(обзовем их dbgFirst, dbgSecond) один компонент SocetConnection.

SocetConnection

Если вы все сделали правильно, то при установке свойства Connected в True, должно запустится приложение сервер.

ClientDataSet

Запустим Редактор Полей (двойной клик по компоненту) и занесем туда все поля. Для компонентов ClientDataSet напишем обработчик sdsAfterPost

Procedure TMainForm.cdsAfterPost(DataSet: TDataSet);
begin
with (DataSet as TClientDataSet) do begin
if State in then
try
ApplyUpdates(0);
Refresh;
except
raise EDatabaseError.Create("Ошибочка вышла");
end;
end;
end;

И подставим его на обработку события AfterPost обоих ClientDataSet"ов.

DataSource

Свойство Значение
DataSet cdsFirst и cdsSecond соответственно

DBGrid

Свойство Значение
DataSource dsFirst и dsSecond соответственно

Запустим Редактор колонок (двойной клик по компоненту) и занесем туда все поля.
Положим на форме возле Гридов по кнопке и в обработчик onClick запишем:

  • для dbgFirst - cdsFirst.Post
  • dbgSecond - cdsSecond.Post

Все. Закрываем Делфи (для чистоты эксперимента). И запускаем приложение клиент. Если вы все сделали правильно то вы должны увидеть нечто подобное этому


Если статья получит отзывы и заслужит интерес у читателей, то продолжение не заставит себя долго ждать.

Введение

Данная статья посвящена созданию приложений архитектуры клиент/сервер в Borland Delphi на основе сокетов ("sockets" - гнезда ). В отличие от предыдущей статьи на тему сокетов, здесь мы разберем создание серверных приложений.

Следует сразу заметить, что для сосуществования отдельных приложений клиента и сервера не обязательно иметь несколько компьютеров. Достаточно иметь лишь один, на котором Вы одновременно запустите и сервер, и клиент. При этом нужно в качестве имени компьютера, к которому надо подключиться, использовать хост-имя localhost или IP-адрес - 127.0.0.1 .

Итак, начнем с теории. Если Вы убежденный практик (и в глаза не можете видеть всяких алгоритмов), то Вам следует пропустить этот раздел.

Алгоритм работы сокетного сервера

Что же позволяет делать сокетный сервер?.. По какому принципу он работает?.. Сервер, основанный на сокетном протоколе, позволяет обслуживать сразу множество клиентов. Причем, ограничение на их количество Вы можете указать сами (или вообще убрать это ограничение, как это сделано по умолчанию). Для каждого подключенного клиента сервер открывает отдельный сокет, по которому Вы можете обмениваться данными с клиентом. Также отличным решением является создание для каждого подключения отдельного процесса (Thread).

Разберем схему подробнее:

  • Определение св-в Port и ServerType - чтобы к серверу могли нормально подключаться клиенты, нужно, чтобы порт, используемый сервером точно совпадал с портом, используемым клиентом (и наоборот). Свойство ServerType определяет тип подключения (подробнее см.ниже);
  • Открытие сокета - открытие сокета и указанного порта. Здесь выполняется автоматическое начало ожидания подсоединения клиентов (Listen );
  • Подключение клиента и обмен данными с ним - здесь подключается клиент и идет обмен данными с ним. Подробней об этом этапе можно узнать ниже в этой статье и в статье про сокеты (клиентская часть);
  • Отключение клиента - Здесь клиент отключается и закрывается его сокетное соединение с сервером;
  • Закрытие сервера и сокета - По команде администратора сервер завершает свою работу, закрывая все открытые сокетные каналы и прекращая ожидание подключений клиентов.

Следует заметить, что пункты 3-4 повторяются многократно, т.е. эти пункты выполняются для каждого нового подключения клиента.

Примечание : Документации по сокетам в Дельфи на данный момент очень мало, так что, если Вы хотите максимально глубоко изучить эту тему, то советую просмотреть литературу и электронную документацию по Unix/Linux-системам - там очень хорошо описана теория работы с сокетами. Кроме того, для этих ОС есть множество примеров сокетных приложений (правда, в основном на C/C++ и Perl).

Краткое описание компонента TServerSocket

Здесь мы познакомимся с основными свойствами, методами и событиями компонента TServerSocket .

Свойства Методы События
Socket - класс TServerWinSocket, через который Вы имеете доступ к открытым сокетным каналам. Далее мы рассмотрим это свойство более подробно, т.к. оно, собственно и есть одно из главных. Тип: TServerWinSocket ;
ServerType - тип сервера. Может принимать одно из двух значений: stNonBlocking - синхронная работа с клиентскими сокетами. При таком типе сервера Вы можете работать с клиентами через события OnClientRead и OnClientWrite . stThreadBlocking - асинхронный тип. Для каждого клиентского сокетного канала создается отдельный процесс (Thread). Тип: TServerType ;
ThreadCacheSize - количество клиентских процессов (Thread), которые будут кэшироваться сервером. Здесь необходимо подбирать среднее значение в зависимости от загруженности Вашего сервера. Кэширование происходит для того, чтобы не создавать каждый раз отдельный процесс и не убивать закрытый сокет, а оставить их для дальнейшего использования. Тип: Integer ;
Active - показатель того, активен в данных момент сервер, или нет. Т.е., фактически, значение True указывает на то, что сервер работает и готов к приему клиентов, а False - сервер выключен. Чтобы запустить сервер, нужно просто присвоить этому свойству значение True . Тип: Boolean ;
Port - номер порта для установления соединений с клиентами. Порт у сервера и у клиентов должны быть одинаковыми. Рекомендуются значения от 1025 до 65535, т.к. от 1 до 1024 - могут быть заняты системой. Тип: Integer ;
Service - строка, определяющая службу (ftp , http , pop , и т.д.), порт которой будет использован. Это своеобразный справочник соответствия номеров портов различным стандартным протоколам. Тип: string ;
Open - Запускает сервер. По сути, эта команда идентична присвоению значения True свойству Active ;
Close - Останавливает сервер. По сути, эта команда идентична присвоению значения False свойству Active .
OnClientConnect - возникает, когда клиент установил сокетное соединение и ждет ответа сервера (OnAccept );
OnClientDisconnect - возникает, когда клиент отсоединился от сокетного канала;
OnClientError - возникает, когда текущая операция завершилась неудачно, т.е. произошла ошибка;
OnClientRead - возникает, когда клиент передал берверу какие-либо данные. Доступ к этим данным можно получить через пеаедаваемый параметр Socket: TCustomWinSocket ;
OnClientWrite - возникает, когда сервер может отправлять данные клиенту по сокету;
OnGetSocket - в обработчике этого события Вы можете отредактировать параметр ClientSocket ;
OnGetThread - в обработчике этого события Вы можете определить уникальный процесс (Thread) для каждого отдельного клиентского канала, присвоив параметру SocketThread нужную подзадачу TServerClientThread;
OnThreadStart , OnThreadEnd - возникает, когда подзадача (процесс, Thread) запускается или останавливается, соответственно;
OnAccept - возникает, когда сервер принимает клиента или отказывает ему в соединении;
OnListen - возникает, когда сервер переходит в режим ожидания подсоединения клиентов.

TServerSocket.Socket (TServerWinSocket)

Итак, как же сервер может отсылать данные клиенту? А принимать данные? В основном, если Вы работаете через события OnClientRead и OnClientWrite , то общаться с клиентом можно через параметр ClientSocket (TCustomWinSocket). Про работу с этим классом можно прочитать в статье про клиентские сокеты, т.к. отправка/посылка данных через этот класс аналогична - методы (Send/Receive)(Text,Buffer,Stream). Также и при работе с TServerSocket.Socket. Однако, т.к. здесь мы рассматриваем сервер, то следует выделить некоторые полезные свойства и методы:

  • ActiveConnections (Integer ) - количество подключенных клиентов;
  • ActiveThreads (Integеr ) - количество работающих процессов; Connections (array ) - массив, состоящий из отдельных классов TClientWinSocket для каждого подключенного клиента. Например, такая команда:
    ServerSocket1.Socket.Connections.SendText("Hello!");
    отсылает первому подключенному клиенту сообщение "Hello!". Команды для работы с элементами этого массива - также (Send/Receive)(Text,Buffer, Stream);
  • IdleThreads (Integer ) - количество свободных процессов. Такие процессы кэшируются сервером (см. ThreadCacheSize );
  • LocalAddress , LocalHost , LocalPort - соответственно - локальный IP-адрес, хост-имя, порт;
  • RemoteAddress , RemoteHost , RemotePort - соответственно - удаленный IP-адрес, хост-имя, порт;
  • Методы Lock и UnLock - соответственно, блокировка и разблокировка сокета.

Практика и примеры

А теперь рассмотрим вышеприведенное на конкретном примере. Скачать уже готовые исходники можно, щелкнув .

Итак, рассмотрим очень неплохой пример работы с TServerSocket (этот пример - наиболее наглядное пособие для изучения этого компонента). В приведенных ниже исходниках демонстрируется протоколирование всех важных событий сервера, плюс возможность принимать и отсылать текстовые сообщения:

Пример 1. Протоколирование и изучение работы сервера, посылка/прием сообщений через сокеты.

{... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1} {Полный исходник смотри } procedure TForm1.Button1Click(Sender: TObject); begin {Определяем порт и запускаем сервер} ServerSocket1.Port:= 1025; {Метод Insert вставляет строку в массив в указанную позицию} Memo2.Lines.Insert(0,"Server starting"); ServerSocket1.Open; end; procedure TForm1.Button2Click(Sender: TObject); begin {Останавливаем сервер} ServerSocket1.Active:= False; Memo2.Lines.Insert(0,"Server stopped"); end; procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь сервер "прослушивает" сокет на наличие клиентов} Memo2.Lines.Insert(0,"Listening on port "+IntToStr(ServerSocket1.Port)); end; procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь сервер принимает клиента} Memo2.Lines.Insert(0,"Client connection accepted"); end; procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь клиент подсоединяется} Memo2.Lines.Insert(0,"Client connected"); end; procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь клиент отсоединяется} Memo2.Lines.Insert(0,"Client disconnected"); end; procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin {Произошла ошибка - выводим ее код} Memo2.Lines.Insert(0,"Client error. Code = "+IntToStr(ErrorCode)); end; procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); begin {От клиента получено сообщение - выводим его в Memo1} Memo2.Lines.Insert(0,"Message received from client"); Memo1.Lines.Insert(0,"> "+Socket.ReceiveText); end; procedure TForm1.ServerSocket1ClientWrite(Sender: TObject; Socket: TCustomWinSocket); begin {Теперь можно слать данные в сокет} Memo2.Lines.Insert(0,"Now can write to socket"); end; procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer; var ClientSocket: TServerClientWinSocket); begin Memo2.Lines.Insert(0,"Get socket"); end; procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread); begin Memo2.Lines.Insert(0,"Get Thread"); end; procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread end"); end; procedure TForm1.ServerSocket1ThreadStart(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread start"); end; procedure TForm1.Button3Click(Sender: TObject); var i: Integer; begin {Посылаем ВСЕМ клиентам сообщение из Edit1} for i:= 0 to ServerSocket1.Socket.ActiveConnections-1 do begin ServerSocket1.Socket.Connections[i].SendText(Edit1.Text); end; Memo1.Lines.Insert(0,"< "+Edit1.Text); end;

Приемы работы с TServerSocket (и просто с сокетами)

Хранение уникальных данных для каждого клиента.

Наверняка, если Ваш сервер будет обслуживать множество клиентов, то Вам потребуется хранить какую-либо информацию для каждого клиента (имя, и др.), причем с привязкой этой информации к сокету данного клиента. В некоторых случаях делать все это вручную (привязка к handle сокета, массивы клиентов, и т.д.) не очень удобно. Поэтому для каждого сокета существует специальное свойство - Data . На самом деле, Data - это всего-навсего указатель. Поэтому, записывая данные клиента в это свойство будьте внимательны и следуйте правилам работы с указателями (выделение памяти, определение типа, и т.д.)!

Посылка файлов через сокет.

Здесь мы рассмотрим посылку файлов через сокет (по просьбе JINX-а) :-). Итак, как же послать файл по сокету? Очень просто! Достаточно лишь открыть этот файл как файловый поток (TFileStream) и отправить его через сокет (SendStream)! Рассмотрим это на примере:

Нужно заметить, что метод SendStream используется не только сервером, но и клиентом (ClientSocket1.Socket.SendStream(srcfile) )

Почему несколько блоков при передаче могут обьединяться в один

Это тоже по просьбе JINX-а:-). За это ему огромное спасибо! Итак, во-первых, надо заметить, что посылаемые через сокет данные могут не только объединяться в один блок, но и разъединяться по нескольким блокам. Дело в том, что сокет - обычный поток, но в отличие, скажем, от файлового (TFileStream), он передает данные медленнее (сами понимаете - сеть, ограниченный трафик, и т.д.). Именно поэтому две команды:
ServerSocket1.Socket.Connections.SendText("Hello, ");
ServerSocket1.Socket.Connections.SendText("world!");
совершенно идентичны одной команде:
ServerSocket1.Socket.Connections.SendText("Hello, world!");

И именно поэтому, если Вы отправите через сокет файл, скажем, в 100 Кб, то тому, кому Вы посылали этот блок, придет несколько блоков с размерами, которые зависят от трафика и загруженности линии. Причем, размеры не обязательно будут одинаковыми. Отсюда следует, что для того, чтобы принять файл или любые другие данные большого размера, Вам следует принимать блоки данных, а затем объединять их в одно целое (и сохранять, например, в файл). Отличным решением данной задачи является тот же файловый поток - TFileStream (либо поток в памяти - TMemoryStream). Принимать частички данных из сокета можно через событие OnRead (OnClientRead), используя универсальный метод ReceiveBuf . Определить размер полученного блока можно методом ReceiveLength . Также можно воспользоваться сокетным потоком (см. статью про TClientSocket). А вот и небольшой примерчик (приблизительный):

Как следить за сокетом

Это вопрос сложный и требует долгого рассмотрения. Пока лишь замечу, что созданный Вашей программой сокет Вы можете промониторить всегда:-). Сокеты (как и большинство объектов в Windows) имеют свой дескриптор (handle), записанный в свойстве Handle. Так вот, узнав этот дескриптор Вы свободно сможете управлять любым сокетом (даже созданным чужой программой)! Однако, скорее всего, чтобы следить за чужим сокетом, Вам придется использовать исключительно функции WinAPI Sockets.

Эпилог

В этой статье отображены основные приемы работы с компонентом TServerSocket в Дельфи и несколько общих приемов для обмена данными по сокетам. Если у Вас есть вопросы - скидывайте их мне на E-mail: [email protected] , а еще лучше - пишите в конференции этого сайта (Delphi. Общие вопросы), чтобы и другие пользователи смогли увидеть Ваш вопрос и попытаться на него ответить!

Карих Николай (Nitro ). Московская область, г.Жуковский

Сегодня остается весьма актуальной тема в сфере информационных технологий, посвященная разработке и развертыванию архитектуры «клиент - сервер». Связано это с тем, что информационные системы различных масштабов находят все большее применение в различных областях. Кроме того, структура любой базы данных создается по определенным параметрам, причем таким образом, что работать с ней в штатном режиме возможно только с применением специальных приложений. Поэтому реализация архитектуры «клиент - сервер» - очень важная задача, возникающая перед отделами информационных технологий на различных объектах, успешное решение которой позволит работать предприятию оптимально и бесперебойно.

Сервер на основе сокетов

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

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

Схема работы архитектуры «Клиент - сервер»

Рассмотрим более подробно схему реализации архитектуры, последовательно анализируя часто используемые определения:

1. Свойства ServerType и Port. Для того чтобы пользователь имел возможность подключиться к серверу, важно гарантировать, чтобы порт, используемый как сервером, так и клиентом совпадал. Именно назначение данного параметра производится в свойстве Port. Тип подключения определяется параметром ServerType.

2. Процесс открытия сокета. Для начала сессии «клиент - сервер» важно открыть сокет и порт. Используется свойство Listen.

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

Применение TServerSocket в приложениях на языке Delphi «Клиент - сервер»

Для отправления и приема информации от пользователя используются события OnClientWrite и OnClientRead. При этом взаимодействовать с пользователем можно через такой параметр, как ClientSocket. При работе часто используются следующие методы и свойства:

Количество подключенных в текущий момент пользователей;

Число активных процессов;

Число свободных процессов;

Порт, хост-имя и локальный IP-адрес;

Разблокировка и блокировка сокета.

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

Документация

На сегодняшний день литературы по разработке приложений «клиент - сервер» Delphi, связанной с разработкой и применением сокетов, представлено очень мало. Поэтому для детального изучения данного аспекта можно обратиться к документации по Linux и Unix-системам, т.к. в ней достаточно подробно рассматривается технология развертывания приложений с применением сокетов, однако, как правило, на языках Perl или С++.