Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Закрытая тема
Ваша тема закрыта, почему это могло произойти? Возможно,
Нет наработок или кода, если нужно готовое решение - создайте тему в разделе Фриланс и оплатите работу.
Название темы включает слова - "Помогите", "Нужна помощь", "Срочно", "Пожалуйста".
Название темы слишком короткое или не отражает сути вашего вопроса.
Тема исчерпала себя, помните, один вопрос - одна тема
Прочитайте правила и заново правильно создайте тему.
 
Опции темы Поиск в этой теме
Старый 15.01.2012, 21:33   #21
MAMOHT128
 
Регистрация: 15.01.2012
Сообщений: 5
По умолчанию

посмотрите здесь:
http://sourceforge.net/projects/indy10clieservr/

p.s. синяя кнопка browse code )
MAMOHT128 вне форума
Старый 26.01.2012, 11:13   #22
MAMOHT128
 
Регистрация: 15.01.2012
Сообщений: 5
Смех

Подведу итоги работы с компонентой idCmdTCPServer и idTCPClient из INDY 10.5.8.0
Проблемы и решения.

1. Проблемы с кодировкой, полученные сообщения приходят ввиде ????? (кракозябры, вопросы, не русские буквы)
События указаны для наглядного примера.

На старте пишем (не обязательно):
Код:
procedure TForm1.FormCreate(Sender: TObject);
begin
  TIdTextEncoding.Create;
end;
На сервере при подключении к нему клиента, указываем:
Код:
procedure TForm1.CmdTCPServer1Connect(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.DefStringEncoding:=TIdTextEncoding.UTF8;
end;
В клиенте, при подключении к серверу:
Код:
TcpClient1.IOHandler.DefStringEncoding:=TIdTextEncoding.UTF8;
2. Как запустить сервер и подключиться клиенту?
В простом сервере это выглядит так, кодировку мы ранее указали:
Код:
  try
    CmdTCPServer1.DefaultPort:=strtoint('4000');  
    CmdTcpServer1.active:=true;
  except
    on E: Exception do
    begin
      Memo1.Lines.Add('['+TimeToStr(now())+'] '+'Ошибка Запуска! ' + StringReplace(E.Message, EOL, ' ', [rfReplaceAll]));  //если ошибка - выведем её в Memo
      cmdTcpServer1.active:=False; 
      raise;
    end;
В клиенте:
Код:
  try
    with TcpClient1 do
      begin
        Connect;
        IOHandler.DefStringEncoding:=TIdTextEncoding.UTF8;
        GetResponse(200);  //"регистрируемся" на сервере
        Memo1.Lines.AddStrings(LastCmdResult.Text);  //получаем приветствие
      end;
   except
     on E: Exception do
       begin
         TcpClient1.Disconnect;
         Memo1.Lines.Add('['+TimeToStr(now())+'] '+'Ошибка подключения! ' + StringReplace(E.Message, EOL, ' ', [rfReplaceAll]));
         raise;
       end;
   end;
3. Как отправить сообщение на сервер?
TIdCmdTCPServer не зря назвали "Cmd", у него есть полезное свойство CommandHandlers (доступно через инспектор объектов), вот им и воспользуемся.
В редакторе создадим команду "say", в свойствах NormalReply присвоим код 201, чтобы сервер смог различать где имя команды, а где параметры (в нашем случае параметр это текст сообщения отправляемый клиентом) существуют свойства CmdDelimiter и ParamDelimiter, присвоим что ни будь по интереснее, например символ "¡" (перевернутый восклицательный знак ), незабыв удостоверится что ParseParams присвоено True.
Для команд доступно только одно событие, а именно OnCommand.
Допустим сервер уже успешно запущен.
Код:
var
  s: string;
begin
  s:='['+TimeToStr(now())+'] '+ASender.Params[0]; // [10:51:05] Привет
  Memo1.Lines.Add(s);  //запишем например в Memo
end;
ASender.Params[0] - это первый параметр команды от клиента, о нём далее.
С сервером разобрались он знает команду с кодом 201 и записывает поступившие с ней параметры в Memo, теперь клиент должен уметь отправить этот запрос, допустим он у нас уже успешно подключён, а текст сообщения мы отправляем из Идита:
Код:
try
  Application.ProcessMessages;  //на всякий случай, бывали не приятные ситуации когда возвращаемые результаты путались в "параллельно" выполняемых запросах
  TcpClient1.SendCmd('Say¡'+Edit1.Text, 201);
except
  TcpClient1.Disconnect;  //если ошибка, то отключаемся
end;
4. Как получить сообщение от сервера?
В INDY 9 была распространена практика, когда сервер сам инициировал передачу сообщений, т.е. "брал" список подключенных к нему клиентов и всем подрят рассылал, это несколько не правильно по отношению к структуре Клинет-Сервер, в которой клиент должен сам спрашивать, а сервер только отвечать.
Для этого потребуется создать еще один обработчик команды на сервере, назавем его "all" и присвоем ему код 202, а в обработчике события напишем:
Код:
ASender.Response.add('Привет я сервер');
Рассмотрим вариант когда нам нужно постоянно получать сообщения от сервера.
Для этого воспользуемся компонентом Timer c интервалом 500мс, т.е. событие OnTimer будет выполняться каждые пол секунды (не беспокоимся, это совсем не много и вашь компьютер не зависнет )
Код:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  try
  if TcpClient1.Connected then
    begin
      Application.ProcessMessages;
      TcpClient1.SendCmd('all¡',202);
      TcpClient1.IOHandler.Capture(memo1.Lines);
    end;
  Except
    Timer1.Enabled:=false;
  end;
end;
В итоге у нас будет куча "приветов" от сервера, но уже вам решать, что он будет отправлять, в параметрах запроса можно указывать конкретизирующую информацию, например: сколько сообщений, за какой период поступления и т.п., при этом сервер должен уметь их обработать.

Последний раз редактировалось MAMOHT128; 26.01.2012 в 12:50.
MAMOHT128 вне форума
Старый 26.01.2012, 12:13   #23
MAMOHT128
 
Регистрация: 15.01.2012
Сообщений: 5
Смех

5. Как отправть сообщение клиента всем клиентам сервера?
Сообщения клиентов храним в стринглисте, в interface var добавляем:
Код:
var
  Messages: TStringList;
И не забываем его создать, например тут:
Код:
procedure TForm1.FormCreate(Sender: TObject);
begin
  messages:=TStringList.Create;
end;
Модернезируем ранее рассмотренные обработчики событий команд сервера.
Когда сервер получает сообщение, записываем его не только в Memo, но и сразу в стринглист сообщений:
Код:
var
  s: string;
begin
  s:='['+TimeToStr(now())+'] '+ASender.Params[0];
  Memo1.Lines.Add(s);  
  messages.Add(s);
end;
На "all" запрос клиента отправляем всё что есть:
Код:
  ASender.Response.add(messages.Text);
Не совсем логично отправлять клиентам каждый раз одно и тоже, но у нас ни сервер ни клиенты не знают поступали ли новые сообщения или мы гоняем дубликаты.
Чтобы этого избежать на сервере требуется вести историю входящих сообщений, а лучше каждому сообщению присвоить индивидуальный номер (ID), например с каждым входящим сообщением просто увеличивать на 1, а на клиенте хранить ID последнего сообщения.
Допустим сервер получил 50 сообщений (ID=50), клиент получил 48 (т.е. между запросами поступило еще 2),
клиент спрашивает: у тебя какой последний идентификатор сообщения?
сервер: 50
клинет: 50>48 - высылай
сервер: отправил (данные...)
клиент: последний ID?
сервер: 50
клиент: 50=50 - ненужно
и т.д.

6. Как отправить сообщения с сервера клиентам?
Точно также как и в предыдущем случае, просто добавляем сообщение в стринглист и при следующей синхронизации клиенты его получат:
Код:
  s:='['+TimeToStr(now())+'] Admin: '+'Я всё вижу!';
  Messages.Add(s);
7. Как хранить списки клиентов на сервере?
Создаем некую упорядоченную структуру на сервере.
Клиентов будем хранить в массиве записей, в interface пишем и var добавляем:
Код:
type
  client = record
    clIP: String;
    Con: TIdContext;
    Used: boolean;    
  end;

var
  clients: array [1..100] of client;
При подключении к серверу записываем туда информацию о клиенте, а чтоб вообще по хорошему, то создаём команду обработки регистрации нового клиента, проверка пароля, ника и т.п.
В общем случае:
Код:
var
  i: integer;
begin
  for i:=1 to 100 do
    if not clients[i].Used then
      begin
        clients[i].clIP:=ASender.Context.Connection.Socket.Binding.PeerIP;
        clients[i].Con:=ASender.Context;
        clients[i].Used:=true;
        Memo1.Lines.Add('ктото пришел');
        break;
      end;
end;
И на последок не забываем прощаться с сервером
Код:
  if TcpClient1.Connected then
    begin
      Application.ProcessMessages;
      TcpClient1.SendCmd('quit¡', 203);
      TcpClient1.Disconnect;
    end;
Естественно заранее создав обработчик команды "quit" на сервере указав в свойствах Disconnect = True
Выключая сервер освобождаем ресурсы
Код:
try
  if CmdTCPServer1.Active then
    CmdTCPServer1.Active:=false;
finally
  messages.Free;
end;

Последний раз редактировалось MAMOHT128; 26.01.2012 в 12:50.
MAMOHT128 вне форума
Старый 15.02.2012, 23:37   #24
MAMOHT128
 
Регистрация: 15.01.2012
Сообщений: 5
Смех

Приложил исходник рабочего клиента/сервера
в одном приложении

на идеальность кода не претендую т.к. данный проект создавался без определенной цели и лишь для проверки работы компонентов, со множеством передлок в процессе написания
Вложения
Тип файла: rar 3.rar (1.93 Мб, 96 просмотров)
MAMOHT128 вне форума
Старый 27.02.2012, 08:56   #25
ProGramer86
 
Регистрация: 19.01.2012
Сообщений: 5
По умолчанию

На Delphi 2010 не канает, сервер видит всех клиентов, а клиенты друг друга не могут, обидно, разбираться в коде. А так пример хороший, спасибо.

Может у кого нибудь есть свои примеры? Народ, пожалуйста скиньте примерчики) Будем благодарны.
ProGramer86 вне форума
Закрытая тема


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
TForm & TImage & PNG & Прозрачность delphi_beginner Общие вопросы Delphi 7 19.09.2009 08:46
Просто не понимаю б чём проблема!!!!!!!(char* && index) artush1984 Общие вопросы C/C++ 12 30.05.2009 03:30
TJVRichEdit & Delphi 2009 fanatica Компоненты Delphi 1 20.04.2009 13:28
Проблема с MVC++ 2005 && 2008 на висте NetGod Софт 1 09.08.2008 17:14
Delphi & Access & Ole vodila БД в Delphi 0 15.07.2008 11:11