B4J Question MQTT Broker

Declan

Well-Known Member
Licensed User
Longtime User
I have been playing with the Chat Room example to understand the workings of MQTT.
Is there a simple example of a MQTT Broker that will accept connections from remote devices and reply to the remote devices?
My remote devices are GSM devices that connect to the server with MQTT.
There are numerous examples that use WiFi libraries - but I am connecting via GSM.
 
Solution
For example:

All clients will subscribe at YouGAMETopic/PlayerXX --->where XX will be then number of any client... they are gonna send their message there... and will wait...

The one client (with the role of server if we can tell it like that) will read all (if subscribed there) the YouGAMETopic/ and could send at YouGAMETopic/PlayerXX/AnswerofServer ... caution will also read AnswerofServer... so the message must be at least a MAP (could be) with 2 keys ... that will show who sent message (if server not want to process it) and a key with the message you want...

also clients to receive the answers must be subscribed to YouGAMETopic/PlayerXX/AnswerofServer ....

Actually you can do everything ... almost everything.. don't know the...

Magma

Expert
Licensed User
Longtime User
I have been playing with the Chat Room example to understand the workings of MQTT.
Is there a simple example of a MQTT Broker that will accept connections from remote devices and reply to the remote devices?
My remote devices are GSM devices that connect to the server with MQTT.
There are numerous examples that use WiFi libraries - but I am connecting via GSM.
I think it is better/easy replying a client running the same time in machine hosting the MQTT Broker (server).. (have a client that will replying...)

Think MQTT broker - as middle/as the table of conversation...
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
There is some confusion here.
The broker "doesn't care" how the clients connect. The exact same broken will work whether the client is running on the same machine, connected to the same local network or over the internet.
Making it accessible over the internet requires some configuration. You need to open the relevant port in Windows firewall and you need to configure the router to forward the traffic to the correct computer.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
There is some confusion here.
The broker "doesn't care" how the clients connect. The exact same broken will work whether the client is running on the same machine, connected to the same local network or over the internet.
Making it accessible over the internet requires some configuration. You need to open the relevant port in Windows firewall and you need to configure the router to forward the traffic to the correct computer.
Yes the relevant port is open on the server and the IP set in the Broker is the static IP of the server.
I am attempting a simple Broker in B4J that will accept a MQTT Client connection from my remote devices.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
As I wrote, from the broker side it doesn't matter whether the client is on the same computer or communicating from Mars through NASA proxy.
Mmmmm
Ok, let me explain:
I have a device in the DRC (Democratic Republic of Congo) which is a few thousand kilometers from me in South Africa - Not quite as far as Mars (and NASA refused my request to use their proxy).
I can upload firmware to the device with OTA.
The reason that I would like to port over to MQTT is that with both HTTP and UDP, connections to my server have proved to be extremely unreliable.
I now have a firware version on the device that establishes a MQTT connection and this has proven to be very reliable (1000 connections out of 1000 connections - 100%)

I am running the following code on my server to test incoming connections:
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    #AdditionalJar: sqlite-jdbc-3.7.2
#End Region

Sub Process_Globals
    Dim Broker As MQTTBrokerExtended
    Dim Const Port As Int = 17189

    Dim ActivePlayers As Map
    Dim WaitingPlayer As String
    Public SQL As SQL
    Dim Const DBName As String = "store.db"

    Dim Serializator As B4XSerializator
    Type typInitialData(PlayerID1 As String, PlayerID2 As String, MatchID As String)
End Sub

Sub AppStart (Args() As String)

    'Starts the broker
    Broker.Initialize(Port, "Broker")
    Broker.DebugLog = False
    Broker.Start
    StartMessageLoop 'Non-UI app
End Sub

Sub Broker_Connect(ClientID As String, UserName As String, Password() As Byte, ProtocolName As String, ProtocolVersion As Byte, QOS As String, KeepAlive As Int, IsCleanSession As Boolean, IsDupFlag As Boolean, IsUserFlag As Boolean, IsPasswordFlag As Boolean, IsWillFlag As Boolean)
    If Password = Null Then
        Log("CONNECT : " & ClientID & " UserName=" & UserName & " Password=null ProtocolName=" & ProtocolName & " ProtocolVersion=" & ProtocolVersion)
'    Else
'        Log("CONNECT : " & ClientID & " UserName=" & UserName & " Password=" & BytesToString(Password, 0, Password.Length, "UTF-8") & " ProtocolName=" & ProtocolName & " ProtocolVersion=" & ProtocolVersion)
    End If
    Log("        : QOS=" & QOS & " KeepAlive=" & KeepAlive & " IsCleanSession=" & IsCleanSession & " IsDupFlag=" & IsDupFlag & " IsUserFlag=" & IsUserFlag & " IsPasswordFlag=" & IsPasswordFlag & " IsWillFlag=" & IsWillFlag)
End Sub

Sub Broker_Disconnect(ClientID As String)
    Log("DISCONNECT : " & ClientID)
End Sub

Sub Broker_LastWill(ClientID As String, QOS As Byte, TopicName As String, Payload() As Byte, IsRetain As Boolean)
    Log("LASTWILL : " & ClientID & " QOS=" & QOS & " Topic=" & TopicName & " Payload=" & Payload.Length & " IsRetain=" & IsRetain)
End Sub

Sub Broker_Publish(ClientID As String, QOS As String, TopicName As String, Payload() As Byte, IsDup As Boolean, IsRetain As Boolean)
    Log("PUBLISH : " & ClientID & " QOS=" & QOS & " Topic=" & TopicName & " Payload=" & Payload.Length & " IsDup=" & IsDup & " IsRetain=" & IsRetain)
End Sub

Sub CreateMessage(Data As Object) As Byte()
    Return Serializator.ConvertObjectToBytes(Data)
End Sub

Sub Broker_Suscribe(ClientID As String, RequestedQOS As String, TopicFilter As String)
    Log("SUSCRIBE : " & ClientID & " QOS=" & RequestedQOS & " TopicFilter=" & TopicFilter)
    If TopicFilter = ClientID Then
        'Is there a pending match for this player?
        If ActivePlayers.ContainsKey(ClientID) Then
            'Nothing to do because the broker re-sends automatically the last retained message
        Else If WaitingPlayer <> ClientID Then
            'Is there a player waiting for another player?
            If WaitingPlayer <> "" Then
                'Creates a match for these players
                Dim Data As typInitialData
                Data.Initialize
                Data.PlayerID1 = WaitingPlayer
                Data.PlayerID2 = ClientID
                Data.MatchID = "MID" & DateTime.Now & "|" & Data.PlayerID1 & Data.PlayerID2 & Rnd(100, 999)

                'Updates the DB
                SQL.BeginTransaction
                Try
                    SQL.ExecNonQuery("INSERT INTO ActivePlayers VALUES (""" & Data.PlayerID1 & """,""" & Data.MatchID & """)")
                    SQL.ExecNonQuery("INSERT INTO ActivePlayers VALUES (""" & Data.PlayerID2 & """,""" & Data.MatchID & """)")
                    SQL.ExecNonQuery("DELETE FROM WaitingPlayer")
                Catch
                    Log(LastException)
                    SQL.Rollback
                    Broker.InternalPublish(Data.PlayerID1, CreateMessage("ERROR: The server encountered a database error."), Broker.EXACTLY_ONCE, False)
                    Broker.InternalPublish(Data.PlayerID2, CreateMessage("ERROR: The server encountered a database error."), Broker.EXACTLY_ONCE, False)
                    Return
                End Try
                SQL.TransactionSuccessful

                'Updates the internal data
                ActivePlayers.Put(Data.PlayerID1, Data.MatchID)
                ActivePlayers.Put(Data.PlayerID2, Data.MatchID)
                WaitingPlayer = ""

                'Sends the MatchID to the players in a private message
                Dim Msg() As Byte = CreateMessage(Data)
                Broker.InternalPublish(Data.PlayerID1, Msg, Broker.EXACTLY_ONCE, True)
                Broker.InternalPublish(Data.PlayerID2, Msg, Broker.EXACTLY_ONCE, True)

                'Sends a shared message in the match topic to start the sequence of exchanges
                Broker.InternalPublish(Data.MatchID, Msg, Broker.EXACTLY_ONCE, True)
            Else
                'Puts the new player in the waiting room
                Try
                    SQL.ExecNonQuery("INSERT INTO WaitingPlayer VALUES (""" & ClientID & """)")
                Catch
                    Log(LastException)
                    Broker.InternalPublish(ClientID, CreateMessage("ERROR: The server encountered a database error."), Broker.EXACTLY_ONCE, False)
                    Return
                End Try
                WaitingPlayer = ClientID
            End If
        End If
    End If
End Sub

Sub Broker_Unsuscribe(ClientID As String, TopicFilter As String)
    Log("UNSUSCRIBE : " & ClientID & " TopicFilter=" & TopicFilter)
    If TopicFilter <> ClientID Then
        'Updates the DB
        Try
            SQL.ExecNonQuery("DELETE FROM ActivePlayers WHERE PlayerID=""" & ClientID & """")
        Catch
            Log(LastException)
            Broker.InternalPublish(ClientID, CreateMessage("ERROR: The server encountered a database error."), Broker.EXACTLY_ONCE, False)
            Return
        End Try

        'Updates the internal data
        ActivePlayers.Remove(ClientID)
        Log(ClientID & " is no longer active")

        'Removes the retained message from the private topic
        Broker.InternalPublish(ClientID, Array As Byte(), Broker.EXACTLY_ONCE, True)
    End If
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

The Log is:
B4X:
Waiting for debugger to connect...
Program started.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.PlatformDependent0 (file:/C:/Program%20Files/Anywhere%20Software/B4J/Libraries/moquette8.jar) to field java.nio.Buffer.address
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.PlatformDependent0
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
CONNECT : HB002 UserName=null Password=null ProtocolName=MQIsdp ProtocolVersion=3
        : QOS=MOST_ONE KeepAlive=20 IsCleanSession=true IsDupFlag=false IsUserFlag=false IsPasswordFlag=false IsWillFlag=false
DISCONNECT : HB002
CONNECT : HB002 UserName=null Password=null ProtocolName=MQIsdp ProtocolVersion=3
        : QOS=MOST_ONE KeepAlive=20 IsCleanSession=true IsDupFlag=false IsUserFlag=false IsPasswordFlag=false IsWillFlag=false

So, I know that I can establish a connection using MQTT from my remote device.
 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
My remote device will establish the connection send data to the MQTT Broker on the server.
The server will then reply to the device.
The device will then shut the connection.
It does not matter whether the reply message from the server is sent to all remote devices (Clients) as the device will parse the message that contains it unique ID.
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
My remote device will establish the connection send data to the MQTT Broker on the server. (OK)

The server will then reply to the device. (The Sever - Broker doesn't replies - only keeps the messages/data)
For replying you must run a client anywhere (you want at server side, or in a different area) that will read all messages and will send new messages to broker - ready to be read from other clients...
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
An MQTT client establishes a connection with the MQTT broker. Once connected, the client can either publish messages, subscribe to specific messages, or do both. When the MQTT broker receives a message, it forwards it to subscribers who are interested.

 
Upvote 0

Declan

Well-Known Member
Licensed User
Longtime User
For replying you must run a client anywhere (you want at server side, or in a different area) that will read all messages and will send new messages to broker - ready to be read from other clients...
So I must create a server side Client?
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
For example:

All clients will subscribe at YouGAMETopic/PlayerXX --->where XX will be then number of any client... they are gonna send their message there... and will wait...

The one client (with the role of server if we can tell it like that) will read all (if subscribed there) the YouGAMETopic/ and could send at YouGAMETopic/PlayerXX/AnswerofServer ... caution will also read AnswerofServer... so the message must be at least a MAP (could be) with 2 keys ... that will show who sent message (if server not want to process it) and a key with the message you want...

also clients to receive the answers must be subscribed to YouGAMETopic/PlayerXX/AnswerofServer ....

Actually you can do everything ... almost everything.. don't know the type of messages and the length for your projects...

Ofcourse all that can be different... any much simpler if you understand the protocol... you can also send any object with b4x serializator ! so you can have only one channel... and sending a map with more keys to be specific what sending... (from which client, towho, message, from server... etc)
 
Upvote 0
Solution
Top