Jump to content
JusJJ

Indy 10 android problem

Recommended Posts


I have a reconnection problem in an android application,using Indy TidTcpClient and TIdThreadComponent.
I've tried both the Delphi bundled and the latest Indy version.

 

At the beginning, the program goes through the IP addresses of the available devices.

Read and write routines work without problems, but when you try to change the IP address, almost without exception the program freezes. Sometimes it takes a few seconds, sometimes a few minutes before the program responds and about every ten times the program stops responding completely. In Windows, changing the IP address works reasonably well, but sometimes it also freezes.

 

Receiving works...:

try
    StartChar := TCPSocket.IOHandler.ReadChar;
    if StartChar = '%' then
    begin
      Data := TCPSocket.IOHandler.ReadLn('&');
      Payload := Data;
      TCPSocket.IOHandler.ReadBytes(ChecksumBytes, 4);
      ReceivedChecksum := BytesToString(ChecksumBytes);
      CalculatedChecksum := CRC16(Payload);
      if ReceivedChecksum = CalculatedChecksum then
      begin
        TThread.Queue(nil,
          procedure
          var
            i: Integer;
          begin
           HandleData;
          end);
      end
      else
      begin
          TThread.Queue(nil,
          procedure
          begin
           HandleChecksumError;
          end);
      end;
    end;
  except
    on E: Exception do
    begin
      TThread.Queue(nil,
        procedure
        begin
         HandleException;
        end);
      ReceiveThread.Terminate;
	  end;

...but changing IP fails: 

procedure IpSelectF.IPBtnClick(Sender: TObject);
Var
  Btn: TButton;
  NewIp: string;
begin
  if Sender is TButton then
  begin
    Btn := TButton(Sender);
    if AvailableIP[Btn.Tag] then
    begin
      TTask.Run(
        procedure
        begin
          try
            if Assigned(MainF.ReceiveThread) and MainF.ReceiveThread.Active then
            begin
              MainF.ReceiveThread.Stop;
              MainF.ReceiveThread.WaitFor;  
            end;
            if MainF.TCPSocket.Connected then
              MainF.TCPSocket.Disconnect;
            NewIp := '192.168.1.' + IntToStr(Btn.Tag);
            MainF.TCPSocket.Host := NewIp;
            try
              MainF.TCPSocket.ConnectTimeout := 3000; 
              MainF.TCPSocket.Connect;
              if MainF.TCPSocket.Connected then
              begin
                TThread.Queue(nil,
                  procedure
                  begin
                    MainF.ReceiveThread.Start;
                    ShowAlertDialog('Connected to ' + NewIp);
                  end
                );
              end
              else
              begin
                TThread.Queue(nil,
                  procedure
                  begin
                    ShowAlertDialog('Connection to ' + NewIp + ' failed.');
                  end
                );
              end;
            except
              on E: Exception do
              begin
                TThread.Queue(nil,
                  procedure
                  begin
                    ShowAlertDialog('Failed to connect to ' + NewIp + ': ' + E.Message);
                  end
                );
              end;
            end;
          except
            on E: Exception do
            begin
              TThread.Queue(nil,
                procedure
                begin
                  ShowAlertDialog('Error during IP switch: ' + E.Message);
                end
              );
            end;
          end;
        end
      ); 
    end;
  end;	  

 

Share this post


Link to post

The first thing I notice is that you are calling Connected() across thread boundaries,  Don't do that.  Connected() performs a socket read in the calling thread, so it could interfere with a read operation in another thread.  You shouldn't be calling Connected() at all.  Certainly not right after a successful Connect() (which will raise an exception if it fails), and not right before a Disconnect() (which will close the socket and doesn't care if the socket was connected).

 

Another thing I notice is that you are not serializing access to your processing data.  TThread.Queue() runs asynchronously, so it is possible that you might receive a new message and overwrite your variables before an earlier queued HandleData() call has been processed.

 

I also wonder why you are creating a new Task to stop the reading thread and reconnect, but are using the main thread to start the reading thread?

 

Your use of multiple threads is odd to me. I would suggest a different design.  I would move the (re)connect logic into the reading thread itself, where the thread runs a loop that connects, reads until done, and then disconnects, repeating until stopped.  If the IP needs to change, then cache the new IP and ask the thread to reconnect at its earliest convenience.  That way, the thread alone decides when it is safe to read and when it is safe to disconnect and reconnect, notifying the main thread of any status changes as needed.

 

Try something more along the lines of this:

// set ReceiveThread.Loop = True
// start the thread when you are ready to connect the 1st time
// stop the thread when you are ready for the final disconnect

public
  ServerHost: String;
  ServerHostChanged: Boolean;

procedure TMainForm.ReceiveThreadRun(Sender: TIdThreadComponent);
begin
  try
    if (TCPSocket.Host = '') or ServerHostChanged then
    begin
      TCPSocket.Host := ServerHost;
      ServerHostChanged := False;
    end;

    try
      TCPSocket.Connect;
    except
      on E: Exception do
      begin
        ...
        for var I := 1 to 5 do
        begin
          if not ReceiveThread.Active then Break;
          Sleep(1000);
        end;
        Exit;
      end;
    end;

    try
      ...
      while ReceiveThread.Active and (not ServerHostChanged) do
      begin
        try
          if TCPSocket.IOHandler.InputBufferIsEmpty then
          begin
            TCPSocket.IOHandler.CheckForDataOnSource(250);
            TCPSocket.IOHandler.CheckForDisconnect;
            if TCPSocket.IOHandler.InputBufferIsEmpty then Continue;
          end;
          // read a message and process as needed ...
        except
          ...
          Break;
        end;
      end;
    finally
      TCPSocket.Disconnect;
      ...
    end;
  except
    ...
  end;
end;

...

procedure IpSelectF.IPBtnClick(Sender: TObject);
var
  Btn: TButton;
begin
  Btn := Sender as TButton;
  if not AvailableIP[Btn.Tag] then Exit;
  MainF.ServerHost := '192.168.1.' + IntToStr(Btn.Tag);
  MainF.ServerHostChanged := True;
  if not MainF.ReceiveThread.Active then
    MainF.ReceiveThread.Start;
end;

 

Share this post


Link to post

Thank you very much Remy! It seems to work that way.

 

One more question : What would be the best and fastest way to initially check if the devices are connected between 192.168.1.1 - 192.168.1.42?

 

 

Edited by JusJJ

Share this post


Link to post
2 hours ago, JusJJ said:

What would be the best and fastest way to initially check if the devices are connected between 192.168.1.1 - 192.168.1.42?

You are going to have to elaborate on that.  What are you trying to check, exactly?

Share this post


Link to post

It is an old, huge industrial compressed air system. It is divided into sectors with a total of 42 devices, valves, dryers, coolers etc.
Some devices automatically go into sleep mode because whole system can only work at full power momentarily. Each device is controlled via an ethernet converter and, since it draws the operating voltage directly from the actuator in question, it is of course not possible to contact it.
Now, if you need to adjust an individual device to change settings in some sector, it's easier to see directly which devices are in use than to go through them one by one.

Share this post


Link to post

How to go through ip addresses between 192.168.1.1-192.168.1.42 are they available.


If device[1].canconnect then

deviceavailable[1] = true

else 

deviceavailable[1] = false

etc.

Share this post


Link to post

If you are asking how to know whether a given device can be connected to by your TCP client, then the only way to determine that is to actually attempt to connect to the device's TCP server and see if it succeeds or fails.

 

At the very least, you might try simply pinging each IP to see if it is on the network, but that won't tell you whether the TCP server can be connected to.

 

If you have access to change the device's code, then you might add a UDP server that runs alongside the TCP server, and then you can send a UDP broadcast across the network subnet and see which UDP servers respond.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×