Indy 10 TCPServer не взаимодействует с подключенными клиентами в Linux

У меня проблема, которая заставляет меня взбираться на стену. Я пытаюсь перенести серверную часть клиент-серверного приложения Indy 10 в Windows на Linux, чтобы сэкономить средства. Приложение было первоначально разработано с использованием Delphi 2010. С тех пор я перенес его на Lazarus / FreePascal, и оно отлично работает в Windows. Учитывая, что Lazarus / FreePascal является мультиплатформенным и бесплатным, это идеальный кандидат на эту работу.

Я пробовал все, что мог, чтобы серверное приложение работало в Linux, но безуспешно. Сервер просто не общается с подключенными клиентами. Вообще ничего!

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

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  s: string;
  i: Integer;
begin
  with AContext.Connection.IOHandler do
  try
    WriteLn('Type an integer and Enter');
    s := ReadLn;
    try
      i := StrToInt(s);
      WriteLn(s + ' squared is ' + IntToStr(i*i));
    except
      WriteLn(s + ' is not an integer');
    end;
  finally
    Free;
  end;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
  Binding: TIdSocketHandle;
begin
  {$IFDEF UNIX}
  Binding := IdTCPServer1.Bindings.Add;
  //Binding.IPVersion := Id_IPv4;   <----- Gives compilation error Error: Identifier not found "Id_IPv4"
  {$ENDIF}
  Binding.IP := '127.0.0.1';
  Binding.Port := 6501;
  IdTCPServer1.Active := True;
end;

end.

Это файл проекта программы squares.lpr

program squares;

{$mode objfpc}{$H+}
// The following line is is necessary for Linux thread support            
{$IFDEF UNIX}{$DEFINE UseCThreads}{$ENDIF}     

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, uSquares
  { you can add units after this };

{$R *.res}

begin
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Когда я пытаюсь подключиться к серверу с терминала с помощью telnet, я получаю следующий ответ

telnet 127.0.0.1 6501
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
7
Connection closed by foreign host.

Как видите, telnet подключается к серверу. Но первый ответ сервера после подключения клиента «Введите целое число и введите» не отображается. Кроме того, когда я отправляю на сервер число, например «7» для возведения в квадрат, telnet сообщает: «Соединение закрыто внешним хостом». Таким образом, клиент telnet также не получает ответов от сервера. Я использую версию Indy svn, поэтому речь не идет о старой версии Indy.

Так что даже этот базовый пример не работает в Linux! Я не знаю, как решить эту проблему, поэтому мне очень нужна ваша помощь. Кроме того, если у вас есть какой-либо материал, который я могу прочитать по программированию сокетов в Linux с использованием Pascal, я буду очень признателен.

Я использую Lazarus 0.9.31 / FPC 2.4.4 и Indy 10.5.8 на Linux Mint.

JDaniel


person JDaniel    schedule 14.10.2011    source источник


Ответы (2)


Id_IPv4 определен в IdGlobal.pas, убедитесь, что этот модуль указан в вашем предложении uses. Обратите внимание, что вы вызываете Bindings.Add(), только если определена UNIX, но вы пытаетесь получить доступ к Binding вне блока IFDEF. Вам вообще не нужен блок IFDEF. Indy по умолчанию использует IPv4.

Что касается проблемы связи, я не вижу ничего плохого в показанном вами коде, при условии, что FreePascal правильно вызывает TIdIOHandler.WriteLn(), а не некоторую консольную подпрограмму ввода-вывода WriteLn (). Можете показать клиентский код?

На стороне сервера единственное, что я могу сейчас придумать, что может пойти не так, - это возможный сбой в классе Indy TIdTextEncoding при отправке / получении строк, если вы установили свойство TIdIOHandler.DefStringEncoding или глобальную переменную GIdDefaultEncoding на кодировку, отличную от кодировки по умолчанию. В системах, отличных от Windows, TIdTextEncoding использует библиотеку iconv, а поддержка Indy iconv, как известно, сейчас немного глючит. С другой стороны, кодировка Indy по умолчанию - ASCII, которая вообще не полагается на iconv, поэтому при ее использовании не должно быть никаких сбоев.

person Remy Lebeau    schedule 16.10.2011
comment
Спасибо за ответ, Реми. а) Клиентская сторона - это вывод telnet, указанный выше. Я использую telnet в качестве клиента для тестирования сервера, поэтому клиентский код отсутствует. б) Я вообще не менял состояние кодировки по умолчанию, поскольку все, что должен сделать этот простой сервер, - это отправить квадрат числа клиенту telnet. Поскольку это так, проблема в другом. Однако я исследую, как FreePascal вызывает TIOHandler.WriteLn, и буду держать вас в курсе. - person JDaniel; 16.10.2011

@ Реми Лебо. Я нашел ответ. Я играл с кодировкой текста, пока не получил код, который работает ниже:

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  s: string;
  i: Integer;
begin
  with AContext.Connection.IOHandler do
  try
    {$IFDEF UNIX}
    DefStringEncoding := TIdTextEncoding.Default
    {$ENDIF}
    WriteLn('Type an integer and Enter');
    s := ReadLn;
    try
      i := StrToInt(s);
      WriteLn(s + ' squared is ' + IntToStr(i*i));
    except
      WriteLn(s + ' is not an integer');
    end;
  finally
    Free;
  end;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
  Binding: TIdSocketHandle;
begin
  {$IFDEF UNIX}
  Binding := IdTCPServer1.Bindings.Add;
  Binding.Port := 6501;
  {$ENDIF}
  {IFDEF MSWINDOWS}
  IdTCPServer1.DefaultPort := 6501
  {$ENDIF} 
  IdTCPServer1.Active := True;
end;

Это ответ телнета:

telnet 127.0.0.1 6501
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Type an integer and Enter
7
7 squared is 49
Connection closed by foreign host.

Однако я все же хотел бы знать, как я могу установить глобальную переменную GIdDefaultEncoding, чтобы я мог отказаться от DefStringEncoding: = TIDTextEncoding.Default. Спасибо за вашу помощь.

person JDaniel    schedule 16.10.2011
comment
Вам все еще не нужен IFDEF - person Remy Lebeau; 17.10.2011
comment
Вам по-прежнему не нужны IFDEF. Свойства Bindings и DefaultPort работают точно так же в Windows и Linux. Что касается GIdDefaultEncoding, вы устанавливаете его, как любую другую переменную, например: IdGlobal.GIdDefaultEncoding := encOSDefault;, но он вступает в силу только в том случае, если TIdIOHandler.DefStringEncoding равен нулю, а это не по умолчанию, поэтому вам все равно нужно присвоить ему значение. Не имеет смысла, почему использование TIdTextEncoding.Default работает, но присвоение TIdTextEncoding.ASCII по умолчанию - нет. - person Remy Lebeau; 17.10.2011
comment
Кстати, почему вы звоните Free() на IOHandler? Если вы хотите закрыть соединение, вместо этого вызовите AContext.Connection.Disconnect(). Если вы хотите, чтобы соединение оставалось открытым, чтобы клиент мог отправлять больше команд, просто выйдите из обработчика событий OnExecute и позвольте TIdTCPServer запустить его снова (он запускается в цикле на время существования соединения). Вместо этого назначение свойства IOHandler.DefStringEncoding должно выполняться в событии OnConnect. - person Remy Lebeau; 17.10.2011
comment
да. Мне он не нужен в серверной процедуре OnExecute, но он мне нужен в процедуре FormActivate, потому что Windows не может скомпилировать операторы с Binding в них. Я не знаю, почему TIdTextEncoding.ASCII по умолчанию не работает. Для меня это загадка. В остальном я дополню код вашими предложениями. - person JDaniel; 17.10.2011
comment
Коллекция Bindings отлично работает в Windows. У меня никогда не было проблем с компилятором. Что именно у вас не работает? - person Remy Lebeau; 17.10.2011
comment
К вашему сведению, присвоение свойства TIdIOHandler.DefStringEncoding по умолчанию в настоящее время не привязано к переменной GIdDefaultEncoding. Чем больше я об этом думаю, наверное, так и должно быть. Tat сделает GIdDefaultEncoding более полезным. Я рассмотрю возможность внесения этого изменения в течение ближайшей недели (я работаю в команде разработчиков Indy). - person Remy Lebeau; 17.10.2011
comment
Моя ошибка. Вы правы насчет коллекции Bindings. Он отлично работает как в Windows, так и в Linux. Я удалил все $ IFDEF. Код отлично работает на обеих платформах и без них. - person JDaniel; 17.10.2011
comment
Telnet работает отлично, но я предполагаю, что мне придется явно указать тип кодировки в моих клиентах с графическим интерфейсом, чтобы между сервером и клиентом могло быть идеальное рукопожатие. Кроме того, я заметил, что кодировка по умолчанию в Linux правильно обрабатывает французский текст (Win 1252) (по крайней мере, с использованием Telnet). В моих клиентских и серверных приложениях Windows мне пришлось изменить его на TIdTextEncoding.UTF8, чтобы правильно читать французский текст. Что мне нужно сделать, чтобы он был полностью совместим с Unicode? - person JDaniel; 17.10.2011
comment
Где разместить свойство TIdIOHandler.DefStringEncoding на стороне клиента? Должен ли он быть в событии OnBeforeBind или в событии OnConnected? Спасибо за вашу помощь. - person JDaniel; 17.10.2011
comment
Вы можете использовать Win1252 в Windows, если у вас установлен этот язык / кодовая страница. В Indy используйте TIdTextEncoding.GetEncoding() или лучше CharsetToEncoding() для доступа к любой установленной кодировке / кодовой странице (не забудьте освободить возвращенный объект TIdTextEncoding, когда вы закончите его использовать). Однако лучший вариант для поддержки Unicode - использовать UTF-8. Вы можете установить DefStringEncoding в любое время перед обменом строками, например, после выхода Connect(). Indy в значительной степени не управляется событиями, поэтому не привыкайте полагаться на события на стороне клиента для управления вашей логикой. - person Remy Lebeau; 17.10.2011
comment
В ПОРЯДКЕ. Большое спасибо за вашу помощь. - person JDaniel; 17.10.2011