基本的Indy TCP客户端/服务器适用于Win32/Android

4

我正在尝试搭建一个非常基础的单客户端/服务器系统,这样我就可以从我的电脑向Android平板发送一个字符串。平板会对字符串进行处理,然后将一个字符串发送回电脑。以下是代码:

服务器端(平板电脑)

procedure TForm1.Button1Click(Sender: TObject); // start the server
begin
  IdTCPServer1.Bindings.Clear;
  IdTCPServer1.Bindings.Add.SetBinding('xxx.xxx.x.x', 4405); // for the xxx's the IP adres of the tablet 
  IdTCPServer1.Active := True;
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var s:string;
begin
  s := AContext.Connection.IOHandler.ReadLn(); 
  Try
    ... do something with the string
    AContext.Connection.IOHandler.Writeln('ready');
  Except
    AContext.Connection.IOHandler.Writeln('Error');
  End;
  AContext.Connection.Disconnect;
end;

客户端(桌面版):
procedure TFMain.FormCreate(Sender: TObject);
begin
  IdTCPClient1.host := 'xxx.xxx.x.x';  // xxx's the IP address of the tablet
  IdTCPClient1.Port := 4405;
end;

procedure TForm1.Button1Click(Sender: TObject);
var s:string;
begin
  s := 'some string';
  IdTCPClient1.Connect;
  IdTCPClient1.IOHandler.Writeln(s);
  if IdTCPClient1.IOHandler.ReadLn='ready' then Button1.text := 'ready'
                                           else Button1.text := 'failed';

  IdTCPClient1.Disconnect;
end;

程序在本地计算机或家庭网络中的两台不同计算机上运行正常。但是,在平板电脑上安装服务器端后,单击桌面上的button1将导致“连接正常关闭”的情况。这真的很令人沮丧,因为它在两台PC上正常工作,但在PC到android平板电脑上却不能正常工作。我在互联网上搜索了很长时间,可能与字符串结束的差异有关,或者(XE5在)Android中的线程也可能存在问题。非常感谢任何关于如何使上述代码运行或使用IP进行PC和平板电脑之间通信的不同方法的帮助。
Gerard
稍后添加: 使用wireshark尝试检查发送的信息。当在两个正常的桌面计算机之间运行时,我得到了通信,我认为以下适用于手头的程序(服务器=192.168.2.11,客户端=192.168.2.10)。
197 9.209090000 192.168.2.10    192.168.2.11    TCP 66  56699 > ds-admin [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
198 9.212694000 192.168.2.11    192.168.2.10    TCP 66  ds-admin > 56699 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
199 9.212764000 192.168.2.10    192.168.2.11    TCP 54  56699 > ds-admin [ACK] Seq=1 Ack=1 Win=65536 Len=0
200 9.213041000 192.168.2.10    192.168.2.11    TCP 111 56699 > ds-admin [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=57
201 9.217302000 192.168.2.11    192.168.2.10    TCP 119 ds-admin > 56699 [PSH, ACK] Seq=1 Ack=58 Win=65536 Len=65
202 9.217304000 192.168.2.11    192.168.2.10    TCP 60  ds-admin > 56699 [FIN, ACK] Seq=66 Ack=58 Win=65536 Len=0
203 9.217349000 192.168.2.10    192.168.2.11    TCP 54  56699 > ds-admin [ACK] Seq=58 Ack=67 Win=65536 Len=0
204 9.217543000 192.168.2.10    192.168.2.11    TCP 54  56699 > ds-admin [FIN, ACK] Seq=58 Ack=67 Win=65536 Len=0
207 9.225011000 192.168.2.11    192.168.2.10    TCP 60  ds-admin > 56699 [ACK] Seq=67 Ack=59 Win=65536 Len=0

另一方面,当将Android平板电脑作为服务器进行相同操作时,我会得到以下结果(服务器=192.168.2.8,客户端=192.168.2.10):

116 4.792467000 192.168.2.10    192.168.2.8 TCP 66  56407 > ds-admin [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
117 4.802487000 192.168.2.8 192.168.2.10    TCP 66  ds-admin > 56407 [SYN, ACK] Seq=0 Ack=1 Win=14600 Len=0 MSS=1460 SACK_PERM=1 WS=64
118 4.802571000 192.168.2.10    192.168.2.8 TCP 54  56407 > ds-admin [ACK] Seq=1 Ack=1 Win=65536 Len=0
119 4.802857000 192.168.2.10    192.168.2.8 TCP 111 56407 > ds-admin [PSH, ACK] Seq=1 Ack=1 Win=65536 Len=57
120 4.808816000 192.168.2.8 192.168.2.10    TCP 60  ds-admin > 56407 [FIN, ACK] Seq=1 Ack=1 Win=14656 Len=0
121 4.808860000 192.168.2.10    192.168.2.8 TCP 54  56407 > ds-admin [ACK] Seq=58 Ack=2 Win=65536 Len=0
122 4.808944000 192.168.2.10    192.168.2.8 TCP 54  56407 > ds-admin [FIN, ACK] Seq=58 Ack=2 Win=65536 Len=0
123 4.812517000 192.168.2.8 192.168.2.10    TCP 60  ds-admin > 56407 [ACK] Seq=2 Ack=58 Win=14656 Len=0
124 4.813608000 192.168.2.8 192.168.2.10    TCP 60  ds-admin > 56407 [RST, ACK] Seq=2 Ack=58 Win=14656 Len=0
125 4.813609000 192.168.2.8 192.168.2.10    TCP 60  ds-admin > 56407 [RST] Seq=2 Win=0 Len=0
126 4.814570000 192.168.2.8 192.168.2.10    TCP 60  ds-admin > 56407 [RST] Seq=2 Win=0 Len=0

这是我第一次看到这种信息,但似乎在工作桌面-桌面情况下,第200行对应于客户端命令IdTCPClient1.IOHandler.Writeln(s),第201行对应于服务器命令AContext.Connection.IOHandler.Writeln(...)。在桌面-平板电脑配置中,客户端将字符串发送到服务器(第119行),但没有[PSH,ACK]回复。相反,会有一个[FIN,ACK]响应。这似乎指向了Android服务器端语句中的某些问题:s := AContext.Connection.IOHandler.ReadLn(); 有什么想法可以解决这个问题吗?
稍后再添加:在try/except语句周围放置以下行时 s := AContext.Connection.IOHandler.ReadLn(); 然后引发异常“连接已正常关闭”。调试Delphi时说:项目test.apk引发了异常类段错误(11),并进入单元IdIOHandler rev 1.123(2/8/05)中的以下函数。
function TIdIOHandler.ReadLn(AByteEncoding: IIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
  ): string;
{$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
begin
  Result := ReadLn(LF, IdTimeoutDefault, -1, AByteEncoding
    {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}
    );
end;

此时的调用栈为(第一项前有箭头,其余为蓝色圆点,除了最后三项外,它们前面有灰色圆点):
Idiohandler.TIdIOHandler.ReadLn(0x797e8cb4,nil,nil)
Umaintablet.TFCalibration_tablet_side.IdTCPServer1Execute(0x74124e70,0x769d76a0)
Idcustomtcpserver.TIdCustomTCPServer.DoExecute(0x76414480,0x769d76a0)
Idcontext.TIdContext.Run(0x769d76a0)
Idtask.TIdTask.DoRun(0x769d76a0)
Idthread.TIdThreadWithTask.Run(0x766bd258)
Idthread.TIdThread.Execute(0x766bd258)
System.Classes.ThreadProc(0x766bd258)
System.ThreadWrapper(0x766bd300)
:4003F3DC __thread_entry
:4003EAC8 pthread_create
:00000000 ??

ReadLn()WriteLn()在所有平台上使用相同的行结尾符,因此这不是问题所在。 "连接正常关闭"表示连接被有意关闭。如果您在客户端上遇到错误,则服务器在客户端仍在发送/接收数据时关闭了连接。一时半会儿,我没有看到代码有什么问题。我建议使用数据包嗅探器(例如Wireshark)查看网络上实际发生的情况。我猜测平板电脑在所有数据都发送到客户端之前就过早地关闭了连接。 - Remy Lebeau
谢谢您的回复。我已经将相关(我认为)的Wireshark结果添加到了问题中。您似乎是正确的,平板电脑过早地关闭了连接。在Android服务器端,AContext.Connection.IOHandler.ReadLn语句可能出现了一些问题。有什么想法吗? - Gerard
是的,涉及到平板服务器的日志确实表明在ReadLn()完成之前(或者甚至在调用ReadLn()之前)发生了错误。很可能会引发未捕获的异常,TIdTCPServer通过关闭连接来内部处理它。您是否验证了OnExecute是否被触发?OnConnect呢?尝试将ReadLn()放在try/except中以查看它是否是真正的罪魁祸首。还要查看OnException事件。 - Remy Lebeau
在服务器Readln()周围放置try/except会在异常事件中显示“连接已优雅关闭”。但是,如果我在Readln()处设置断点,则在执行Readln()之前,在客户端侧就会出现带有“连接已优雅关闭”的消息。在IdTCPClient1.Connect周围放置try/except似乎不会生成异常事件。但是,如果我在命令IdTCPClient1.Connect之后立即测试布尔IDTCPClient1.connected的结果,在连接到平板电脑时,结果为false,在连接到其他桌面时结果为true。 - Gerard
尝试绑定到一个更高的端口(例如49152),如短暂端口 - Marcus Adams
显示剩余20条评论
1个回答

0
对于只需要单个客户端的特定问题,使用TidSimpleServer对象而不是TidTCPserver对象在Android上对我有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接