UDP port change after while

I used UDP for send and receive between 8 devices and port, some of them changed and couldn’t receive and send messages between other devices

what can I do to manage ports ?how can I know which port changed?why did this happen?

What kind of devices ?

Android and PC. in both of them I tested.in localhost I don’t have a problem, it’s happened when I use ubuntu server

Show some code? The local port of an open socket doesn’t change.

First, i should say I’m new in Go :slight_smile:
I create room like this

type Room map[string]*RoomStructure

type RoomStructure struct {
	UserAddr1 *net.UDPAddr
	UserAddr2 *net.UDPAddr
	UserAddr3 *net.UDPAddr
	UserAddr4 *net.UDPAddr
	UserAddr5 *net.UDPAddr
	UserAddr6 *net.UDPAddr
	UserAddr7 *net.UDPAddr
	UserAddr8 *net.UDPAddr
}
func Create(r Room, roomname string) {

	r[roomname] = &RoomStructure{}

}

so the first user create room and join to that room like this

func Join(r Room, userroom string, addr *net.UDPAddr) {

	if r[userroom].UserAddr1 == nil {
		r[userroom].UserAddr1 = addr
	} else if r[userroom].UserAddr2 == nil {
		r[userroom].UserAddr2 = addr
	} else if r[userroom].UserAddr3 == nil {
		r[userroom].UserAddr3 = addr
	} else if r[userroom].UserAddr4 == nil {
		r[userroom].UserAddr4 = addr
	} else if r[userroom].UserAddr5 == nil {
		r[userroom].UserAddr5 = addr
	} else if r[userroom].UserAddr6 == nil {
		r[userroom].UserAddr6 = addr
	} else if r[userroom].UserAddr7 == nil {
		r[userroom].UserAddr7 = addr
	} else if r[userroom].UserAddr8 == nil {
		r[userroom].UserAddr8 = addr
	}

}

and broadcast like this

func BroadCast(m string, ur string, conn *net.UDPConn, addr *RoomStructure) {
// m = message
// ur = user room
// 
	if r[ur].UserAddr1 != nil {
		sendResponse(m, conn, r[ur].UserAddr1)
	}
	if r[ur].UserAddr2 != nil {
		sendResponse(m, conn, r[ur].UserAddr2)
	}
	if r[ur].UserAddr3 != nil {
		sendResponse(m, conn, r[ur].UserAddr3)
	}
	if r[ur].UserAddr4 != nil {
		sendResponse(m, conn, r[ur].UserAddr4)
	}
	if r[ur].UserAddr5 != nil {
		sendResponse(m, conn, r[ur].UserAddr5)
	}
	if r[ur].UserAddr6 != nil {
		sendResponse(m, conn, r[ur].UserAddr6)
	}
	if r[ur].UserAddr7 != nil {
		sendResponse(m, conn, r[ur].UserAddr7)
	}
	if r[ur].UserAddr8 != nil {
		sendResponse(m, conn, r[ur].UserAddr8)
	}


}

func sendResponse(m string, conn *net.UDPConn, addr *net.UDPAddr) {

	_, err := conn.WriteToUDP([]byte(m), addr)
	if err != nil {
		fmt.Printf("Couldn't send response %v", err)
	}

}

If I understand well, you don’t have any problem when your in localhost but there are issues when your server is located outside of localhost ? Is this server outside of your LAN ? Somewhere on the internet ?

The problem you most likely have is that UDP is a connectionless protocol.
On your LAN, you won’t have any problem as your devices have fixed IP adresses and keep listening on the same port number. Depending on your client implementation, this might be a fixed port (defined by you) or an ephemeral port
Your client, once “connected” to the UDP server should (if it is the expected behaviour) listen on the same fixed or ephemeral port for packets coming from your server. If it is not the case, then there is an issue with the client implementation.

Now, if your server is outside your LAN, then other mechanisms come into play.
Between your android client and your server, there will be a lot of other routers and firewalls.
When you send a UDP packet from the client, most firewall will set up a temporary NAT rule so that the server could potentially respond to the client.
In other word, on the server side, the IP:Port you see is not the real IP:Port of your android devices (except if your android client have a public IP adress, which is unlikely for IPV4), what you see is the IP:Port of the NAT translation done by the last router before your server.

The thing is, those NAT translations are not permanent (to avoid socket to stay open forever), especially if this is a UDP connection and after a certain amount of time, they become invalid, or rather, not translating to your client anymore.
What that means is that the server can’t communicate with the stored IP:Port anymore because it won’t translate to your Android client.

To solve your problem, the easiest way would be to use TCP, as it is a connected protocol. Note however than even with TCP you could have the same kind of issue, except it won’t happens as fast…

If you want to keep using UDP, the only way for the server to communicate “reliably”, is to communicate immediately after the client sent a package. You could implement some kind of heartbeat mechanism for example.

2 Likes

Thank you for your reply.
Yes, my server is outside of my LAN .as you sed I think is better I update users IP: PORT frequently. but I don’t know how often is good because I don’t want to use too many resources for that.and one more question, is my code optimal?
Thanks.

Well, I don’t really know either. There is no rule on how long the NAT will survive between a client and a server. I would typically do a trial and error approach here.
I’d say that a 2 minutes interval should be a good place to start, but this is just a guess.
For the resource usage, remember that the smallest UDP package is 28 bytes.
So if you ping the server every 2 minutes for 24 hour, you’ll have a rough total of 20 KB of data for one client. After one month, for your 8 client, you’d have a 4.8 MB consumed.

Now the code. Note that I am also new at go but here are my comments :

type RoomStructure struct {
	UserAddr1 *net.UDPAddr
	UserAddr2 *net.UDPAddr
	UserAddr3 *net.UDPAddr
	UserAddr4 *net.UDPAddr
	UserAddr5 *net.UDPAddr
	UserAddr6 *net.UDPAddr
	UserAddr7 *net.UDPAddr
	UserAddr8 *net.UDPAddr
}

Could be replaced by this :

type RoomStructure struct {
    UserAddr [8]*net.UDPAddr
}

Then in your following func you could replace your ifs block in Join :

func Join(r Room, userroom string, addr *net.UDPAddr) {
    r[userroom].UserAddr[len(r[userroom].UserAddr)] = addr
}

And in BroadCast :

func Broadcast(m, ur string, conn *net.UDPConn, addr *RoomStructure) {
    for (i := 0; i < len(r.UserAddr); i++ {
         sendResponse(m, conn, r[ur].UserAddr[i])
    }
}

Might be some typos here or there as I did not test this…

1 Like

Thank you for your information :blush:.Actually, I developing game server and I should consider about resource usage (CPU, RAM)

Well, don’t worry about CPU,RAM usage, it will be negligeable for only 8 clients, even if you’d ping the server every seconds…

sorry I asking too much.
what about 100 rooms which include 8 users in them?each user wants to send 30 packets in a second(30 fps). I know it’s dependent on CPU power but let’s consider our CPU is Intel® Xeon® CPU @ 2.60GHz. what about that?I know golang is a powerful language but I don’t know, my code can handle that process or not.
Thanks

Well.

This is not a question I can answer easily for you as I lack information.
It really depends what you do with each packets you receive. If you just write the content of your packets in a log file, it should be fine, but if you need to do more complex actions (fetch database, sync with other users, do some math, etc.) I can’t tell if it could work. Actually, even if I had total knowledge of your application, I don’t know any way to theorize if it would work or not against your hardware spec.

That’s when you need load testing. Here is a simple example that shows how to profile and load test a simple http server (you should watch the whole video, btw, it’s very instructive)

Now, your worst case scenario has an output of 100 * 8 * 30 packets per seconds. It’s not negligeable. Say you have a typical packet size of 50 bytes, then, your bandwidth need to be at least 1.2 MB/s or 9.6 Mbps download. And we are not counting the upload bandwidth needed for the server responses. You need to check that your host provider gives you enough bandwidth for that.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.