I am trying to setup a very basic single client/server system, so I can send from my PC a string to an android tablet. The tablet does something with the string and then sends back a string to the PC. The code:
server side (tablet)
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;
Client side: (desktop)
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;
The programs run fine when run on localhost or on two different PCs in the home network. However, when installing the server side on the tablet, clicking button1 on the desktop will result in a 'connection closed gracefully'. It is really frustrating, since it does work normal on two PCs, but not on PC to android tablet. I searched a long time on the internet and maybe it has to do with a difference in string endings or there might also be a problem with threads in (XE5's implementation in) Android. Any help on how to get the above code running or a different way (through IP) to communicate between a PC and a tablet using XE5 is highly appreciated.
Gerard
Added later: With wireshark I tried to check what information is being send. When running between two desktops, which behaves normally, I get communication of which I assume the following applies to the programs at hand (server=192.168.2.11, client=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
On the other hand when doing the same thing with the android tablet as a server, I get the following (server=192.168.2.8, client=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
This is the first time I see this kind of information, but it seems that in the working desktop-desktop situation line 200 corresponds to the client command IdTCPClient1.IOHandler.Writeln(s) and line 201 corresponds to the server command AContext.Connection.IOHandler.Writeln(...). In the desktop-tablet configuration the client sends the string to the server (line 119), but there is no [PSH, ACK] back. Instead there is a [FIN, ACK] response. This seems to point to something going wrong in the server-side statement in Android: s := AContext.Connection.IOHandler.ReadLn(); Any ideas what this could be and how to fix it?
Added later again: When putting a try/except statement around the line s := AContext.Connection.IOHandler.ReadLn(); then the exception 'Connection closed gracefully' is raised. When debugging delphi says: Project test.apk raised exception class segmentation fault (11) and goes to the following function in the unit 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;
The Call Stack at that moment is (first item has an arrow in front, rest a blue bullet, except the last 3 items, which have a gray bullet in front):
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 ??
Comments:
ReadLn() and WriteLn() use the same line endings on all platforms, so that is not the problem. "Connection closed gracefully" means the connection was intentionally closed. If you are getting the error on the client, the server closed the connection on its end while the client was still sending/receiving data on its end. Offhand, I don't see a problem with the code. I would suggest using a packet sniffer, such as Wireshark, to see what is actually happening on the network. My guess would be that the tablet closed the connection prematurely before all of the data was sent to the client. – Remy Lebeau Dec 10 '13 at 22:42
Thank you for your response. I added the relevant (I think) Wireshark results to the question. You seem to be right in that the tablet closed the connection prematurely. There might be something going wrong with the AContext.Connection.IOHandler.ReadLn statement on the android server side. Any ideas? – user2297042 Dec 11 '13 at 12:24
Yes, the log involving the tablet server does suggest an error is occurring before ReadLn() completes (or maybe even before ReadLn() is called). There is likely an uncaught exception being raised, which TIdTCPServer handles internally by closing the connection. Did you verify whether OnExecute is being triggered at all? How about OnConnect? Try putting ReadLn() in a try/except to see if it is the actual culprit. Also have a look at the OnException event, too. – Remy Lebeau Dec 11 '13 at 16:54
Putting try/except around the server Readln() gives the the exception 'Connection closed gracefully' in the On exception event. However, if I put a breakpoint at the Readln(), a message with the 'Connection closed gracefully' appears already on the client side before the Readln() is executed. Putting a try/except around IdTCPClient1.Connect does not seem to generate an exeption event. However, if I test the boolean IDTCPClient1.connected right after the command IdTCPClient1.Connect, the result is false in case of a connetion to the tablet, but true in case of a connection to the other desktop
For the specific problem in which there only needs to be a single client, a TidSimpleServer object instead of TidTCPserver object does work on Android for me.