Android Question Finding and connecting with another device on the same subnet

Derek Johnson

Active Member
Licensed User
I have an app that will be used to communicate using a TCPIP socket to another device with the same subnet.

When they are setting up, I don't want the users to have to type in the address of the other device.

What scheme would be best to let the 2 devices find one another?

I've ruled out pinging every device because it is far to slow.
 

Computersmith64

Well-Known Member
Licensed User
Your best bet is to use UDP broadcast, then when the devices find each other switch to TCP. I've pasted in the code from the NetConnect service in my Five Dice app to help get you started...

B4X:
'=========================================================================================================
'This service sends out a UDP broadcast "I'm a server" message while simultaneously looking for another
'device broadcasting the same message via UDP & keeping a TCP server socket open waiting for a connection.
'
'The first device to receive a 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
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Public sMyIP As String
    Public aStream() As AsyncStreams
    Public sClientIP() As String
    Public iClientsConnected As Int = 0
    Public sServerIP As String
  
    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 bFoundServer As Boolean = False
    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)
    CallSub3(Main, "Update_btnRoll", "Connecting...", False)
    GetNetIP
    bFoundServer = False
    iClientsConnected = 0
    If Not(Starter.cOpts.ManualNet) Then
        If sBroadCastIP = "" Then
            CallSub3(Main, "Update_btnRoll", "Connection Failed", False)
            CallSub(Main, "noUDPBroadcast")
            Return
        End If
        UDPSocket1.Initialize("UDP", UDPPort, 256)
        sServerIP = ""
        CheckForServer
    Else
        If Starter.cOpts.PlayerOne Then
            tcpServer.Initialize(TCPPort, "Server")
            tcpServer.Listen
        Else
            tcpClient(0).Initialize("Client")
            tcpClient(0).Connect(sServerIP, TCPPort, 10000)
        End If
    End If
End Sub

'Send "I'm a server" message
Sub SendMYID
    Log("Sending my ID")
    UDPSendMsg("Five Dice 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)
    'Log("Sending UDP message")  
    UDPSocket1.Send(Packet)
    'Log("UDP message sent")
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
        Log(msg)
        Select Case parts(0)
            'If the other device is a server
            Case "Five Dice Server"
                tmr.Enabled = False
                Log("Server IP: " & IP)
                ToastMessageShow("Player found at " & IP, True)
                UDPSendMsg("Five Dice Client", IP)
                bFoundServer = True
                sServerIP = IP
                Log("Closing Socket")
                UDPSocket1.Close
                Log("Socket Closed")
                tmr.Interval = 2000
                tmr.Enabled = True
            'If the other device is a client
            Case "Five Dice Client"
                Log("Found a client")
                ToastMessageShow("Player found at " & IP, True)
            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
        sBroadCastIP = GetBroadcastAddress
        Log("Broadcast Address: " & sBroadCastIP)
    Else
        'sBroadCastIP = Starter.cOpts.OtherIP
        sServerIP = Starter.cOpts.OtherIP
    End If

End Sub

'Returns the UDP broadcast address.
'Returns an empty string if not available.
Sub GetBroadcastAddress As String
    Dim niIterator As JavaObject
    niIterator = niIterator.InitializeStatic("java.net.NetworkInterface").RunMethod("getNetworkInterfaces", Null)
    Do While niIterator.RunMethod("hasMoreElements", Null)
        Dim ni As JavaObject = niIterator.RunMethod("nextElement", Null)
        If ni.RunMethod("isLoopback", Null) = False Then
            Dim addresses As List = ni.RunMethod("getInterfaceAddresses", Null)
            For Each ia As JavaObject In addresses
                Dim broadcast As Object = ia.RunMethod("getBroadcast", Null)
                If broadcast <> Null Then
                    Dim b As String = broadcast
                    Return b.SubString(1)
                End If
            Next
        End If
    Loop
    Return ""
End Sub

Sub CheckForServer
    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 = 1000
        tmr.Enabled = True
        SendMYID
    Else
        'Found the server so will TCP connect as a client
        'Log("Connecting to Server")
        tmr.Enabled = False
        tcpClient(0).Initialize("Client")
        tcpClient(0).Connect(sServerIP, TCPPort, 10000)
    End If
  
End Sub

Sub Client_Connected(Successful As Boolean)
  
    Try
        tmr.Enabled = False
      
        If Successful Then
            'Connected as a client, so open a stream
            ToastMessageShow("Connected", False)
            Starter.NetGame.Initialize
            aStream(0).InitializePrefix(tcpClient(0).InputStream, False, tcpClient(0).OutputStream, "AStream")
            Starter.NetGame.SendConnectedMsg(1)
        Else
            'Connection failed
            tcpClient(0).Close
            ToastMessageShow("Connection Failed", False)  
            Starter.iNumPlayers = 1
            Main.bNetGame = False  
            StopService("")
        End If
    Catch
        Log(LastException.Message)
        CallSub(Main, "showNetError")
    End Try
  
End Sub

Sub Server_NewConnection(Successful As Boolean, NewSocket As Socket)
    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 = 0'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.SendConnectedMsg(iClientsConnected)
        If iClientsConnected < Starter.iNumPlayers - 1 Then tcpServer.Listen
    Else
        'Connection to client failed
        tcpServer.Close
        ToastMessageShow("Connection Failed", False)
        Starter.iNumPlayers = 1
        Main.bNetGame = False
        StopService("")
    End If
    If iClientsConnected >= Starter.iNumPlayers - 1 Then
        tmr.Enabled = False
        UDPSocket1.Close
    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")
    Starter.NetGame.ReceiveMsg(msg)
  
End Sub

Note that not all networks support UDP broadcast, so you still need to provide a way for users to manually enter the IP address of the other device(s) they are trying to connect to.

- Colin.
 

Derek Johnson

Active Member
Licensed User
Your best bet is to use UDP broadcast, then when the devices find each other switch to TCP. I've pasted in the code from the NetConnect service in my Five Dice app to help get you started...
That seems ideal. I'll let you know how it works out for me.

Update: I've used this code and the concept of a UDP Broadcast to find the other users of the APP on the same subnet. If anyone else is thinking of using this code as a starting point I would add that it requires another permission to be added to the manifest

B4X:
AddPermission(android.permission.CHANGE_WIFI_MULTICAST_STATE)
Thanks again for then help.

Derek
 
Last edited:
Top