Overview
I am writing a simple test game server in C++. Currently, I have a bound socket listening to anyone, and when a certain packet type arrives, I want to save the sockaddr struct into a object which will be passed to a separate thread. That separate thread will be looping through all sockets and attempting to write data to them.
For testing purposes, I just want to do the following:
- Listen for the packet and determine its type (done)
- Pass the source address information to a separate function (done)
- Create a new socket, and bind it on a new port (done, but maybe an issue here?)
- Send data through that newly bound/made socket using the sockaddr_in that was passed in earlier (done?)
Setup
Receiving
Currently, this is how the global listener is configured:
void lobbyactions_thread() {
struct sockaddr_in any;
memset(&any, 0, sizeof(any));
any.sin_family = AF_INET;
any.sin_port = htons(LOBBYACTIONS_PORT);
any.sin_addr.s_addr = htonl(INADDR_ANY);
unsigned int any_sz = sizeof(any);
packets::LOBBYACTION_PACKET jpacket;
int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bind(socketfd, (struct sockaddr*) &any, sizeof(any))
while (server_manager::STAY_ALIVE) {
struct sockaddr_in* new_con = new struct sockaddr_in;
memset(new_con, 0, sizeof(*new_con));
unsigned int new_con_size = sizeof(*new_con);
recvfrom(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)new_con, &new_con_size);
bool save = false;
if (jpacket.type == JOIN) { save = true; server_manager::join(jpacket.id, new_con); }
// ...
jpacket.okay = true;
if (!save) {
sendto(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)&any, any_sz);
free(any);
}
}
}
Saving and temporarily Sending
When a join is handled, I want to save the recipient into an internal queue of other recipients / users / connections. For testing purposes, I have this join method which will simply create the new socket, bind it, and send some data just as a proof of concept.
struct Slot {
int playerSocket;
int playerAssignedPort;
struct sockaddr_in* sourceAddress;
unsigned int sourceAddressSize;
};
void join(int id, struct sockaddr_in* raddr) {
Slot* ps = new Slot();
ps->playerAssignedPort = 8810 // temporary
ps->playerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(ps->playerAssignedPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
ps->sourceAddressSize = sizeof(addr);
bind(ps->playerSocket, (struct sockaddr*)&addr, ps->sourceAddressSize);
ps->sourceAddress = raddr;
ps->sourceAddress->sin_port = htons(ps->playerAssignedPort);
// ...
sendto(ps->playerSocket, &updatePacket, packets::GAMESTATE_PACKET_SIZE, 0, (sockaddr*)(ps->sourceAddress), ps->sourceAddressSize);
// ...
}
Question
Why is it that I can not bind to this socket? Am I passing the wrong section of any
around? Or is this something that is not really possible? Any help or pointers (terrible pun maybe intended) would be great.
Why is it that when I save the struct sockaddr_in that was populated by recvfrom
I can for some reason no longer send messages, albeit through another port and another socket? Strange enough, I've come to find that when the client and server have to go through a local router, this does not work, but if the client and server do not have to go through a local router, this infact works! Could it be that there is additional packet/header information that I need to gather? Or is the router doing something (or lack there of) that will restrict the messages? Could it be that there is some weird port forwarding issue, and not infact a software issue at play here?
Edit
It may be important to note that the everything works currently in the lobbyactions_thread()
method, i.e. the receive, process, and send. Its the join()
method that is the issue.
Updated code, in part from own discovery and User @selbie
lobbyactions
, and want to create a new socket and send that same address data at a different port, ie I want to send something to 1.2.3.4:6 later on. – dovedevicsockaddr_in
is just a (usually) 16-byte structure - there's no black magic in it. You "send" it somewhere the same way you send any other piece of data - e.g. by passing its address as a parameter to some function. Take the filled-in structure, keep the address part, update the port number, and pass it along tosendto
– Igor Tandetnikstruct sockaddr_in
directly. It makes your code IPv4-only. Instead, use the address family independent functions likegetaddrinfo()
andgetnameinfo()
to convert from hostnames/ports tostruct sockaddr
and back. Thegetaddrinfo()
function can also be used to give you a list ofstruct sockaddr
s that you canbind()
to. – G. Sliepen