Finding server with broadcast

P

pekspro

I need some code that gets the address from a server. I read somewhere
that you could do this by starting some broadcast server using UDP. The
client should send an broadcast message, and when the server answering
the client gets the address. But how do I implement this?

I did this simple quick-hack:

Server:
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
server.Bind( new IPEndPoint( IPAddress.Any, 48000 ) );

while(true)
{
byte[] buffer = new byte[1000];
server.Receive(buffer);

Console.Write("Server got: " + buffer[0]);
//Next line crash cause some permission error.
server.SendTo( new byte[] {2},
new IPEndPoint( IPAddress.Broadcast, 48000 ) );
}

Client:
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
client.Connect(new IPEndPoint( IPAddress.Broadcast, 48000 ));

Console.Write("Client send: 1");
client.Send( new byte[] {1} );

byte[] buffer = new byte[1000];
client.Receive(buffer);
Console.Write("Client got: " + buffer[0]);

client.Close();

To my surprise the code almost worked :). The server receive the
message from the client, but it fails when sending data. And even if it
did it doesn't help the client cause will not know who send the
message.

I'm thankful for any ideas. What I'm looking for is a way to find the
address of a server. It doesn't need to be perfect, it will just give
the users a hint which address to use for the real application.
 
M

Markus Stoeger

I did this simple quick-hack:

see below
Server:
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
server.Bind( new IPEndPoint( IPAddress.Any, 48000 ) );

while(true)
{
byte[] buffer = new byte[1000];
server.Receive(buffer);

use ReceiveFrom here. this will give you an IPEndPoint with the address of
the sending client.
Console.Write("Server got: " + buffer[0]);
//Next line crash cause some permission error.
server.SendTo( new byte[] {2},
new IPEndPoint( IPAddress.Broadcast, 48000 ) );

don't send the response back by broadcast. you can send it directly to the
client using the above IPEndPoint.
}

Client:
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
client.Connect(new IPEndPoint( IPAddress.Broadcast, 48000 ));

not sure if Connect should be used for a connectionless (UDP) socket. I
think it should work, but I always prefer the SendTo and ReceiveFrom
functions for UDP. also you might want to use port 0 for the client. this
way some free source port will be allocated automatically. in your case, if
client and server are running on the same system, they will both access
port 48000, which might fail.
Console.Write("Client send: 1");
client.Send( new byte[] {1} );

byte[] buffer = new byte[1000];
client.Receive(buffer);
Console.Write("Client got: " + buffer[0]);

client.Close();

hth,
Max
 
P

pekspro

Markus said:
not sure if Connect should be used for a connectionless (UDP) socket. I
think it should work, but I always prefer the SendTo and ReceiveFrom
functions for UDP. also you might want to use port 0 for the client. this
way some free source port will be allocated automatically. in your case, if
client and server are running on the same system, they will both access
port 48000, which might fail.

Thank you, I followed your instructions and now the code works. I also
tried to use port 0 on the client, but then the server didn't received
any message (btw, should I use bind on the server?).

This is the code that have so far. Works pretty well:

Server:
//Start server
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
Console.Write("Running server..." + Environment.NewLine);
server.Bind( new IPEndPoint( IPAddress.Any, 48000 ) );

while(true)
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
byte[] buffer = new byte[1000];
//Recive message from anyone.
server.ReceiveFrom(buffer, ref tempRemoteEP);

Console.Write("Server got '" + buffer[0] +
"' from " + tempRemoteEP.ToString() +
Environment.NewLine);

Console.Write("Sending '2' to " + tempRemoteEP.ToString() +
Environment.NewLine);

//Replay to client
server.SendTo( new byte[] {2},
tempRemoteEP );
}

Client:
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);

IPEndPoint AllEndPoint = new IPEndPoint( IPAddress.Broadcast, 48000 );

//Allow sending broadcast messages
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Broadcast, 1);

//Send message to everyone
client.SendTo( new byte[] {1}, AllEndPoint );
Console.Write("Client send '1' to " + AllEndPoint.ToString() +
Environment.NewLine);

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
byte[] buffer = new byte[1000];

string serverIp;

try
{
//Recieve from server. Don't wait more than 3000 milliseconds.
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 3000);
client.ReceiveFrom(buffer, ref tempRemoteEP);
Console.Write("Client got '" + buffer[0] + "' from " +
tempRemoteEP.ToString()+ Environment.NewLine);

//Get server IP (ugly)
serverIp = tempRemoteEP.ToString().Split(":".ToCharArray(), 2)[0];
}
catch
{
//Timout. No server answered.
serverIp = "?";
}

Console.Write("ServerIp: " + serverIp + Environment.NewLine);
 
M

Markus Stoeger

Thank you, I followed your instructions and now the code works. I also
tried to use port 0 on the client, but then the server didn't received
any message

I think you have to bind the port on the client side also. Then it should
work with port 0 (haven't tried it right now though).
(btw, should I use bind on the server?).

Yes you have to, otherwise it doesn't know where to listen for the
broadcast.

Have you tested this code on a system with multiple network interfaces yet?
I did something like that a while ago and as far as I remember such a
broadcast is only sent out on one interface, not on all. You have to lookup
the network interface addresses and send a broadcast on each of them. You
can use the Dns functions to resolve your computers hostname, that should
give you all available local network interface addresses.

Max
 
P

pekspro

Markus said:
I think you have to bind the port on the client side also. Then it should
work with port 0 (haven't tried it right now though).

I added a bind call in the client code. It failed, as expected, cause
two programs where listening on port 48000 at the same time. I then
changed to port 0. No program crashed, but the client didn't find the
server.
Have you tested this code on a system with multiple network interfaces yet?
I did something like that a while ago and as far as I remember such a
broadcast is only sent out on one interface, not on all. You have to lookup
the network interface addresses and send a broadcast on each of them. You
can use the Dns functions to resolve your computers hostname, that should
give you all available local network interface addresses.

Good point. I could get all addresses by calling:
string hostname = System.Net.Dns.GetHostName();
IPHostEntry allLocalNetworkAddresses = Dns.Resolve(hostname);

But how do I use that information? Creating EndPoint like this:

foreach(IPAddress ip in allLocalNetworkAddresses.AddressList)
{
IPEndPoint AllEndPoint = new IPEndPoint( ip, Port );

will only make the client call it self to find the server. In the
original code I do this:

IPEndPoint AllEndPoint = new IPEndPoint( IPAddress.Broadcast, Port );

How do I get the broadcast address for each network?

PEK
 
M

Markus Stoeger

Markus Stoeger wrote:
Good point. I could get all addresses by calling:
string hostname = System.Net.Dns.GetHostName();
IPHostEntry allLocalNetworkAddresses = Dns.Resolve(hostname);

But how do I use that information?

I don't have the source code here right now, but I think you have to create
one socket for each ip address that you get from Dns.Resolve. Then bind the
sockets to these addresses (instead of binding them to IPAddress.Any).
When you call SendTo, use IPAddress.Broadcast as destination.
This way it will be sent out as broadcast on exactly the interface you have
bound the socket to. You'll also have to call ReceiveFrom on each socket.
I added a bind call in the client code. It failed, as expected, cause
two programs where listening on port 48000 at the same time. I then
changed to port 0. No program crashed, but the client didn't find the
server.

It should work.. maybe there is another problem? Take a look at the Bind
description on MSDN: http://tinyurl.com/cqoce
It says "If you do not care which local port is used, you can create an
IPEndPoint using 0 for the port number. In this case, the service provider
will assign an available port number between 1024 and 5000."

hth,
Max
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top