我正在尝试搭建一个非常基础的单客户端/服务器系统,这样我就可以从我的电脑向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 LebeauReadLn()
完成之前(或者甚至在调用ReadLn()
之前)发生了错误。很可能会引发未捕获的异常,TIdTCPServer
通过关闭连接来内部处理它。您是否验证了OnExecute
是否被触发?OnConnect
呢?尝试将ReadLn()
放在try/except
中以查看它是否是真正的罪魁祸首。还要查看OnException
事件。 - Remy Lebeau