I'm not great at networking concepts, but my basic understanding is that computer networking relies on constructs like IP addresses and port numbers to direct data packets to the correct device and application.
So, if I'm playing an online game like Minecraft or Counter-Strike, I am able to connect to the dedicated game server using the server's IP address (which might be associated with a physical device, or a virtual device like a VM or a OCI container) and port number (which is like a communication "channel", associated with a particular piece of software).
And, as such, you can host multiple game server instances for the same game on different IPs or ports, as long as each server has some way to be individually addressed.
But I start to get a bit confused when I think about the packet's return trip and how the server communicates back to the clients...
In some cases, each client will be connecting from their own network modem, with their own IP address assigned to them dynamically or statically by their ISP. In that case, I would imagine that the server could just keep a list of client connections and route relevant data back to each client.
But in other cases, you might have a multiple clients playing a game behind one modem (like housemates or a LAN party where multiple players join the same remote/internet server), or a newer problem, multiple different networks sharing an IP address due to CG-NAT at the ISP level. In which case, from the perspective of the server, multiple players would be playing from the same IP address and communicating over the same port, right?
So how does the server differentiate between >2 players connecting from the same IP address and communicating over the same port?
Or does it not even try, and instead just broadcast all of the relevant game data to every client?
And if that's the case, how do huge games like battle royales or MMOs handle sending game state to a large number of users?
The other commenters have provided many disparate answers that do reply to parts of your question, but allow me to approach the question holistically and thoroughly.
This is substantially correct. An IP address is a network identity of some "node" machine that participates on the network. A port number is a protocol-specific number for how to interact with a given node. For gaming, we are almost always talking about UDP as the protocol, so the port number will be a UDP port number; the same logic applies for TCP, but I'm going to gloss over that unless you specifically want details about this.
In the case of Legacy IPv4, an IP address is a 32-bit number that is usually presented as four decimal bytes separated by dots (eg 203.0.113.67). Or for modern IPv6, it will be a 128-bit number presented as hexadecimal groups of double bytes that are colon-separated, but where zeros can be abbreviated (eg 2001:db8::67). A TCP port number is any value between 1 and 65535 inclusive; 0 is technically usable but most software will not allow its use.
Correct. Your client aims at the server's IP address, and the UDP port number on that server. And on your end, you will have your own client IP address and client UDP port number. Implicit to the internet, we know that this must be using IP (v4 or v6 does not matter in this scenario) and the client and server only know how to speak UDP. Thus, there are five pieces of information which capture the entire connection: the source IP, source port number, destination IP, destination port number, and the protocol (UDP). In the networking parlance, we call this as the 5-tuple, because it captures the notion of a single conversation between two applications across the network.
Note that I'm specifically using the word "conversation" and not "connection" because the latter has a specific meaning in the business. A connection means that we're holding a resource open -- like a telephone line --for the entire duration that data is being exchanged. But UDP doesn't reserve resources like that, and is more like sending a post card and hoping for a reply.
The 5-tuple concept is important because like an IRL conversation, it's entirely possible to send data in the reverse direction, and while the source IP/port and destination IP/port will be reversed, it's easy to see that this is functionally the same "conversation", just in reverse. The network doesn't really care if the tables have turned: it just passes packets around. So I will simplify and say that if a 5-tuple reverses its source and destination values, then that's functionally no change at all.
I can now answer your question with technical precision: a game server can distinguish multiple game clients by using their unique 5-tuple. The rest of your question is answered by a brief explanation of various workarounds that are needed for the post-1995 Legacy IPv4 world, but which were fixed in the modern IPv6.
This is exactly what existed pre-1995 when the end-to-end principle was alive-and-well on the public Legacy IPv4 Internet. As I mentioned before, an IP address is a network identity, and in the original conception of IP going back to ARPANet, an identity was not meant to be shared amongst multiple machines. Instead, every machine was expected to have its own IP address. However, during the 1995 explosion of dial-up users, network operators could not (or would) not) obtain new tranches of IP addresses to hand out to users, so they began using NAT as a workaround, to reduce their need for public IP addresses. But the keyword was "reduce" not "eliminate", and by 2012, the world had officially run out of available IP addresses to hand out to ISPs.
All of these workarounds (NAT/NPAT, CG-NAT, etc) all work by mutilating the 5-tuple and then unmutilating it for return traffic. When a home router performs NAT, it replaces the client's source IP (eg 192.168.3.42) with the router's (eg 203.0.113.67), and usually also replaces the client's UDP port (eg 12345) with a random one available on the router (eg 45467). The resulting 5-tuple is what the game server will receive. NAT must save the mapping (12345 -> 45467) for future reference.
When the game server wants to reply, it will -- exactly the same as the case with the end-to-end principle -- reverse the source/destination fields in the 5-tuple, and send the packet. This means the destination is now the home router's IP (203.0.113.670 and UDP port (45467). What NAT will now do is to again modify the source IP (to restore the original value of 192.168.3.42) and then use its stored mapping to restore the original UDP port number of 12345.
From the client's perspective, it receives a reversed 5-tuple of the one it sent to earlier. Thus, it's perfectly happy to receive that traffic and nobody is the wiser.
Recall that NAT on the router will: 1) generate a random, new UDP port number, and 2) store the mapping of the originator's port number. So if there are two clients at home playing Minecraft, the router will have generated a different random UDP port number for each, meaning the 5-tuple that arrives to the game server will match 4 out of 5 parts, but not all five. The crucial -- and only -- distinction between these two clients at the same house are that they present a different source UDP port number to the server. And that is also how the game server will treat those two clients separately.
If the home router were to spontaneously reboot -- thus forgetting the NAT mapping table for UDP port numbers -- then both clients cannot recover the conversation at all, even after the home router is back online: the mappings are lost, and nothing can be done but to reconnect to the game server as a new 5-tuple. The end-to-end scenario does not have this problem, and pre-1995, routers did in-fact crash more often than they do now. Genuinely, today's Legacy IPv4 service is poorer than it was in the past, and certainly poorer than what modern IPv6 can deliver.
As long as the home router has available UDP port numbers, NAT can continue to randomly generate a unique UDP port number for each client that is behind the NAT. Since UDP port numbers can be as large at 65535, that's a lot of clients. Though practically, no home router would ever see that many Minecraft clients. Even CG-NAT tends to only support approximately 64-128 clients on a single Legacy IPv4 address.
As for why, all this mutilation of packets takes a little bit longer than just passing the packet through the internet. It is not fun for the ISP to have to build CG-NAT infrastructure. It is not fun to build home router firmware that will get blamed for the user's bandwidth or firewall problems. Also, NAT would require a (relatively large) table in memory store lots of mappings, and so they just don't do that for consumer routers.
In the USA, AT&T's fibre internet modem/router is known to max out after a critical number of UDP conversations or TCP connections, because the NAT feature has run out of memory. This is precisely why some people bypass the modem/router (so they can use their own high-end router) or will use IPv6 for their connection-intensive workloads, like sharing Linux ISOs.
Game servers definitely do not do this, because broadcast is not permitted on the public Internet whatsoever, whether on Legacy IPv4 or modern IPv6. The original conception of the internet did describe "multicast" which can target a group of IPs on a network, but this was never well-implemented for IPv4 and is only implemented on LANs for IPv6. The public internet does not support multicast, for a number of historical and bandwidth/security reasons.
If a game server wanted to send the same data to each client, one after another, it can. But it still must know the 5-tuple that identifies each client. Fortunately, the game server's OS will have taken care to record this info (eg BSD sockets).
The way that MMOs deal with 100k+ clients goes deep into the realm of clustering, load-balancing, and network engineering. The only things that get more complex than that are high-bandwidth applications involving hundreds of thousands of clients (eg Netflix) or are massive hyper-scaler cloud providers (eg Azure, Alibaba).
That said, the fundamentals are still there: clients are identified by their 5-tuple, and all the engineering done to spread that load must still end up producing a reply that has the reversed 5-tuple.
(cont)
In some cases, the game server's IP address is actually anycasted, which is an approach that (very carefully) breaks the notion that a network identity belongs to a single machine. Instead, that one IP address would actually be routed to a nearby machine which is authorized to assume the identity of the game server, and will thus handle the game traffic for that region. So long as all regions handle their traffic identically and the results are consistent as if there were one giant machine that were handling all the traffic, this can work. An example where the seams are visible are how YouTube's view counters will momentarily not match up across all geographies, because the backend servers don't sync up to each other instantly or even quickly; and few people require that precision anyway, so they just don't bother engineering it to do that. When you're providing a global service, that is exactly the sort of engineering tradeoff that must be considered, because even they do not have unlimited money.
In other situations, the game server IP address really is for a single machine, but that machine is a specialized hardware load-balancer that is situated in a cloud provider's network. All that this machine does is to be the frontend for the 5-tuple, and will create a new conversation/connection 5-tuple with a cluster of game servers within the cloud provider's network. There might be some superficial comparisons between a load-balancer and NAT, but the latter works by fraudulence whereas a load-balancer is a subcontractor.