Client - Windows desktop behind NAT, runs a UDP client application.
Server - Ubuntu server with public IP, runs a UDP server application that expects packets on port 6000.
ClientPrivate/Public - private/public IPs of the client
ServerPrivate/Public - private/public IPs of the server
WireShark output on Server:
475 88.xxx.xxx.90 -> 10.0.0.5 UDP 777 → 6000
476 10.0.0.5 -> 88.xxx.xxx.90 UDP 6000 → 777
WireShark output on Client:
152 192.168.2.85 104.40.xxx.250 UDP 777 → 6000
153 104.40.xxx.250 192.168.2.85 UDP 6000 → 777
WireShark output on Server:
475 88.xxx.xxx.90 -> 10.0.0.5 UDP 777 → 6000
476 10.0.0.5 -> 88.xxx.xxx.90 UDP 46666 → 777
WireShark output on Client:
152 192.168.2.85 104.40.xxx.250 UDP 777 → 6000
Why does the Client receive a datagram in Case 1 and does not in Case 2?
I think this is due to the client-side NAT. When your client sends to the server public IP, the NAT router is making an entry in its internal table of connections (something like "192.168.2.85[777] => 104.40.xxx.250[6000]).
So when a response comes back from 104.40.xxx.250[6000] the NAT will find the matching entry and return the response to the client machine. That entry is how it knows which machine in the client network to send the packet to. However, when a response comes back from 104.40.xx.250[46666], that doesn't match any entry in its table so it's dropping that packet.
This is the way NAT works. Consider if you had another machine behind your NAT also talking to the same server, say, "192.168.2.86[777] => 104.40.xxx.250[6000]". I think you would find your NAT router actually changing the source port to something other than 777. Else how can it properly route the response back to the correct machine. In general for NAT, if the incoming packet from the router doesn't match an entry in its table (OR you've configured a port-forwarding rule) the packet will be dropped.
Port-forwarding allows you to open up your internal machine for general access by the outside world. But lacking that, you really don't want any random IP address / port pair to be able to send a datagram to your client machine.