Android Tutorial [B4X] Network + AsyncStreams + B4XSerializator

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
Better implementation, based on B4XPages: https://www.b4x.com/android/forum/t...-asyncstreams-b4xserializator.119011/#content

New video tutorial:













This is a simple and important example. It demonstrates several good practices related to network communication.

1. The network related code is in the Starter service. The network state is also stored in the starter service.
2. The Activity is only responsible for the UI.
3. Using B4XSerializator it is simple to work with types instead of strings or bytes. In this case the message structure is:
B4X:
Type MyMessage (Name As String, Age As Int, Image() As Byte)
- Bitmaps cannot be directly serialized so we need to convert the bitmap to bytes and vice versa.
- B4XSerializator is cross platform compatible. This means that you can send the exact same structure to B4J or B4i applications.

Platform specific notes

- The code in B4J and B4i is a bit simpler as there is no need to use a second module (starter module). All modules are always active in B4J and B4i.
- In B4i we need to recreate the server socket when the application becomes active as the socket can become stale while the app is in the background (and not killed).
- Windows firewall blocks incoming connections so if you want to connect to the B4J app, you need to first allow incoming communication on the relevant port.
 

Attachments

Last edited:

Joerg Rothballer

Member
Licensed User
Hi. Great possibility. This is what I am looking for.
How do I get these data to an Windows-Desktop-Application written in VB.Net
Does anyone has an sample code to get the Type and Bitmap by an vb.net (SERVER) application?
Thanks
 

Asim A Baki

Active Member
Licensed User
Serializator only supports objects less than 16K length, it truncate other bytes,
Any chance to get over this so I can send Files, and images?
 

a n g l o

Member
Licensed User
hello and thank you

1. on both sides, in the 'ConnectToServer' sub, the 'client (socket)' variable is declared locally inside the sub, although there's a global variable having same name.
is it on purpose, and if yes - why ?

2. on both, in the 'btnSend_Action' sub, you use the 'out (OutputStream)' after it was closed. it seems that the 'close' should come after the usage, or it does'nt matter since the stream is already filled up ?

thank you
 

Erel

Administrator
Staff member
Licensed User
1. The code is:
B4X:
Public Sub ConnectToServer(Host As String)
   Log("Trying to connect to: " & Host)
   CloseExistingConnection
   Dim client As Socket
   client.Initialize("client")
   client.Connect(Host, PORT, 10000)
End Sub
Local variables in B4X never hide global variables. This means that this code initializes the global variable again. This is on purpose as you cannot reuse a socket object.

2:
B4X:
Dim out As OutputStream
out.InitializeToBytesArray(0)
cvs.Bitmap.WriteToStream(out, 100, "PNG")
out.Close
mm.Image = out.ToBytesArray
The OutputStream is a memory stream. It is safe to call ToBytesArray after it was closed.
 

a n g l o

Member
Licensed User
hi,
still learning & testing this example, i've tried Erel's reply here 2 month back :
Read the file into an array of bytes and send the bytes:
implementing the b4a as a server on android, and the b4j as a client on windows(desktop),
and setting both sides clocks to exact same time,
the changes to Erel's original code are :
b4a :
B4X:
Sub btnSend_Click
                Dim mm As MyMessage
                Dim out As OutputStream
               
                mm.Initialize
                mm.Age = edtAge.Text
                mm.Name = edtName.Text
               
                'convert the bitmap to bytes
               
        'replace the 4  folowing lines       
                'out.InitializeToBytesArray(0)
                'cvs.Bitmap.WriteToStream(out, 100, "PNG")
                'out.Close
                'mm.Image = out.ToBytesArray
        'with the folowing 8 lines       
                Dim sharedDir As String
                Dim fileName As String
                Dim bAR () As Byte
                sharedDir=File.DirRootExternal & "/tmp"
                fileName="testo.mp3"
                bAR=Bit.InputStreamToBytes(File.OpenInput(sharedDir, fileName))
                mm.Image=bAR
                mm.Name="3.8 MB file test"
        'end replace-with
               
                CallSub2(Starter, "SendData", ser.ConvertObjectToBytes(mm))
End Sub
and :
B4X:
Public Sub SendData (data() As Byte)
    'new code
                Log(DateTime.Time(DateTime.Now))
    'end new code           
                If connected Then astream.Write(data)
End Sub
b4j:
B4X:
Sub AStream_NewData (Buffer() As Byte)
            Dim mm As MyMessage
            Dim in As InputStream
            Dim bmp As Image
    'new code
            Log(DateTime.Time(DateTime.Now))
    'end new       
            mm= ser.ConvertBytesToObject(Buffer)
            edtAge.Text = mm.Age
            edtName.Text = mm.Name
   
            'convert the array of bytes to image
    'delete the folowing 3 lines       
            'in.InitializeFromBytesArray(mm.Image, 0, mm.Image.Length)
            'bmp.Initialize2(in)
           
            'draw the image
            'cvs.DrawImage(bmp, 0, 0, cvs.Width, cvs.Height)
    'end delete           
End Sub
i've taken the times for that 3.8mb file transfer about 30 times.
all times were between 40-65 seconds, which is very SLOW (less then 100k/sec).
my network is fast and nothing heavy was occupying it.
is that the best i can get, or am i missing something ?

Thank you
 

Gnappos

Active Member
Licensed User


This is a simple and important example. It demonstrates several good practices related to network communication.

1. The network related code is in the Starter service. The network state is also stored in the starter service.
2. The Activity is only responsible for the UI.
3. Using B4XSerializator it is simple to work with types instead of strings or bytes. In this case the message structure is:
B4X:
Type MyMessage (Name As String, Age As Int, Image() As Byte)
- Bitmaps cannot be directly serialized so we need to convert the bitmap to bytes and vice versa.
- B4XSerializator is cross platform compatible. This means that you can send the exact same structure to B4J or B4i applications.

Cmpiled with b4a v 5.02(1):
by running this app on my smartphone with Android 6 I get the following error:


Note: log switch off, only log_main and log_events will have logs!
--------- beginning of main
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
Error occurred on line: 36 (Main)
java.lang.RuntimeException: Object should first be initialized (FloatLabeledEditText).
at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
at anywheresoftware.b4a.objects.FloatLabeledEditTextWrapper.getEditText(FloatLabeledEditTextWrapper.java:68)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:636)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:302)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:238)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:121)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
at b4a.example.main.afterFirstLayout(main.java:106)
at b4a.example.main.access$100(main.java:17)
at b4a.example.main$WaitForLayout.run(main.java:78)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5621)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)



 
Last edited:

Erel

Administrator
Staff member
Licensed User
A new version of the example was uploaded to the first post. It uses the new Wait For keyword to simplify the code. It requires B4A v7+ (beta will be released this week).

This allows listening for new connections with this code:
B4X:
Private Sub ListenForConnections
   Do While working
     server.Listen
     Wait For Server_NewConnection (Successful As Boolean, NewSocket As Socket)
     If Successful Then
       CloseExistingConnection
       client = NewSocket
       astream.InitializePrefix(client.InputStream, False, client.OutputStream, "astream")
       UpdateState(True)
     End If
   Loop
End Sub
 

rkwan

Member
Licensed User
Thank you very much, Erel!

However, I am still too dummy to get it working with my original ESP8266 Wifi server, which is just sending numeric strings all the time...

1. I can modify your B4i sample and build it ok to my iPhone.

However, unlike my B4a version, the connection to my ESP8266 Wifi server is no longer static but broken easily and I don't see any trigger to AStream_NewData() tor receive the hard-coded numeric string like "123" even though I already simplified it into:

B4X:
Sub astream_NewData (Buffer() As Byte)
    Dim Rx_String As String

        Rx_String = BytesToString(Buffer, 0, 4 , "UTF8")
       edtName.Text = Rx_String
End Sub
2. Since I could not the Rx side working, I decided to modify the Tx side to see if my ESP8266 Wifi Server can receive from B4i app.

Again, I simplified:-

B4X:
Sub btnSend_Click
   Dim conv As ByteConverter
    Dim data() As Byte = conv.StringToBytes(edtName.Text, "UTF8")
    SendData(data)
End Sub
so when I put "12345" in the edtName.Text and press "Send",
I can see from the ESP8266 UART log that a special character + "12345" got received.

I am curious if I can remove the special character at the beginning easily?!
But I verified that the Send function is ok in B4i, not the Receive function I need.

Any other suggestion please?

If possible, I would like to preserve my ESP8266 Wifi server code the same for both B4a and B4i app
but if it's not possible, I will try to modify my ESP8266 code then.
My ESP8266 code is again pretty straight-forward if I just send hard-coded data out to B4a and B4i app:

B4X:
void loop() {
  client1 = server1.available();  
   
  String readString;
   
  // Wait until the client sends some data  
  Serial.println("new client");  
  while( client1.connected() )  
  {  
    char buffer[10];

    //Hard-coded string to be sent to B4a & B4i app for testing 
    readString = "S123";
   
      if (readString.substring(0,1) == "S")
      {
         char charBuf[4];
         int count = 0;
         readString.substring(1).toCharArray(charBuf, 4) ;
         for (int i=0; i<readString.length(); i++)
         {
            if (isDigit(charBuf[i]))
            {
                count++;
            }
         }
         if (count == readString.length()-1)
         {
            distance = atoi(&charBuf[0]);
         }
          snprintf(buffer,5,"%4d",distance);  
           
          client1.print(buffer);  // Sending "123" to B4a & B4i app, B4a - OK but not B4i
          client1.flush();
       }
      readString="";

  } // while (client.connected)

  Serial.println("new client disconnected");  
  client1.stop();  
}

Thanks & Best Regards,
Robert
 
Status
Not open for further replies.
Top