Android TIdIOHandler。写入过程在移动设备上不起作用

Android TIdIOHandler。写入过程在移动设备上不起作用,android,ios,delphi,delphi-10.1-berlin,Android,Ios,Delphi,Delphi 10.1 Berlin,我正在尝试从移动设备(iOS、Android)向TCP服务器发送流。对于服务器端和客户端,我使用Indy组件 当我试图从移动设备上运行的FMX应用程序发送流时,就会出现问题。如果我从Windows运行客户端代码,客户端会将流发送到服务器应用程序。但是我在一个移动设备上运行了相同的代码,流没有被发送 对于服务器和客户端来说,这是一个最小、完整且可验证的示例,可以重现问题 服务器端。服务器是一个VCL应用程序 unit uServer; interface uses Winapi.Windo

我正在尝试从移动设备(iOS、Android)向TCP服务器发送流。对于服务器端和客户端,我使用Indy组件

当我试图从移动设备上运行的FMX应用程序发送流时,就会出现问题。如果我从Windows运行客户端代码,客户端会将流发送到服务器应用程序。但是我在一个移动设备上运行了相同的代码,流没有被发送

对于服务器和客户端来说,这是一个最小、完整且可验证的示例,可以重现问题

服务器端。服务器是一个VCL应用程序

unit uServer;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdBaseComponent, IdComponent,
  IdCustomTCPServer, IdTCPServer, Vcl.StdCtrls, IdContext;

type
  TFrmServer = class(TForm)
    IdTCPServer1: TIdTCPServer;
    MemoLog: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure IdTCPServer1Execute(AContext: TIdContext);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FrmServer: TFrmServer;

implementation

uses
  IdGlobal,
  IdIOHandler,
  System.StrUtils;

{$R *.dfm}

procedure TFrmServer.FormCreate(Sender: TObject);
begin
  IdTCPServer1.Bindings.Clear;
  IdTCPServer1.DefaultPort := 28888;
  IdTCPServer1.Active := True;
  MemoLog.Lines.Add('Running');
end;

procedure TFrmServer.IdTCPServer1Execute(AContext: TIdContext);
var
  LHandler  : TIdIOHandler;
  s: string;
  LMemoryStream : TMemoryStream;
  AFormatSettings: TFormatSettings;
  d : Int64;
begin
  try
    LHandler := AContext.Connection.IOHandler;
    s := LHandler.ReadLn(LF, IdTimeoutDefault, MaxInt);
    AFormatSettings := TFormatSettings.Create;
    if (s <> '') then
    begin
       if StartsText('<',  s) and EndsText('>',  s)  then
       begin
            TThread.Queue(nil,
              procedure
              begin
                MemoLog.Lines.Add(Format('%s', [s], AFormatSettings));
              end
            );
            LMemoryStream := TMemoryStream.Create;
            try
                LHandler.LargeStream := True;
                LHandler.ReadStream(LMemoryStream, -1, False);
                d := LMemoryStream.Size;
                TThread.Queue(nil,
                  procedure
                  begin
                    MemoLog.Lines.Add(Format('Stream Size %d', [d], AFormatSettings));
                  end
                );
            finally
              LMemoryStream.Free;
            end;
       end
       else
         LHandler.InputBuffer.Clear;
    end;
  except
    on E: Exception do
    begin
      s := E.Message;
      TThread.Queue(nil,
        procedure
        begin
          MemoLog.Lines.Add(Format('Exception %s', [s], AFormatSettings));
        end
      );
    end;
  end;
end;

end.
unituserver;
接口
使用
Winapi.Windows、Winapi.Messages、System.SysUtils、System.Variants、System.Classes、Vcl.Graphics、,
控件、窗体、对话框、IdBaseComponent、IdComponent、,
IdCustomTCPServer、IdTCPServer、Vcl.StdCtrls、IdContext;
类型
TFrmServer=class(TForm)
IdTCPServer1:TIdTCPServer;
备忘录:TMemo;
过程表单创建(发送方:ToObject);
过程IdTCPServer1Execute(AContext:TIdContext);
私有的
{私有声明}
公众的
{公开声明}
结束;
变量
FrmServer:TFrmServer;
实施
使用
IdGlobal,
白痴汉德勒,
系统结构;
{$R*.dfm}
过程TFrmServer.FormCreate(发送方:TObject);
开始
IdTCPServer1.Bindings.Clear;
IdTCPServer1.DefaultPort:=28888;
IdTCPServer1.Active:=True;
MemoLog.Lines.Add('Running');
结束;
过程TFrmServer.IdTCPServer1Execute(AContext:TIdContext);
变量
LHandler:TIdIOHandler;
s:字符串;
LMemoryStream:TMemoryStream;
a格式设置:t格式设置;
d:Int64;
开始
尝试
LHandler:=AContext.Connection.IOHandler;
s:=LHandler.ReadLn(LF,IdTimeoutDefault,MaxInt);
a格式设置:=t格式设置。创建;
如果是“”,则
开始
如果开始文本(“”,s),则
开始
TThread.Queue(nil,
程序
开始
添加(格式('%s',[s],格式设置));
结束
);
LMemoryStream:=TMemoryStream.Create;
尝试
LHandler.LargeStream:=True;
ReadStream(LMemoryStream,-1,False);
d:=LMemoryStream.尺寸;
TThread.Queue(nil,
程序
开始
MemoLog.Lines.Add(格式('流大小%d',[d],格式设置));
结束
);
最后
自由流;
结束;
结束
其他的
LHandler.InputBuffer.Clear;
结束;
除了
关于E:Exception-do
开始
s:=E.消息;
TThread.Queue(nil,
程序
开始
MemoLog.Lines.Add(格式('Exception%s',[s],aformSettings));
结束
);
结束;
结束;
结束;
结束。
客户端(FMX应用程序)

单元客户端;
接口
使用
System.SysUtils、System.TYPE、System.UITYPE、System.Classes、System.VARIANT、,
FMX.类型,FMX.控件,FMX.窗体,FMX.图形,FMX.对话框,
IdBaseComponent、IdComponent、IdTCPConnection、IdTCPClient、FMX.ScrollBox、,
FMX.Memo,FMX.Controls.Presentation,FMX.StdCtrls;
类型
TFrmClient=class(TForm)
IdTCPClient1:tidtcplient;
按钮1:t按钮;
备忘录:TMemo;
过程表单创建(发送方:ToObject);
程序按钮1点击(发送方:ToObject);
私有的
程序发送;
公众的
{公开声明}
结束;
变量
FRM客户:TFrmClient;
实施
{$R*.fmx}
类型
TSendThread=class(TThread)
私有的
FTCPClient:TIdTCPClient;
公众的
程序执行;推翻
构造函数创建(ATCPClient:TIdTCPClient);重新引入;
结束;
程序TFrmClient.Button1Click(发件人:ToObject);
开始
发送;
结束;
过程TFrmClient.FormCreate(发送方:TObject);
开始
尝试
IdTCPClient1.端口:=28888;
IdTCPClient1.Host:=“192.168.1.134”//将此更改为TCP服务器的ip。
IdTCPClient1.ConnectTimeout:=5000;
IdTCPClient1.Connect();
MemoLog.Lines.Add('Connected');
E上除外:例外
MemoLog.Lines.Add('Exception'+E.Message);
结束;
结束;
程序TFrmClient.Send;
开始
如果IdTCPClient1.已连接,则
创建(IdTCPClient1);
结束;
{TSendThread}
构造函数TSendThread.Create(ATCPClient:TIdTCPClient);
开始
继承创建(False);
FTCPClient:=ATCPClient;
结束;
程序TSendThread.Execute;
变量
1流:TStream;
d:Int64;
开始
LStream:=TMemoryStream.Create;
尝试
//发送来自所有平台的文本非常完美。
FTCPClient.IOHandler.WriteLn(“”);
l流大小:=1024;
1流位置:=0;
d:=L流尺寸;
FTCPClient.IOHandler.LargeStream:=True;
//这只能在Windows上工作
FTCPClient.IOHandler.Write(LStream,d,True);
最后
自由流;
结束;
结束;
结束。
问题是,如何使用Indy组件从移动设备发送流

更新:

Android权限

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Indy在所有平台上的运行方式都相同,因此流的发送或接收方式应该没有区别

我在代码中看到的唯一问题是:

  • 在Windows上运行时,客户端代码中存在内存泄漏

  • InputBuffer的不必要调用。在服务器代码中清除

  • 但是我没有看到任何会导致你所描述的问题的东西。您必须使用调试器和数据包嗅探器来调试通信,以找出哪里出了问题

    传输的字节应如下所示:

    3C 48 65 6C 6C 6F 3E 0D 0A 00 00 00 00 00 00 04 00
    
    后跟1024字节的随机数据(因为您没有用任何有意义的数据填充
    TMemoryStream

    也就是说,在本例中,您实际上不需要将
    d
    传递给
    TIdIOHandler.Write(TStream)
    ASize
    参数。你可以通过
    -1
    
    3C 48 65 6C 6C 6F 3E 0D 0A 00 00 00 00 00 00 04 00
    
    LHandler.ReadStream(LMemoryStream, -1, False);
    
    LHandler.LargeStream := True;
    LHandler.ReadBytes(LBytes, SizeOf(Int64), False);
    d := BytesToInt64(LBytes);
    LHandler.ReadStream(LMemoryStream, d, False);