Android Question 6 computers communicating via AsyncStreamsObject

almontgreen

Active Member
Licensed User
Longtime User
I have an android X80h tablet and Tronsmart box communicating beautifully over a portable private hotspot running on the tablet using the brilliant AsyncStreamsObject library. Problem is, I need to talk to 4 more Tronsmart boxes from the tablet. I've read the forums and there are comments regarding adding sockets to do this but I don't know enough to understand. I looked at the CCTV examples and I'm still a bit lost.

I'd hate to go blutooth... but that could be an option I suppose. :(
 

almontgreen

Active Member
Licensed User
Longtime User
Bluetooth will only make it more complicated.

The B4J example here: https://www.b4x.com/android/forum/threads/b4j-cctv-example.34695/#post-203347 (2nd post) manages multiple AsyncStreams. You need to do something similar.
Ok, I will study cctv example harder! Sorry for 2nd post, wasn't sure 1st post actually posted. The support here is unsurpassed along with the patience of those willing to help people that are less accomplished trying to understand things. I am always surprised on the upside with what the tools from Anywhere Software can achieve and how rapidly things can happen. I'm not a "professional coder" by any measure, but I can make things work surprisingly well thanks to the tools and support available here.
 
Upvote 0

almontgreen

Active Member
Licensed User
Longtime User
Is it possible to have the server send info to the clients and have at least one client respond back to the server? I see where the clients get the server image with the Sub astream_NewData but I don't understand the reason to use AsyncStreamsObject vs. AsyncStreams ? So, can you have a server send info out to multiple clients and have at least one client respond? Before I start testing it would be useful to know if this is possible...
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

almontgreen

Active Member
Licensed User
Longtime User
AsyncStreamsObject wraps AsyncStreams. It is useful in cases where you want to send and receive objects instead of bytes: https://www.b4x.com/android/forum/t...ceive-objects-instead-of-bytes.30543/#content

The server can send data to each of the asyncstreams (they are not related to each other).
Thanks for pointing me in the right direction. First, I will try server sending to 2 clients, then I will try sending and receiving between server and two clients, then scale up to 4 clients. I assume from your answer that this is possible.
 
Upvote 0

almontgreen

Active Member
Licensed User
Longtime User
Ok, on a new connection I initialize a stream and start the stream input and output and write an object - then wait for a stream newObject response. I then close the stream and wait for a new connection and repeat. In this way I can communicate to several computers. I understand that much now. Apparently, you can't have more than one stream connected at any given time. Is that right? That was my takeaway from the cctv example. The issue with this approach is that an event happens only on the one computer that listens or acts as the server. The other computers have to keep trying to connect until successful and then close the connection as soon as possible so another computer can connect.

Do I have this right?

B4X:
Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        If astreamO.IsInitialized Then astreamO.Close
        astreamO.Initialize(Me, "astreamO")
        StartAstream(NewSocket)
   End If
End Sub

Sub StartAstream(s As Socket)
    astreamO.Start(s.InputStream, s.OutputStream)
    Dim p As StuffToSend
    p.info1 = "a1"
    p.info2 = sf.Left(DateTime.Now,sf.Len(DateTime.Now)-1)
    p.info3 = "z1"
    astreamO.WriteObject("form", p)
End Sub

Sub astreamO_NewObject(Key As String, Value As Object)
    Select Key
'       Case "simple value"
            Dim number As Int = Value
            ToastMessageShow("Close Connection" & number, False)
            astreamO.Close
   End Select
End Sub
 
Last edited:
Upvote 0

almontgreen

Active Member
Licensed User
Longtime User
I am revisiting things after a break, and I've spent all day reading posts about using AsyncStreams, Sockets and multiple connections. I've stared at the B4J CCTV code and I don't understand how to handle multiple connections as it relates to trying to send and receive using AsyncStreamsObject with multiple sockets using more than one at a time.

So far, I get that the place to start is with the Sub Server_NewConnection and putting each AsyncStream into a Type container. I don't understand what qualifies as "successful" because that doesn't seem to happen for more than one connection at a time. I love how the AsyncStreamsObject works but translating the individual AsyncStreams info from the B4J CCTV project into code that works with the AsyncStreamsObject for Android seems to be above my pay grade ;^)

I am pretty stupid, but I don't think I'm the only one that doesn't understand working with a connection between more than two computers. A bit more info or tutorial would be incredibly helpful! I just upgraded to the new version of B4A and am blown away. I'm really struggling to understand how to keep track of multiple sockets and how the triggered events work. I thought about initializing multiple AsyncStreamsObject objects but that doesn't seem right?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Managing multiple connections is indeed not simple.

It will be much simpler and more reliable to create a real server with B4J (not an Android app).

I will try to explain this code:
B4X:
Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
   If Successful Then
     Dim astream As AsyncStreams
     astream.InitializePrefix(NewSocket.InputStream, False, NewSocket.OutputStream, "astream")
     For Each li As LabelAndImageView In slots
       If li.connected = False Then
         li.connected = True
         li.lbl.Text = "Status: Connected"
         connections.Put(astream, li)
         Exit
       End If
     Next
   Else
     Log(LastException)
   End If
   server.Listen
End Sub

A new client has connected to the server so this event is raised.
We create an AsyncStreams object that works with this socket.
Now we need to store this AsyncStreams object. In this case the astream itself is used as the key and the value is an additional object which we will later use to show the image.

Later when there is NewData available:
B4X:
Sub astream_NewData (Buffer() As Byte)
'   Log("received: " & DateTime.GetSecond(DateTime.Now))
   Dim In As InputStream
   In.InitializeFromBytesArray(Buffer, 0, Buffer.Length)
   Dim img As Image
   img.Initialize2(In)
   Dim liiv As LabelAndImageView = connections.Get(Sender) '<-- this is the interesting code
   liiv.iv.SetImage(img)
End Sub

We take the additional object from the Map and we set the received image to this object.

In the CCTV example the server is not sending any information. If you need to send information to a specific client then you can use another Map that maps between the client id and the astream object.
 
Upvote 0

almontgreen

Active Member
Licensed User
Longtime User
Ok, so I've got something working on 3 tablets where two tablets send text to a server and the server then can send text to the two tablets. My code is terrible I'm sure, but it works! I'm starting to understand how this works and how it can be greatly simplified from what I have! Please take a look at the server side app. The last sub is a touch event that gets a swipe. I didn't use any layout as that makes it easier for me to understand and put the code into other projects. I am confused about what should go in the main and what should go somewhere else. I hope this helps somebody along with me! :eek:
B4X:
#Region  Project Attributes 
    #ApplicationLabel: Server
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private server As ServerSocket
   
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim connections As Map
    Dim Imageinfo1, Imageinfo2, Imageinfo3, Imageinfo4, Imageinfo5, Imageinfo6 As String
    Dim computer1, computer2, computer3, computer4, computer5, computer6 As String
    Dim slots As List
    Dim Panel1 As Panel
    Dim Canvas1 As Canvas
    Dim DestRect As Rect
    Dim n As Int
    Dim nz As Int
    Dim v_nbr As Int
    Dim nString As String
    Dim sf As StringFunctions
    Type LabelAndImageView (lbl As String, iv As String, connected As Boolean)
End Sub

Sub Activity_Create(FirstTime As Boolean)
    n=0
    nz=0
    nString=" "
    slots.Initialize
    connections.Initialize
    computer1 = "0"
    computer2 = "0"
    computer3 = "0"
    computer4 = "0"
    computer5 = "0"
    computer6 = "0"
    slots.Add(CreateSlot(computer1, Imageinfo1))
    slots.Add(CreateSlot(computer2, Imageinfo2))
    slots.Add(CreateSlot(computer3, Imageinfo3))
    slots.Add(CreateSlot(computer4, Imageinfo4))
    slots.Add(CreateSlot(computer5, Imageinfo5))
    slots.Add(CreateSlot(computer6, Imageinfo6))
    server.Initialize(17178, "server")
    server.Listen
   
    DestRect.Initialize(109, 0, 1279, 517)
    Panel1.Initialize("Panel1")
    Activity.AddView(Panel1,0,0, Activity.Width, Activity.Height)
    Canvas1.Initialize(Panel1)
    Canvas1.DrawText("My IP: " & server.GetMyIP,30dip,30dip, Typeface.DEFAULT,20,Colors.white,"LEFT")
    Panel1.Invalidate
    Msgbox("ready","Alert")
End Sub

Sub Activity_Resume
    server.Listen
End Sub

Sub Activity_Pause (UserClosed As Boolean)
Log("paused")
End Sub

Private Sub CreateSlot(lbl As String, iv As String) As LabelAndImageView
    Dim liiv As LabelAndImageView
    liiv.Initialize
    liiv.iv = iv
    liiv.lbl = lbl
    liiv.connected = False
    Return liiv
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    nz=nz+1
    nString = NumberFormat(nz, 0, 0)&" Connection"
    If Successful Then
        Dim Astream As AsyncStreams
        Astream.InitializePrefix(NewSocket.InputStream, False, NewSocket.OutputStream, "Astream")
        For Each li As LabelAndImageView In slots  'There are now 6 slots
            If li.connected = False Then
                li.connected = True
                connections.Put(Astream, li)  'put info in map
                'Log(li)
                Canvas1.DrawText("██████████████████████████████████████████████████████████████████",20dip,50dip, Typeface.DEFAULT,20,Colors.black,"LEFT")
                Canvas1.DrawText(Astream,20dip,50dip, Typeface.DEFAULT,20,Colors.white,"LEFT")
                Panel1.Invalidate
                Exit
            End If
        Next
    Else
        Log(LastException)
        Canvas1.DrawText("████████████████████████",20dip,50dip, Typeface.DEFAULT,20,Colors.black,"LEFT")
        Canvas1.DrawText("Status: Connect Failed",20dip,50dip, Typeface.DEFAULT,20,Colors.white,"LEFT")
        Panel1.Invalidate

    End If
    server.Listen
End Sub

Sub Astream_NewData (Buffer() As Byte)
    n=n+1
    nString=sf.Right(sf.left(connections.Get(Sender),6),1)
    Dim msg As String
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
        If nString<> sf.Right(sf.Left(msg,10),1) Then
            n=1
            For Each li As LabelAndImageView In slots
                If li.lbl=nString Then
                    li.lbl=sf.Right(sf.Left(msg,10),1)
                    Select True
                        Case n=1
                            computer1=sf.Right(sf.Left(msg,10),1)
                        Case n=2
                            computer2=sf.Right(sf.Left(msg,10),1)
                        Case n=3
                            computer3=sf.Right(sf.Left(msg,10),1)
                        Case n=4
                            computer4=sf.Right(sf.Left(msg,10),1)
                        Case n=5
                            computer5=sf.Right(sf.Left(msg,10),1)
                        Case n=6
                            computer6=sf.Right(sf.Left(msg,10),1)
                    End Select
                    Exit
                End If
                n=n+1
            Next
        End If
    Canvas1.DrawText("██████████████████████████████████████████████████████████████████",20dip,70dip, Typeface.DEFAULT,20,Colors.black,"LEFT")
    Canvas1.DrawText(nString&" "&msg,20dip,70dip, Typeface.DEFAULT,20,Colors.white,"LEFT")
    Panel1.Invalidate
End Sub

Sub Send1(OutStr As String)
    Dim buffer() As Byte
    buffer = OutStr.GetBytes("UTF8")
    Dim Astream As AsyncStreams = connections.GetKeyAt(sf.Val(computer2)-1) 'subtract 1 because it starts with 0
    Astream.Write(buffer)
End Sub

Sub Send2(OutStr As String)
    Dim buffer() As Byte
    buffer = OutStr.GetBytes("UTF8")
    Dim Astream As AsyncStreams = connections.GetKeyAt(sf.Val(computer1)-1) 'subtract 1 because it starts with 0
    Astream.Write(buffer)
End Sub

Sub Send3(OutStr As String)
    Dim buffer() As Byte
    buffer = OutStr.GetBytes("UTF8")
    Dim Astream As AsyncStreams = connections.GetKeyAt(sf.Val(computer3)-1) 'subtract 1 because it starts with 0
    Astream.Write(buffer)
End Sub

' Add additional subs for up to 6 computers

Sub Astream_Error
    Log("Error: " & LastException)
    Dim Astream As AsyncStreams = Sender
    ReleaseSlot(Astream)
    Astream.Close
End Sub

Private Sub ReleaseSlot(Astream As AsyncStreams)
    If connections.ContainsKey(Astream) Then
        Dim liiv As LabelAndImageView = connections.Get(Astream)
        liiv.connected = False
        Canvas1.DrawText("████████████████████████",20dip,50dip, Typeface.DEFAULT,20,Colors.black,"LEFT")
        Canvas1.DrawText("Status: Disconnected",20dip,50dip, Typeface.DEFAULT,20,Colors.white,"LEFT")
        Panel1.Invalidate
        connections.Remove(Astream)
    End If
End Sub

Sub Astream_Terminated
    Dim Astream As AsyncStreams = Sender
    ReleaseSlot(Astream)
    Astream.Close
End Sub

Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
    Select True
        Case Action = 0
            v_nbr = X
        Case Action = 2
           
        Case Action = 1
            Select True
                Case v_nbr< (X-60)
                    Send1("Right")
                Case v_nbr> (X+60)
                    Send2("Left")
            End Select
    End Select
End Sub
 
Upvote 0
Top