Jump to content
  • Advertisement
Sign in to follow this  
Hernan13

Basic multiplayer with datagram sockets

This topic is 3399 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im working on a fairly simplistic flight simulator, and am currently trying to get basic multiplayer running. My code is pretty much the same as that given in Beej's guide, but at the moment it is only "one way", i.e the host can see the client flying around, but the client cannot see the host. Basically the client sends a packet with the position data at the host (and then waits a little bit for a response), which is decoded and all is well. then the host needs to send it's own data back at the client. How is this normally managed? I have tried to send the response packet down the socket that the host recieved the packet through, but that doesn't seem to work (also, from what i understand of UDP sockets it wouldn't really make sense if it did). I have also attempted making a new socket for the response to be sent through, but i am currently having problems properly assigning the address from recvfrom to the socket. Here is where my latest attempt fails:
if(rv = getnameinfo((struct sockaddr *)&their_addr, addr_len, (PCHAR)ReplyAddr.c_str(), 1024, NULL, 0, 0) != 0)
			{
				printf("getnameinfo error: %s\n", gai_strerror(rv));
			}
			if ((rv = getaddrinfo((PCSTR)ReplyAddr.c_str(), SERVERPORT, &hints, &servinfo)) != 0) 
			{
				printf("getaddrinfo: %i\n", gai_strerror(rv));
			}
getnameinfo returns 1, and for some reason instead of gai_strerror outputting a string for the error, it simply outputs "I" which is no help at all. Is there another way to get the IP from their_addr (the address from recvfrom) without using getnameinfo?

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Hernan13
Basically the client sends a packet with the position data at the host (and then waits a little bit for a response), which is decoded and all is well. then the host needs to send it's own data back at the client. How is this normally managed?


Right now, it sounds as if you are making your game P2P. Are you going to have each player connect to each other or are you just trying to make a server/client modeled game where you will have one server and multiple clients connecting to it?

Normally, most games will use a client/server architecture for security and reliability reasons. Each client would send "commands" to the server, the server updates the game world based on those commands after doing verification and game logic upkeep, and then the server sends data back to the client so it can keep synchronized.

A client should not be sending it's current position to the server as that is easy to exploit and have players "warp" around and possibly crash your server by sending invalid coordinates. Instead, you can send commands that affect the yaw, pitch, and roll of your aircraft and have your client update immediately, but the server has the final say in where you are and will occasionally send you your coordinates and data to prevent your client from getting out of synchronization.

Quote:
I have also attempted making a new socket for the response to be sent through, but i am currently having problems properly assigning the address from recvfrom to the socket.


In general, you only need one socket for UDP on the client and server. More advance users might have reasons to have more, but you will just want to use one for now. Those words are an exaggerated oversimplification of network programming, but it's ok, one UDP socket should work fine for your situation!

Quote:
Is there another way to get the IP from their_addr (the address from recvfrom) without using getnameinfo?


I've never used getnameinfo myself. There are two ways of getting the IP address in an 'easy to read" form from the results of recvfrom.

1. Use inet_ntoa to get an ascii IP addres from the parameter the recvfeom gives you (not sure if this is available on non windows platforms)

2. Access the 'sin_addr.S_un.s_b1' tp s_b4 fields (I'm on Windows, it might be different in *nix). Or, you can use the 'sin_addr.S_un.S_addr' to get the IP as a UINT32. If you get each individual byte, you can build the IP address string yourself using like sprintf(buffer, "%i.%i.%i.%i", all the .sb_x fields here)

In addition, if you are using UDP, you should understand how the protocol is unreliable, so I would suggest you look into using a networking library such as enet or RakNet for your networking logic in the game rather than trying to roll your own. If you have no need for reliable data in your game, then you can get by doing it yourself, but overall, you will be able to focus on your game mechanics if you used an existing networking library.

I know a lot of programmers like to 'know how it works' before using it, but networking programming is one of those things where it's a project on it's own! I've been working on my own high performance UDP code and still used enet as one higher level layer than what I have for my base and it's been tough!

Good luck with your game!

Share this post


Link to post
Share on other sites
I still can't get the address from their_addr (inet_ntoa takes an in_addr, while recvfrom gives a sockaddr, and i can't seem to convert it)
also, as you said you only need one socket, im back trying to shove the reply back into the socket it recieved the packet from, and i'm getting nothing (could it have something to do with routers?)

if ((numbytes = sendto(sockfd, Packet.c_str(), Packet.length(), 0, (const sockaddr*)&their_addr, addr_len)) == -1)
{
printf("reply: sendto\n");
}

this is done straight after the host recieves the packet from the client, and im not sure where the problem lies. The host doesn't see a problem with it, and happily shoots the reply out, but it doesn't get to the client.

As you can see i am of the ilk who like to know how things work before using a library to do it all for me. Also, this is a pretty simple game and will mostly be played just between groups of friends, so security isn't really an issue. It won't end up being p2p when theres more than 2 players, as there will be a distinct host to which all the clients send their data, and the host will send all of the data to all of the clients.

Share this post


Link to post
Share on other sites
A great tool for debugging network activity is Wireshark. If you run that on the receiving machine and filter it to just UDP traffic on that port you'll see whether it's actually arriving or not. It's no substitute for proper code but it does help when you need sanity-check the networking.

Where are the 2 PCs located? If they're on the same LAN then the shared router shouldn't be an issue. If they're on different networks then there might be address translation or firewalls to contend with. (In fact, firewalls can be an issue anyway.)

Share this post


Link to post
Share on other sites
They are on the same LAN, but eventually i want to get this going over the net.
Thanks to wireshark i've found that the client pc is recieving the packets, but they appear to be on the wrong port. I thought i had all ports set to 1916 (its a ww1 flight sim) but heres the ports from wireshark

outbound packets (client - host):
source port: 63739 (63739)
destination port: persona (1916)

inbound packets (host - client):
source port: persona (1916)
destination port: 63739 (63739)

I have nowhere defined a port as anything but 1916, so im not sure where 63739 is coming from, and i'm not sure where to change it.

edit: had a slightly closer look, that destination port of the inbound packets is not constant, its different for every packet.

Share this post


Link to post
Share on other sites
So when you send a packet to a target, you connect to a fixed port on the target.

Your 'source' port is set to an arbitrary, high-value number.

When you respond to a packet, you reverse the two numbers, and send off the result.

Under TCP/IP, this allows multiple communication streams between the two nodes, even if you are talking to the same well known port, indexed by the random source port of whomever initiates the conversation.

This is also how you can get multiple clients behind a NAT to be distinguished 'on the internet' -- the source port of the communication will differ based on which client is speaking, and you can then respond to a given client using a different source port (and NAT automagically knows that a given source port corresponds to a given internal IP address and source port, and remaps the packet).

UDP isn't something I'm familiar with. So I don't know the conventions for UDP communication and how source ports should be used and/or set.

Share this post


Link to post
Share on other sites
After fiddling around with it for a while longer, i'm still unable to get it going to the right port. (i attempted using sockaddr_in instead of sockaddr_storage for their_addr, and manually changing the port to 1916, but that still didnt work).
Im thinking the best way will be to extract a string with the ip address, and use that to send out the packet. I have attempted this (again) with getnameinfo(), however, i had to modify my code to accept a string as input for setting the target ip, which shouldn't be a problem, but when i use:
getaddrinfo(IP.c_str(), SERVERPORT, &hints, &servinfo)) != 0)

(with IP as a paramater of the function call to send packets) instead of:
getaddrinfo("192.168.0.196", SERVERPORT, &hints, &servinfo)) != 0)

it crashes in release builds, and in debug builds it outputs (null) for the IP, and a bunch of strange characters for the SERVERPORT.
IP is definately set, and SERVERPORT is most definately set, as it is defined right at the start of the program.

Share this post


Link to post
Share on other sites
If the client sends the first message, you use recvfrom on the server to get the source into a struct sockaddr, more specifically by passing a pointer to a sockaddr which is actually to a sockaddr_in. That will now store the IP address and port of the sender, which you can use for a reply.

If you're doing this and the data seems wrong, it may be because you're not converting the port from host to network byte order and back again. Look for examples of htons and ntohs to see this in practice.

If the port number you're sending to (or indeed from) changes each time, that implies you're creating a new socket each time and it picks a different 'random' one on each occasion. To know this for sure, we'd have to see the socket initialisation code.

As for your most recent post, I expect your declaration of servinfo or hints is wrong, but you'd have to show the code.

Share this post


Link to post
Share on other sites
Yeah, i am opening a new socket for each packet, and closing it again after (i know this is bad practice, but i figured i would get it working, and then optimise it).
I converted the port to network order, and now the replies are being sent to the right place, however, the client is for some reason not receiving them.
Heres an example client-host packet:
ip
source: 192.168.0.185
destination: 192.168.0.196
port
source: 60003
destination: 1916

and host-client reply:
ip
source: 192.168.0.196
destination: 192.168.0.185
port:
source: 1916
destination: 1916

the only thing i can think of is a firewall blocking it, but it shouldn't be, as i've given it permission etc, and even made sure by opening the port and checking router settings. Also, is it possibly a problem that the source and destination have the same port. (although the packets are definately reaching the client, as that is where wireshark is running.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!