I'm currently developing a server and app that needs to communicate using UDP. My server uses Golang, it's related code looks like this:
import (
"flag"
"fmt"
"net"
"os"
)
var (
UDP_SOCKET_PORT = flag.Int("udp_socket_server_port", 10001, "Socket sckServer port")
udpSocketAddrArr = []net.Addr{}
_udpConn net.PacketConn
_broadcastAddr *net.UDPAddr
)
func StartUDPSocket() {
_udpConn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", *UDP_SOCKET_PORT))
if err != nil {
panic(err)
}
defer _udpConn.Close()
fmt.Printf("conn: %s\n", _udpConn.LocalAddr())
_broadcastAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", getBroadcastIP(), *UDP_SOCKET_PORT))
if err != nil {
panic(err)
}
fmt.Printf("UDP serving at %s:%d...\n", _broadcastAddr.IP, _broadcastAddr.Port)
for {
buf := make([]byte, 2048)
_, addr, err := _udpConn.ReadFrom(buf)
if err != nil {
panic(err)
}
udpSocketAddrArr = append(udpSocketAddrArr, addr)
messageTxt := string(buf)
checkUDPPayload(messageTxt, _udpConn, _broadcastAddr)
}
}
func UDPSend(payload string) {
for _, el := range udpSocketAddrArr {
udpConnWrite(payload, GetUDPConn(), el)
}
}
func UDPBroadcast(payload string) {
udpConnWrite(payload, _udpConn, _broadcastAddr)
}
func udpConnWrite(payload string, conn net.PacketConn, addr net.Addr) {
bytesWritten, err := conn.WriteTo([]byte(payload), addr)
if err != nil {
panic(err)
}
fmt.Printf("UDP Wrote %d bytes to %s\n", bytesWritten, addr.String())
}
func getBroadcastIP() string {
return "255.255.255.255"
}
func GetCurrentIP() string {
//find current ip
mIpV4 := ""
host, _ := os.Hostname()
addrs, _ := net.LookupIP(host)
for _, addr := range addrs {
if ipv4 := addr.To4(); ipv4 != nil {
mIpV4 = ipv4.String()
}
}
return mIpV4
}
func GetUDPConn() net.PacketConn {
return _udpConn
}
func GetUDPBroadcastConn() *net.UDPAddr {
return _broadcastAddr
}
Then inside checkUDPPayload()
, I have a code that looks like this:
func checkUDPPayload(payload string, conn net.PacketConn, addr net.Addr) {
...
udpConnWrite(
"some string",
conn,
addr,
)
}
It works, it receives the payload from my android app (which was a pain to make it work on my home network for some reason, and works fairly easily at work). But my app can't receive the packet server sent, and my app can receive it's own packet (which makes sense, I guess). My android code looks like this:
private val broadcastAddr: InetAddress = InetAddress.getByName("255.255.255.255")
private lateinit var localBroadcastAddr: InetAddress
private lateinit var connectedToServerSocket: DatagramSocket
private lateinit var appSocket: DatagramSocket
val socketRespObservers = mutableListOf<(String) -> Unit>()
private var socketResp: String by Delegates.observable("") { _, _, newVal ->
socketRespObservers.forEach { it(newVal) }
}
private lateinit var ctx: FragmentActivity
fun start(ctx: FragmentActivity) {
this@SocketHelper.ctx = ctx
//socket communication
ctx.lifecycleScope.launch(Dispatchers.IO) {
try {
connectedToServerSocket = DatagramSocket()
appSocket = DatagramSocket(UDP_SERVER_PORT)
connectedToServerSocket.connect(getBroadcastAddr(), UDP_SERVER_PORT)
connectedToServerSocket.broadcast = true
Log.i(MainActivity.TAG, "connectedToServerSocket connected: ${connectedToServerSocket.isConnected}")
Log.i(MainActivity.TAG, "appSocket connected: ${appSocket.isConnected}")
sendToUDP("some string", getLocalBroadcastAddr())
while (true) {
receiveFromUDP()
receiveFromServerUDP()
}
} catch (e: SocketException) {
Log.e(TAG, "Socket Error:", e)
close()
}catch (e: SecurityException) {
Log.e(TAG, "Sec Error:", e)
close()
} catch (e: IOException) {
Log.e(TAG, "IO Error:", e)
close()
} catch (e: Exception) {
Log.e(TAG, "Error:", e)
close()
}
}
}
fun sendToUDP(payload: String, srvAddr: InetAddress) {
try {
val sendPacket =
DatagramPacket(
payload.toByteArray(),
payload.length,
srvAddr,
UDP_SERVER_PORT
)
appSocket.send(sendPacket)
}catch (e : java.lang.Exception){
Log.e(TAG, "err sending udp: ${e.localizedMessage}")
}
}
private fun receiveFromUDP() {
val receiveData = ByteArray(1024)
val receivePacket = DatagramPacket(receiveData, receiveData.size)
appSocket.receive(receivePacket)
socketResp = String(receivePacket.data, 0, receivePacket.length)
}
private fun receiveFromServerUDP() {
val receiveData = ByteArray(1024)
val receivePacket = DatagramPacket(receiveData, receiveData.size)
connectedToServerSocket.receive(receivePacket)
socketResp = String(receivePacket.data, 0, receivePacket.length)
}
fun getBroadcastAddr() : InetAddress {
return broadcastAddr
}
fun getLocalBroadcastAddr() : InetAddress {
if(!::localBroadcastAddr.isInitialized){
val currentIp = Utils.instance.getIpv4HostAddress()
Log.i(TAG, "currentIp: $currentIp")
val currentIpArr = currentIp.split(".")
localBroadcastAddr = InetAddress.getByName("${currentIpArr[0]}.${currentIpArr[1]}.${currentIpArr[2]}.255")
}
return localBroadcastAddr
}
fun close() {
appSocket.close()
}
companion object {
const val TAG = "SocketHelper"
val instance: SocketHelper by lazy {
SocketHelper()
}
const val UDP_SERVER_PORT = 10001
}
The code that works at work uses 255.255.255.255
for sending and a DatagramSocket
doesn't need a port. And I use that DatagramSocket
for both sending and receiving. As you can see, the code is more uglier as I tried to have 2 DatagramSocket
to see if having different configured DatagramSocket
, I might receive the server's response to my "message" as right now, at my home network, the app can't receive the server's packet only the one sent my the app itself. connectedToServerSocket.isConnected
return true, but I seen an answer that it was meant for TCP
, and even if it says it's connected, it doesn't seem do anything useful for me.
UPDATE:
I did some testing today and continue to do some research and I still can't make this work. Hopefully, when I get back to work, nothing breaks. I did some small changes to both sides. Now I'm thinking, maybe the issue is caused by my local network at home. My ISP is kinda lame, like currently my NAT is set to strict which I think is caused by me not having a fixed IP (because it cost almost as much as my current prepaid internet plan), so I have some suspicions that my modem's configuration might have an effect on the UDP communication on my local network. So I bought a router thinking that if I connect my PC and phone to it's network, I would at least have more control to at least allowing UDP ports so my system would work, but it doesn't seem to be that easy. I already activated UPnP, but my golang server doesn't seem to be included in the apps that gets allowed or can use UPnP.
UPDATE:
So just got back home from work, and my changes did break the system from working on my work's network. Then I checked my previous working code, and at least on the android side, it's the simplest it could be. Here's what it looks like:
private lateinit var basicSocket: DatagramSocket
private val broadcastAddr: InetAddress = InetAddress.getByName("255.255.255.255")
basicSocket = DatagramSocket()
basicSendToUDP("some string", getBroadcastAddr())
while (true) {
basicReceiveFromUDP()
}
fun basicSendToUDP(payload: String, iAddr: InetAddress) {
Thread {
val sendPacket =
DatagramPacket(
payload.toByteArray(),
payload.length,
iAddr,
UDP_SERVER_PORT
)
basicSocket.send(sendPacket)
}.start()
}
private fun basicReceiveFromUDP() {
val receiveData = ByteArray(1024)
val receivePacket = DatagramPacket(receiveData, receiveData.size)
basicSocket.receive(receivePacket)
socketResp = String(receivePacket.data, 0, receivePacket.length)
}
fun getBroadcastAddr(): InetAddress {
return broadcastAddr
}
Then here's what the golang side looks like, it's isn't that different from what I posted earlier:
import (
"flag"
"fmt"
"net"
"os"
)
var (
UDP_SOCKET_PORT = flag.Int("udp_socket_server_port", 10001, "Socket sckServer port")
udpSocketAddrArr = []net.Addr{}
_udpServer net.PacketConn
_broadcastAddr *net.UDPAddr
)
func StartUDPSocket() {
conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", *UDP_SOCKET_PORT))
if err != nil {
panic(err)
}
_udpServer = conn//don't remove this; this seems to be quite important
defer _udpServer.Close()
fmt.Printf("UDP serving at %v...\n", _udpServer.LocalAddr().String())
_broadcastAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", getBroadcastIP(), *UDP_SOCKET_PORT))
if err != nil {
panic(err)
}
for {
buf := make([]byte, 2048)
_, addr, err := _udpServer.ReadFrom(buf)
if err != nil {
panic(err)
}
udpSocketAddrArr = append(udpSocketAddrArr, addr)
messageTxt := string(buf)
checkUDPPayload(messageTxt, _udpServer, addr)
}
}
func UDPSend(payload string) {
for _, el := range udpSocketAddrArr {
udpConnWrite(payload, GetUDPConn(), el)
}
}
func UDPBroadcast(payload string) {
udpConnWrite(payload, GetUDPConn(), GetUDPBroadcastConn())
}
func udpConnWrite(payload string, conn net.PacketConn, addr net.Addr) {
if conn == nil {
fmt.Println("udpConnWrite conn nil")
}
if addr == nil {
fmt.Println("udpConnWrite addr nil")
}
bytesWritten, err := conn.WriteTo([]byte(payload), addr)
if err != nil {
panic(err)
}
fmt.Printf("UDP Wrote %d bytes to %s\n", bytesWritten, addr.String())
}
func getBroadcastIP() string {
return "255.255.255.255"
}
func GetCurrentIP() string {
//find current ip
mIpV4 := ""
host, _ := os.Hostname()
addrs, _ := net.LookupIP(host)
for _, addr := range addrs {
if ipv4 := addr.To4(); ipv4 != nil {
mIpV4 = ipv4.String()
}
}
return mIpV4
}
func GetUDPConn() net.PacketConn {
return _udpServer
}
func GetUDPBroadcastConn() *net.UDPAddr {
return _broadcastAddr
}
func GetUDPConnLst() []net.Addr {
return udpSocketAddrArr
}
func ClearUDPConnLst() {
udpSocketAddrArr = nil
}
So what could be wrong on my home network when the simplest code works flawlessly on my work network?