Android Code Snippet Auto-connect without predefined client or server

A post that Erel made on Facebook about client/server auto-connect reminded me that I had this code & should probably share it because it doesn't require either device to be "hard" configured as either a client or a server.

This is the code I use in my Five Dice game to connect 2 devices on a local network. It will initially look for another device sending a server broadcast & if it doesn't find one, will send one itself.

The first device to receive a server broadcast message sends back a client message & then both devices connect via TCP sockets (one a server & the other a client).

When I first wrote this code, I assumed that all networks that allow UDP broadcast (some block it, so I also have a manual IP configuration option for my users) would use the .255 address - however I see that Erel has recently posted a code snippet that will find the correct broadcast address for the local network, so I might look at implementing that in my code. Having said that, I've only had a handful of users tell me that they can't get the auto-connect to work (which I assumed was because their network was blocking UDP broadcasts) & they have been able to successfully connect using the manual option.

B4X:
#Region  Service Attributes
    #StartAtBoot: False
#End Region

'=========================================================================================================
'This service looks for a UDP broadcast "I'm a server" message & if it doesn't immediately see one
'will start sending its own while simultaneously looking for another device broadcasting the same message via UDP. During the broadcast it keeps a TCP server socket open waiting for a connection.
'
'The first device to receive a server broadcast message sends an "I'm a client" message to the server 'IP address, then stops the UDP broadcast & opens a TCP client socket connected to the other device's 'TCP server socket.
'
'When the other device receives the "I'm a client" message, it closes it's UDP socket & accepts the 'client connection on the TCP socket.
'
'Both devices then open an asynchronous stream to use for sending messages to each other. 

Sub Process_Globals
    Public sMyIP As String
    Public aStream() As AsyncStreams
    Public sClientIP() As String
    Public iClientsConnected As Int = 0
  
    Private UDPSocket1 As UDPSocket 
    Private Tablet As Reflector
    Private UDPPort As Int = 13130
    Private TCPPort As Int = 13131
    Private sBroadCastIP As String
    Private tmr As Timer
    Private tcpClient() As Socket
    Private tcpServer As ServerSocket
    Private sIP(4) As String
    Private bFoundServer As Boolean = False
    Private sServerIP As String
    Private iTryCount As Int = 0
  
End Sub

Sub Service_Create
    Tablet.Target = Tablet.GetContext
    Tablet.Target = Tablet.RunMethod2("getSystemService", "wifi", "java.lang.String")
    Tablet.Target = Tablet.RunMethod2("createMulticastLock", "mylock", "java.lang.String")
    Tablet.RunMethod2("setReferenceCounted", False, "java.lang.boolean")     'not really necessary but safer
    Tablet.RunMethod("acquire")                                            'acquire the lock
End Sub

Sub Service_Start (StartingIntent As Intent)
    Private ws As PhoneWakeState
      
    Private tcpClient(Starter.iNumPlayers) As Socket
    Private aStream(Starter.iNumPlayers) As AsyncStreams
    Private sClientIP(Starter.iNumPlayers) As String
  
    ws.KeepAlive(True)
    GetNetIP
    UDPSocket1.Initialize("UDP", UDPPort, 256)
    bFoundServer = False
    iClientsConnected = 0
  
    CheckForServer
  
End Sub

'Send "I'm a server" message
Sub SendMYID
    UDPSendMsg("Server", sBroadCastIP)
End Sub

Sub UDPSendMsg(msg As String, IPAddr As String)
    Private Packet As UDPPacket
    Private data() As Byte 

    data = msg.GetBytes("UTF8")
    Packet.Initialize(data, IPAddr, UDPPort)   
    UDPSocket1.Send(Packet)
End Sub

'Receive UDP broadcast messages
Sub UDP_PacketArrived (Packet As UDPPacket)
    Private msg As String
    msg = BytesToString(Packet.Data, Packet.Offset, Packet.Length, "UTF8")
    Private IP As String = Packet.HostAddress
    Private parts() As String = Regex.Split("~", msg)
  
  
    If IP <> sMyIP Then
        Select Case parts(0)
            'If the other device is a server
            Case "Server"
                tmr.Enabled = False
                'Log("Server IP: " & IP)
                ToastMessageShow("Player found at " & IP, True)
                UDPSendMsg("Client", IP)
                bFoundServer = True
                sServerIP = IP
                UDPSocket1.Close
                tmr.Interval = 2000
                tmr.Enabled = True
            'If the other device is a client
            Case "Client"
                tmr.Enabled = False
                ToastMessageShow("Player found at " & IP, True)
                'Log("Client IP: " & IP)
                UDPSocket1.Close
            Case Else           
        End Select
    End If
End Sub

Sub Service_Destroy
    Kill_Connections
End Sub

Sub Kill_Connections
    Private ws As PhoneWakeState
    Private iCnt As Int
  
    UDPSocket1.Close
    Tablet.RunMethod("release")
    For iCnt = 0 To tcpClient.Length - 1
        tcpClient(iCnt).Close
    Next
    tcpServer.Close
    tmr.Enabled = False
    ws.ReleaseKeepAlive
  
End Sub

'Get the network address & create a UDP broadcast address
Sub GetNetIP
    Private netS As ServerSocket
  
    netS.Initialize(0, "")
    sMyIP = netS.GetMyIP
    netS.Close
    If Not(Starter.cOpts.ManualNet) Then
        sIP = Regex.Split("\.", sMyIP)
        'UDP broadcasts are on the .255 address of the network
        sBroadCastIP = sIP(0) & "." & sIP(1) & "." & sIP(2) & "." & "255"
    Else
        sBroadCastIP = Starter.cOpts.OtherIP
    End If

End Sub

Sub CheckForServer
    If tmr.IsInitialized = False Then tmr.Initialize("tmr", 5000)
    tmr.Enabled = True
End Sub

Sub tmr_Tick
  
    'Haven't found the server yet
    If bFoundServer = False Then
        iTryCount = iTryCount + 1
        If tcpServer.IsInitialized = False Then
            tcpServer.Initialize(TCPPort, "Server")
            tcpServer.Listen
        End If
        tmr.Enabled = False
        tmr.Interval = 2000
        tmr.Enabled = True
        SendMYID
    Else
        'Found the server so will TCP connect as a client
        tmr.Enabled = False
        tcpClient(0).Initialize("Client")
        tcpClient(0).Connect(sServerIP, TCPPort, 10000)
    End If
  
End Sub

Sub Client_Connected(Successful As Boolean)
    tmr.Enabled = False
  
    If Successful Then
        'Connected as a client, so open a stream
        ToastMessageShow("Connected", False)
        Starter.NetGame.Initialize
        Starter.NetGame.PlayerMe = Starter.NetGame.PLAYER_SECOND
        aStream(0).InitializePrefix(tcpClient(0).InputStream, False, tcpClient(0).OutputStream, "AStream")
        Starter.NetGame.SendMsg("Connected:" & Starter.cOpts.GameMode & ":" & Starter.NetGame.PlayerMe)
    Else
        'Connection failed
        tcpClient(0).Close
        ToastMessageShow("Connection Failed", False)
        StopService("")
    End If
  
End Sub

Sub Server_NewConnection(Successful As Boolean, NewSocket As Socket)
    tmr.Enabled = False
    UDPSocket1.Close
    If Successful Then
        'Connected to a client
        iClientsConnected = iClientsConnected + 1
        ToastMessageShow("Connected Player " & (iClientsConnected + 1) & " of " & Starter.iNumPlayers, False)
        If iClientsConnected = 1 Then
            Starter.NetGame.Initialize
            Starter.NetGame.PlayerMe = Starter.NetGame.PLAYER_FIRST
        End If
        tcpClient(iClientsConnected - 1) = NewSocket
        aStream(iClientsConnected - 1).InitializePrefix(tcpClient(iClientsConnected - 1).InputStream, False, tcpClient(iClientsConnected - 1).OutputStream, "AStream")
        sClientIP(iClientsConnected - 1) = Get_Client_IP(NewSocket)
        Starter.NetGame.SendMsg("Connected:" & Starter.cOpts.GameMode & ":" & iClientsConnected)
    Else
        'Connection to client failed
        tcpServer.Close
        ToastMessageShow("Connection Failed", False)
        StopService("")
    End If
End Sub

'Get the client IP address from the socket
Sub Get_Client_IP(ClientSock As Socket) As String
    Private r As Reflector
  
      r.Target = ClientSock
      r.Target = r.GetField("socket")
      r.Target = r.RunMethod("getInetAddress") 'InetAddress
      Return r.RunMethod("getHostAddress")
End Sub

'Received a message on the stream
Sub AStream_NewData (Buffer() As Byte)
    Private msg As String
  
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    'Do something with the msg string
  
End Sub
 
Top