2016-11-28 86 views
3

我试图从移动设备(iOS,Android)向TCP服务器发送流。对于服务器和客户端,我使用Indy组件。TIdIOHandler.Write程序不适用于移动设备

当我尝试从运行在移动设备中的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. 

客户端(FMX应用)

unit uClient; 

interface 

uses 
    System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 
    FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, 
    IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, FMX.ScrollBox, 
    FMX.Memo, FMX.Controls.Presentation, FMX.StdCtrls; 

type 
    TFrmClient = class(TForm) 
    IdTCPClient1: TIdTCPClient; 
    Button1: TButton; 
    MemoLog: TMemo; 
    procedure FormCreate(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    private 
    procedure Send; 
    public 
    { Public declarations } 
    end; 

var 
    FrmClient: TFrmClient; 

implementation 

{$R *.fmx} 

type 

    TSendThread = class(TThread) 
    private 
    FTCPClient : TIdTCPClient; 
    public 
    procedure Execute; override; 
    constructor Create(ATCPClient : TIdTCPClient); reintroduce; 
    end; 

procedure TFrmClient.Button1Click(Sender: TObject); 
begin 
    Send; 
end; 

procedure TFrmClient.FormCreate(Sender: TObject); 
begin 
try 
    IdTCPClient1.Port := 28888; 
    IdTCPClient1.Host := '192.168.1.134'; //change this to the ip of the TCP server. 
    IdTCPClient1.ConnectTimeout := 5000; 
    IdTCPClient1.Connect(); 
    MemoLog.Lines.Add('Connected'); 

    except on E: Exception do 
    MemoLog.Lines.Add('Exception ' + E.Message); 
end; 
end; 

procedure TFrmClient.Send; 
begin 
    if IdTCPClient1.Connected then 
    TSendThread.Create(IdTCPClient1); 
end; 

{ TSendThread } 

constructor TSendThread.Create(ATCPClient: TIdTCPClient); 
begin 
    inherited Create(False); 
    FTCPClient := ATCPClient; 
end; 

procedure TSendThread.Execute; 
var 
    LStream : TStream; 
    d : Int64; 
begin 
    LStream := TMemoryStream.Create; 
    try 
    //Send a text from all the platforms works perfect. 
    FTCPClient.IOHandler.WriteLn('<Hello>'); 
    LStream.Size := 1024; 
    LStream.Position := 0; 
    d := LStream.Size; 
    FTCPClient.IOHandler.LargeStream := True; 
    //this only works from Windows 
    FTCPClient.IOHandler.Write(LStream, d, True); 
    finally 
    LStream.Free; 
    end; 
end; 

end. 

问题是,如何可以使用印部件从移动设备发送一个流?

UPDATE:

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" /> 
+0

你有在移动应用程序清单的网络接入相应的权限? –

+0

我刚刚添加了使用的权限。 – Salvador

回答

1

印运行在所有平台上以同样的方式,所以应该有一个流是如何发送或接收的没有区别。

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

  1. 在客户端代码中的内存泄漏在Windows上运行时

  2. 在服务器代码

InputBuffer.Clear不必要的呼叫

但我没有看到任何会导致您描述的问题。您将不得不通过调试器和数据包嗅探器来调试通信,以了解发生了什么问题。

的传输的字节应该是这样的:

3C 48 65 6C 6C 6F 3E 0D 0A 00 00 00 00 00 00 04 00 

由1024个字节的随机数据紧随其后(因为你没有填充TMemoryStream任何有意义的数据)。

话虽这么说,你并不真的需要通过d在这个例子中,ASize参数的TIdIOHandler.Write(TStream)。您可以通过-1(所有数据来自当前Position)或0(整个流)。默认值是0

+0

感谢Remy在客户端和'InputBuffer.Clear'.call中发现内存泄漏(这只是一个示例代码来显示问题)。我一直在努力解决这个问题好几天,我真的使用了一个android数据包嗅探器,但只显示IOHandler.WriteLn程序发送的数据,当与流一起使用时,IOHandler.Write程序没有发送数据参数。 – Salvador

+0

我向你保证'写(TStream)'的作品。它使用'WriteLn()'在内部使用的相同'Write(TIdBytes)'重载。即使'TStream'数据没有被发送,流的大小也会是。所以还有其他事情要做。确保'IOHandler.SendBufferSize'> 0,'LStream.Size'确实是> 0等等。如果需要的话,使用调试器进入Indy的源代码。 –

+0

我刚刚检查了'IOHandler.SendBufferSize'属性的值,值是'32768',流大小也是'1024'。另外,我放弃了所有与之相关的功能:'TIdIOHandler.Write' - >'TIdIOHandler.WriteDirect' ........ - >'TIdStackVCLPosix.WSSend',最后我在调用Posix.SysSocket.send '返回1024(写入的字节数),但不发送数据。 – Salvador

2

最后,我发现这个问题,我犯了一个错误的思维,其数据没有被发送,该流发送,但因为TIdIOHandler.ReadStream函数没有被正确读取流的大小,服务器无法处理的数据流。当在AByteCount参数中传递-1值时会发生这种情况。然后使用TIdIOHandler.ReadInt64TIdIOHandler.ReadInt32函数读取流的大小,并在内部使用函数GStack.NetworkToHost函数尝试转换整数的字节序。

我修正了读取流大小而不转换字节数的问题。

我换成这行

LHandler.ReadStream(LMemoryStream, -1, False); 

此代码

LHandler.LargeStream := True; 
LHandler.ReadBytes(LBytes, SizeOf(Int64), False); 
d := BytesToInt64(LBytes); 
LHandler.ReadStream(LMemoryStream, d, False); 
相关问题