Разработка клиент-серверного приложения на Delphi. Часть 3

Автор: Беднягин Павел

25 ноября 2010

В предыдущей статье мы создали клиент-серверное приложение при помощи компонентов TSofteriumHTTPController. Теперь я расскажу Вам, как можно передать по сети таблицу с данными, ведь это одна из главнейших задач, которые должны решать такие программы. Рассмотрим пример работы с TClientDataSet, хотя вместо него можно использовать и другие похожие компоненты, например TdxMemData, которая содержится в наборе "DevExpress" или аналоги TQuery.

Для начала откроем новый проект и добавим на главную форму следующие компоненты:

  • DBGrid: TDBGrid (закладка "Data Controls");
  • mtCleintTable и mtServerTable: TClientDataSet (закладка "Data Access");
  • dsClientTable: TDataSource (закладка "Data Access");
  • myClient: TSHttpSenderCommands (закладка "Softerium", описание в предыдущей статье);
  • myServer: TSHttpReceiver (закладка "Softerium");
  • iServer: TIdHTTPServer; (закладка "Indy Servers").

Теперь создадим для mtCleintTable и mtServerTable поля "Id"(integer) и "Name"(string). Не забудте после этого нажать правой кнопкой по этим компонентам и выбрать пункт меню "Create DataSet". После проведенных манипуляций привяжем клиентскую таблицу к нашему DBGrid-у через dsClientTable, чтобы увидеть результат передачи данных.

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

const
  //Список команд
  CMD_GET_USERS_LIST = 'getUsersList';
  //Список параметров
  PRM_ID = 'id';
  PRM_NAME = 'name';

Представим, что наш сервер уже умеет взаимодействовать с базой данных и у него есть некая таблица пользователей. Хранятся они в mtServerTable. Теперь надо придумать, как нам грамотно превратить эти данные в обычный string и передать по TCP/IP протоколу. Для этих целей я написал функцию GetMemList и хочу поделиться ей с Вами. Ничего сложного, конечно, там нет, но, возможно, кому-то это пригодится. Так вот:

function GetMemList(mtData: TClientDataSet; const aFields: array of string): string;
var
  i: integer;
begin
  result := '';
  mtData.First;
  while not mtData.Eof do
  begin
    for i := 0 to (Length(aFields) - 1) do
      result := result + Format('"%s=%s",', [aFields[i], 
					mtData.FieldByName(aFields[i]).AsString]);
    mtData.Next;
    if not mtData.Eof then
      result := result + sLineBreak;
  end;
end;

На выходе мы получим примерно такую строчку: "id=1","name=Пользователь 1" + #13#10 + "id=2","name=Пользователь 2" и так далее. Только будьте внимательнее, заполняя параметр aFields, т.к. если таких полей в таблице не будет, то мы получим исключительную ситуацию. Представим, что у нас есть все необходимые данные и теперь их надо снова преобразовать в табличный вид. Для этого была написана процедура AddListToMemData:

procedure AddListToMemData(var mtData: TClientDataSet; commaText: string; 
  aFields: array of string; aFieldsTypes: array of TFieldType);
var
  i, j: integer;
  recCount: integer;
  sl, tmpSl: TStringList;
begin
  //Создаем 2 стринг-листа, с которыми будем работать
  sl := TStringList.Create;
  tmpSl := TStringList.Create;
  try
    sl.Text := commaText;
    recCount := sl.Count - 1;
    if not mtData.Active then
      mtData.Open
    else
    begin
      mtData.Close;
      mtData.Open;
    end;

    //Если датасет связан с таблицей - преобразования будут замедлены, поэтому 
	//отключаем контроль.
    mtData.DisableControls;
    try
      for i := 0 to recCount do
      begin
        mtData.Append;

        //Каждую строчку присланной информации разбиваем как комма-текст
        tmpSl.CommaText := sl[i];
        //В зависимости от типа поля - заполняем датасет
        for j := 0 to (Length(aFields) - 1) do
        begin
          case aFieldsTypes[j] of
            ftInteger:
              mtData.FieldByName(aFields[j]).AsInteger := 
                            StrToIntDef(tmpSl.Values[aFields[j]], -1);
            ftString:
              mtData.FieldByName(aFields[j]).AsString := 
                            tmpSl.Values[aFields[j]];
            ftDate:
              mtData.FieldByName(aFields[j]).AsDateTime := 
                            StrToDateDef(tmpSl.Values[aFields[j]], 0);
            ftDateTime:
              mtData.FieldByName(aFields[j]).AsDateTime := 
                            StrToDateTimeDef(tmpSl.Values[aFields[j]], 0);
            ftBoolean:
              mtData.FieldByName(aFields[j]).AsBoolean :=
                            StrToBoolDef(tmpSl.Values[aFields[j]], false);
          end;
        end;
        mtData.Post;
      end;
      mtData.First;
    finally
      mtData.EnableControls;
    end;
  finally
    FreeAndNil(sl);
    FreeAndNil(tmpSl);
  end;
end;

Достаточно передать в эту процедуру нужный нам датасет, полученную от сервера информацию, а так же массив имён полей и их типов. Поля у нас всего 2: это "Id" и "Name" (для них мы сделали константы PRM_ID и PRM_NAME). Их типы — ftInteger и ftString соответственно. Теперь рассмотрим весь процесс передачи данных. Начнём с клиентского приложения. В нашем примере при создании главной формы делаем следующее:

procedure TForm1.FormCreate(Sender: TObject);
begin
  mtCleintTable.Open;
  mtServerTable.Open;
  FillUsersTable;

  myClient.SendCommand(CMD_GET_USERS_LIST, [], []);
  AddListToMemData(mtCleintTable, myClient.Receive, [PRM_ID, PRM_NAME],
                                                    [ftInteger, ftString]);
end;

Всё, что мы делаем — это посылаем серверу команду CMD_GET_USERS_LIST и результат запроса записываем в наш mtCleintTable при помощи процедуры AddListToMemData. После этого переходим к описанию события onGetCommand компонента myServer:

procedure TForm1.myServerGetCommand(command: String; params: TStrings;
  var response: String);
begin
  if command = CMD_GET_USERS_LIST then
    response := GetMemList(mtServerTable, [PRM_ID, PRM_NAME]);
end;

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

Исходники можно скачать здесь.

Назад
Яндекс цитирования

Valid XHTML 1.0 Transitional

Правильный CSS!