Sign in to follow this  

Winsock and UDP (client/server app)

This topic is 2633 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

Hello!

I'm currently working on an airhockey game, where you can host/connect. I'm using TCP for connecting to the host, and I'm going to use UDP for sending game data packets. Everything has gone great so far, but now I'm stuck in the UDP part. Currently the server is creating a UDP socket and binding it to a specific port. The client creates a udp socket aswell. The problem here is, how is the client going to recieve packets from the server? the recvfrom(...) takes a bound socket as first argument. The only bound udp socket that is created, is by the server. Should I send the server udp socket descriptor to the client using tcp? That is the only thing I can think of, I've searched everywhere, looked for tutorials, but nothing has worked... Any kind of help is appreciated.

Mufflot

Share this post


Link to post
Share on other sites
The socket doesn't need to be bound, you can use a socket freshly created from socket().
You can't receive UDP data on a TCP socket, or vice-versa.

However, UDP and TCP are completely separate, you'll probably run into firewall issues if the clients try to recvfrom() the server without that port open.
What would be better would be to have the clients also open a UDP connection to the server when the TCP connection is established. Then you can use the UDP socket to sendto() / recvfrom() the server

Share this post


Link to post
Share on other sites
Thanks for the quick answer.

Quote:
Original post by Evil Steve
The socket doesn't need to be bound, you can use a socket freshly created from socket().


Ah great.

Quote:
Original post by Evil Steve
What would be better would be to have the clients also open a UDP connection to the server when the TCP connection is established. Then you can use the UDP socket to sendto() / recvfrom() the server


Ok, I'm not sure I understand completely. Do you mean that when the the TCP connection is established. The client should use connect(...), using the UDP socket descriptor as argument, to establish a UDP connection to the server?

Mufflot

Share this post


Link to post
Share on other sites
Quote:
Original post by Mufflot
Ok, I'm not sure I understand completely. Do you mean that when the the TCP connection is established. The client should use connect(...), using the UDP socket descriptor as argument, to establish a UDP connection to the server?
Yep - the client needs to make an outgoing connection for the firewall (router, etc) to allow traffic from the server to reach the client. That means making a connect() call to connect to the server and bind the socket, or to call sendto() with the server's address in the sockaddr, which will also bind the socket.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Yep - the client needs to make an outgoing connection for the firewall (router, etc) to allow traffic from the server to reach the client. That means making a connect() call to connect to the server and bind the socket, or to call sendto() with the server's address in the sockaddr, which will also bind the socket.


I see, I'm gonna try that out. Very much thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Yep - the client needs to make an outgoing connection for the firewall (router, etc) to allow traffic from the server to reach the client. That means making a connect() call to connect to the server and bind the socket, or to call sendto() with the server's address in the sockaddr, which will also bind the socket.


I think the general term for that is "NAT Punchthrough" You might find some information about that on google. There is also the possibility that both server and client are behind a Router in which case you'd have to introduce a third party to make connection possible.

Share this post


Link to post
Share on other sites
Quote:
Original post by Madhed
I think the general term for that is "NAT Punchthrough" You might find some information about that on google. There is also the possibility that both server and client are behind a Router in which case you'd have to introduce a third party to make connection possible.


Okay. So using 'sendto' might be better then perhaps?

I got my socket waiting in the 'recvfrom' call atleast, which is progress compared to the last time I tried when I got an 'invalid socket' error.
I've got an other issue now. I want the server to send packets to the client, the packets doesn't seem to arrive to the client. Here is my current implementation. I really can't see why the packets doesn't arrive.

Server

udpSocket.SendTo(
udpSocket.GetDescriptor(),
PortUDP, // port the socket is bound to
clientIpAddress.c_str(),
"heja",
4);

void
Socket::SendTo(
int socketDescriptor,
int port,
const char* ip,
const char* data,
unsigned int size)
{
ULONG internetAddress = inet_addr(ip);

sockaddr_in socketAddress;
GetSocketAddress(socketAddress, internetAddress, port);

sendto(
socketDescriptor,
data,
size,
0,
(SOCKADDR*)&socketAddress,
sizeof(socketAddress));
}

void
Socket::GetSocketAddress(sockaddr_in& out, ULONG internetAddress, int port) const
{
out.sin_family = AF_INET;
out.sin_addr.S_un.S_addr = internetAddress;
out.sin_port = htons((u_short)port);
}



Client

if (udpSocket.ReceiveFrom(
udpSocket.GetDescriptor(),
PortUDP,
serverIpAddress.c_str(),
buffer))
{
// packet recieved, get data from it, using GamePacket
int breakHere = 0;breakHere;
}

bool
Socket::ReceiveFrom(int socketDescriptor, int /*port*/, const char* /*ip*/, std::string& out)
{
SOCKADDR from;
char inbuffer[256];
int len = sizeof(SOCKADDR);
memset(inbuffer, '\0', 256);

int numberOfBytes = recvfrom(
socketDescriptor,
inbuffer,
sizeof(inbuffer)-1,
0,
&from,
&len);

if (numberOfBytes <= 0)
{
return false;
}

out = inbuffer;
return true;
}




Share this post


Link to post
Share on other sites
Here is some more:


enum
{
PortTCP = 2048,
PortUDP = 2050,
};

bool
Server::Initialize()
{
#ifdef _DEBUG
Debug* debug = (Debug*)Engine::GetModule(ModuleDebug);
debug->AddFadingText("initializing server");
#endif

time = (Time*)Engine::GetModule(ModuleTime);

if (! InitializeTCPSocket())
{
return false;
}

if (! InitializeUDPSocket())
{
return false;
}

InitializeThreads();

#ifdef _DEBUG
Debug* debug2 = (Debug*)Engine::GetModule(ModuleDebug);
debug2->AddFadingText("success");
#endif

return true;
}

bool
Server::InitializeTCPSocket()
{
if (! tcpSocket.Initialize(IPPROTO_TCP))
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

if (! tcpSocket.Bind(PortTCP))
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

if (! tcpSocket.Listen())
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

return true;
}

bool
Server::InitializeUDPSocket()
{
if (! udpSocket.Initialize(IPPROTO_UDP))
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

if (! udpSocket.Bind(PortUDP))
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

return true;
}

bool
Socket::Bind(int port) const
{
sockaddr_in socketAddress;
GetSocketAddress(socketAddress, INADDR_ANY, port);

int result = bind(
descriptor,
(SOCKADDR*)&socketAddress,
sizeof(socketAddress));

if (result != 0)
{
LOGERROR(Network::GetLastError().c_str());
return false;
}

return true;
}


Share this post


Link to post
Share on other sites
Ok so you should perform steps in that order:

1. Create server socket
2. Bind server socket to known port
3. Create client socket
4. Send data to server ip/port
5. Wait for data on server using recvfrom (which gives you client's ip/port)
6. Send data to client descriptor (5)
7. Receive data from server through client socket

That should work if your firewall isn't interfering.

Share this post


Link to post
Share on other sites
Quote:
Original post by Madhed
Ok so you should perform steps in that order:

1. Create server socket
2. Bind server socket to known port
3. Create client socket
4. Send data to server ip/port
5. Wait for data on server using recvfrom (which gives you client's ip/port)
6. Send data to client descriptor (5)
7. Receive data from server through client socket

That should work if your firewall isn't interfering.


Ok great! thanks for your help, I'm gonna try this tomorrow, it should really work. I Really need to sleep now, cheers! (=. Will make a post tomorrow how it worked.

Share this post


Link to post
Share on other sites
Alright,
here is some code from me which, I just checked, works.


//bind server to port 12345
Socket server(12345);
//create unbound client socket
Socket client;
//send data to server
client.send(0x7f000001/*127.0.0.1*/, 12345, "Hello", 5);

//Wait some time
Sleep(100);

//prepare buffer to receive data
char buffer[6] = {0};
unsigned long clientIp = 0;
unsigned short clientPort = 0;
//receive message and store client ip/port
if (server.receive(buffer, 6, &clientIp, &clientPort)) {
std::wstringstream str;
str << buffer;
MessageBox(0, str.str().c_str(), L"Message Arrived", MB_OK);

//send message back
server.send(clientIp, clientPort, "AHOI", 4);

//Wait
Sleep(100);
char clientBuffer[5] = {0};
if (client.receive(clientBuffer, 5)) {
std::wstringstream str;
str << clientBuffer;
MessageBox(0, str.str().c_str(), L"Message Arrived", MB_OK);
}
}



:)

[Edited by - Madhed on September 27, 2010 5:50:35 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Mufflot
Hell yeah! I'm sending udp packets between client/server now. Finally I can continue with some gameplay code... Thanks all for help!


Great. Now you can start doing the nasty stuff ;)

Create an (optional) reliable layer above pure UDP that resends packets if they get dropped, returns packets in correct order, optimize packet size, etc.

Quote:
Original post by Mufflot
Ah, well that looks great. Though I'm not a fan of initalizing stuff in the ctor.


RAII my friend. Google it up! Though, in the case of my Socket class, I treat unbound sockets to be completely initialized. The binding ctor is just syntactic sugar.

Share this post


Link to post
Share on other sites
The main problem with RAII is the fact that the destructor isn't called if the constructor throws. So each sub-resource you need also needs to be RAII (because destructors for contained and base objects are called). It's kind-of like const-correctness; you have to push it all the way out to the leaves of your code.

Regarding a higher-level "reliable" packet protocol, you really need to separate "messages" from "packets." "Messages" delivery types typically include reliable-in-order, never-reordered-but-possibly-dropped and never-dropped-but-possibly-reordered. For messages, "possibly duplicated or reordered" doesn't make much sense, either from implementation nor user point of views.

Many messages go into each single packet. The packet has sequence numbers and acknowledgement to support the different reliability modes. It probably also has timing information to support clock sync and latency calculation. The messages themselves don't need sequence numbers; you can build all that on top of the data from the packets. You may optionally want to support per-message addressing ("send this to object 3"), and/or multiple channels where the reliability guarantees (in-order, etc) are enforced per channel rather than globally across the connection.

This ends up being a much more efficient structure to use than a naive "message == packet" system.

Share this post


Link to post
Share on other sites

This topic is 2633 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this