Socket programming source, help please.

P

Poster Matt

Hi,

I need some socket programming source code explained please. Here's the
source that I'm currently using in a class that I got from the web - my
question is below the source.


IPHostEntry hostadd = Dns.GetHostEntry(server);
IPEndPoint EPhost = new IPEndPoint(hostadd.AddressList[0], port);

Socket socket = new Socket(EPhost.Address.AddressFamily, SocketType.Dgram,
ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, timeoutSendingToServer);
socket.SendTo(messageSend, EPhost);

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;

socket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, timeoutReceivingFromServer);

int numBytesReceived = socket.ReceiveFrom(messageReceive, ref tempRemoteEP);


The code works perfectly there's just one element I don't understand.
Firstly I know Socket should be in a 'using' clause, I've removed that and
error checking so that the code I've posted is just the barebones.

So first of all the server's IP address and port are combined into an
IPEndPoint - no problem.

Then the Socket is created using that IPEndPoint, a timeout added and the
data is sent using SendTo - no problem.

Now comes the code that I don't understand:

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;

A new IPEndPoint is created with any IP address and port 0 and then an
EndPoint is created by casting the new IPEndPoint as an EndPoint. Then,
after setting the receive timeout (no problem), the Socket ReceiveFrom
method is called using the new EndPoint, to get the server's response.

When the Socket was created it was set with the server's address and port,
why is a new IPEndPoint created to receive? Why is it set to any IP address
and port 0? To my mind this says get any data at all coming in from the net
to this computer, how does it even distinguish between the response from the
server and any random incoming internet traffic?

I'm thoroughly confused by this. Any help will be appreciated. Thanks.
 
P

Peter Duniho

Poster said:
[...]
The code works perfectly there's just one element I don't understand.
Firstly I know Socket should be in a 'using' clause, I've removed that
and error checking so that the code I've posted is just the barebones.

You don't necessarily need a "using" statement. It depends on how the
socket will be used. Usually, in fact, the lifetime of a socket
instance is greater than just a particular method, and so instead of a
"using" statement, you'll have to explicitly call Close() or Dispose()
when you're done with it.
So first of all the server's IP address and port are combined into an
IPEndPoint - no problem.

Then the Socket is created using that IPEndPoint, a timeout added and
the data is sent using SendTo - no problem.

Note: a send timeout on a UDP socket probably doesn't make much sense.
UDP is more of a "fire and forget" type of protocol, and it's unlikely
the timeout will ever become relevant. The datagram will get buffered
right away, and thus your call to SendTo() will return right away.
Now comes the code that I don't understand:

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;

A new IPEndPoint is created with any IP address and port 0
True.

and then an
EndPoint is created by casting the new IPEndPoint as an EndPoint.

Not true. No new instance of EndPoint is created. The code simply
changes the (or rather, adds a new) variable type used.
Then,
after setting the receive timeout (no problem), the Socket ReceiveFrom
method is called using the new EndPoint, to get the server's response.

When the Socket was created it was set with the server's address and
port, why is a new IPEndPoint created to receive?

Because, that's the way the ReceiveFrom() method is designed. It has a
"ref" parameter, used for input and output. Personally, I don't know
why they require the parameter to be non-null, but they do. So you have
to initialize it with something.
Why is it set to any
IP address and port 0? To my mind this says get any data at all coming
in from the net to this computer, how does it even distinguish between
the response from the server and any random incoming internet traffic?

It's up to the code that calls ReceiveFrom() to do that. By inspecting
the new IPEndPoint returned by the ReceiveFrom() method, it can
determine whether it was a sender it expected to receive from, if that's
important at all (often it wouldn't be...whatever protocol you're using
would have some internal identity verification/encryption to protect
against spoofing, misdirected traffic, etc.).

Alternatively, my recollection is that you can in fact provide a
"non-any" address for the endpoint argument, and that will be used to
filter incoming datagrams, receiving only those that arrive from the
specified address (for sure, you can do this by calling Connect() on a
connectionless socket).

In fact, it's that behavior that is why you must initialize the argument
to the "any" address, so that no filtering is done. (Again, I would
have made a null input equivalent to that, but no one asked me :) ).

Pete
 
P

Poster Matt

Firstly thanks so much Pete for taking the time to explain this so clearly
for me. I now understand what is going on (pretty much). I've put some
responses and something I'd like clarified below.

Peter said:
Poster said:
[...]
The code works perfectly there's just one element I don't understand.
Firstly I know Socket should be in a 'using' clause, I've removed that
and error checking so that the code I've posted is just the barebones.

You don't necessarily need a "using" statement. It depends on how the
socket will be used. Usually, in fact, the lifetime of a socket
instance is greater than just a particular method, and so instead of a
"using" statement, you'll have to explicitly call Close() or Dispose()
when you're done with it.

OK. In this case the socket's life is not beyond the method it's declared
in, so I'll stick with using 'using' :) as it's quite an elegant way of
handling it, but for future reference I've taken your point.

Note: a send timeout on a UDP socket probably doesn't make much sense.
UDP is more of a "fire and forget" type of protocol, and it's unlikely
the timeout will ever become relevant. The datagram will get buffered
right away, and thus your call to SendTo() will return right away.

Yes you are right, I tested it. It works fine with RecieveFrom() so I'll
skip the SendTo() timeout and just keep the ReceiveFrom() one.

Not true. No new instance of EndPoint is created. The code simply
changes the (or rather, adds a new) variable type used.

Yes, apologies, I understood that and just badly worded what I wrote.

Because, that's the way the ReceiveFrom() method is designed. It has a
"ref" parameter, used for input and output. Personally, I don't know
why they require the parameter to be non-null, but they do. So you have
to initialize it with something.


It's up to the code that calls ReceiveFrom() to do that. By inspecting
the new IPEndPoint returned by the ReceiveFrom() method, it can
determine whether it was a sender it expected to receive from, if that's
important at all (often it wouldn't be...whatever protocol you're using
would have some internal identity verification/encryption to protect
against spoofing, misdirected traffic, etc.).

Alternatively, my recollection is that you can in fact provide a
"non-any" address for the endpoint argument, and that will be used to
filter incoming datagrams, receiving only those that arrive from the
specified address (for sure, you can do this by calling Connect() on a
connectionless socket).

In fact, it's that behavior that is why you must initialize the argument
to the "any" address, so that no filtering is done. (Again, I would
have made a null input equivalent to that, but no one asked me :) ).

This is the bit I need clarified. Can I check I've got it right? Here's the
code:

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
socket.ReceiveFrom(messageReceive, ref tempRemoteEP);

The EndPoint by being set to any IP address and any port (specified by
passing it 0) tells ReceiveFrom() to accept traffic over the socket from any
IP address and any port, and after RecieveFrom() has returned the referenced
EndPoint will hold the details of the IP address and port that it did in
fact receive the data from.

Had I wanted to limit the IP address and port ReceiveFrom() could receive
from then I could set that in the EndPoint prior to calling ReceiveFrom().

That being the case, and the fact that I know I want to receive data from
the same server that I am sending to, wouldn't the following code be better
design? (I just tested to code and it works).

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPEndPoint ipEndPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);
EndPoint endPoint = (EndPoint) ipEndPoint;

Socket socket = new Socket(EPhost.Address.AddressFamily, SocketType.Dgram,
ProtocolType.Udp);

socket.SendTo(messageSend, ipEndPoint);
socket.ReceiveFrom(messageReceive, ref endPoint);

Is there any reason you can think of why I shouldn't change my source code
to the above?

Many thanks again Pete. Hope I'm not boring you or being tedious by asking
for more help on this. Obviously I'm a Socket newbie. :)

Cheers,

Matt
 
P

Peter Duniho

Poster said:
[...]
IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPEndPoint ipEndPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);
EndPoint endPoint = (EndPoint) ipEndPoint;

Socket socket = new Socket(EPhost.Address.AddressFamily,
SocketType.Dgram, ProtocolType.Udp);

socket.SendTo(messageSend, ipEndPoint);
socket.ReceiveFrom(messageReceive, ref endPoint);

Is there any reason you can think of why I shouldn't change my source
code to the above?

No, not really. I can hypothesize a situation where for some reason the
server response comes back from a different IP address than the one to
which you send the initial request, but in general it really _shouldn't_
do that and so writing code that wouldn't handle that case should be fine.

If for some reason you find yourself in a special-case situation where
the response _could_ come back from a different IP address, then of
course you would need to use a broader address for the ReceiveFrom()
(e.g. the "any" address).

One thing I note in the code is that it's not really clear why two
different variables are being declared. The "endPoint" variable should
be sufficient for both SendTo() and ReceiveFrom(). I don't see where
the "ipEndPoint" variable has been used in a way that the "endPoint"
variable could not be instead.

And of course, there is no need to cast an instance of IPEndPoint to
EndPoint. A simple assignment is sufficient.

Pete
 
P

Poster Matt

Thanks very much again Pete. Comments below...

Peter said:
Poster said:
[...]
IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPEndPoint ipEndPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);
EndPoint endPoint = (EndPoint) ipEndPoint;

Socket socket = new Socket(EPhost.Address.AddressFamily,
SocketType.Dgram, ProtocolType.Udp);

socket.SendTo(messageSend, ipEndPoint);
socket.ReceiveFrom(messageReceive, ref endPoint);

Is there any reason you can think of why I shouldn't change my source
code to the above?

No, not really. I can hypothesize a situation where for some reason the
server response comes back from a different IP address than the one to
which you send the initial request, but in general it really _shouldn't_
do that and so writing code that wouldn't handle that case should be fine.

Okay, thanks. I'll change the code as I think it's clearer and won't leave
other people scratching their heads like I've been doing.

One thing I note in the code is that it's not really clear why two
different variables are being declared. The "endPoint" variable should
be sufficient for both SendTo() and ReceiveFrom(). I don't see where
the "ipEndPoint" variable has been used in a way that the "endPoint"
variable could not be instead.

I'll replace the ipEndPoint in SendTo() with endPoint. Of course you can't
create an instance of EndPoint, it's an abstract class, you need to create
an instance of IPEndPoint so it can be assigned to an EndPoint variable,
that's why the "two different variables are being declared".
And of course, there is no need to cast an instance of IPEndPoint to
EndPoint. A simple assignment is sufficient.

Oh yes, good point, I missed that.

Many, many thanks for your help, I really appreciate it and now understand
what's going on.

Cheers,

Matt
 
P

Peter Duniho

Poster said:
[...]
I'll replace the ipEndPoint in SendTo() with endPoint. Of course you
can't create an instance of EndPoint, it's an abstract class, you need
to create an instance of IPEndPoint so it can be assigned to an EndPoint
variable, that's why the "two different variables are being declared".

No, that's not why the two variables are being declared. Or rather, if
that was in fact the reasoning of the original author, it wasn't a valid
reason.

You don't need the first ("ipEndPoint") just to create an instance of
IPEndPoint:

EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Works just fine.
 
P

Poster Matt

Peter said:
Poster said:
[...]
I'll replace the ipEndPoint in SendTo() with endPoint. Of course you
can't create an instance of EndPoint, it's an abstract class, you need
to create an instance of IPEndPoint so it can be assigned to an
EndPoint variable, that's why the "two different variables are being
declared".

No, that's not why the two variables are being declared. Or rather, if
that was in fact the reasoning of the original author, it wasn't a valid
reason.

You don't need the first ("ipEndPoint") just to create an instance of
IPEndPoint:

EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Works just fine.

Ahh yes but that doesn't work with the Socket creation (as it was anyway):

Socket socket = new Socket(ipEndPoint.Address.AddressFamily,
SocketType.Dgram, ProtocolType.Udp);

ipEndPoint.Address.AddressFamily is not liked by the compiler if ipEndPoint
is an EndPoint, so to speak.

However you can re-code it like this:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPAddress[] ipAddresses = ipHostEntry.AddressList;
IPAddress ipAddress = ipAddresses[0];
EndPoint endPoint = new IPEndPoint(ipAddress, port);

Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Dgram,
ProtocolType.Udp);
socket.SendTo(message, endPoint);
socket.ReceiveFrom(message, ref endPoint);

How's that? Does it get your seal of approval? :)

Thanks again.
 
P

Peter Duniho

Poster said:
[...]
You don't need the first ("ipEndPoint") just to create an instance of
IPEndPoint:

EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Works just fine.

Ahh yes but that doesn't work with the Socket creation (as it was anyway):

Socket socket = new Socket(ipEndPoint.Address.AddressFamily,
SocketType.Dgram, ProtocolType.Udp);

ipEndPoint.Address.AddressFamily is not liked by the compiler if
ipEndPoint is an EndPoint, so to speak.

If you're going to hard-code the socket type by, for example, using the
IPEndPoint type, you might as well hard-code the AddressFamily value too:

Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);

I don't see any value, in this example, of pulling the AddressFamily
from the endpoint type, whatever you use.

That said, since you can code this instead:

Socket socket = new Socket(endPoint.AddressFamily, ...);

....it's a moot point anyway. There's still no need to have a variable
typed as IPEndPoint.
However you can re-code it like this:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPAddress[] ipAddresses = ipHostEntry.AddressList;
IPAddress ipAddress = ipAddresses[0];
EndPoint endPoint = new IPEndPoint(ipAddress, port);

Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Dgram,
ProtocolType.Udp);
socket.SendTo(message, endPoint);
socket.ReceiveFrom(message, ref endPoint);

How's that? Does it get your seal of approval? :)

Well, no...I wouldn't bother with the "ipAddress" variable.

That said, it's not my code...if you feel the code is superior when
written that way, you should write it that way. My point is simply that
from a language point of view, there's no _need_ to write it that way.

Pete
 
P

Poster Matt

Peter said:
Poster Matt wrote:

If you're going to hard-code the socket type by, for example, using the
IPEndPoint type, you might as well hard-code the AddressFamily value too:

Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);

I didn't realize that it could be coded like that, I should have looked more
closely.

However you can re-code it like this:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
IPAddress[] ipAddresses = ipHostEntry.AddressList;
IPAddress ipAddress = ipAddresses[0];
EndPoint endPoint = new IPEndPoint(ipAddress, port);

Socket socket = new Socket(ipAddress.AddressFamily, SocketType.Dgram,
ProtocolType.Udp);
socket.SendTo(message, endPoint);
socket.ReceiveFrom(message, ref endPoint);

How's that? Does it get your seal of approval? :)

Well, no...I wouldn't bother with the "ipAddress" variable.

That said, it's not my code...if you feel the code is superior when
written that way, you should write it that way. My point is simply that
from a language point of view, there's no _need_ to write it that way.

Yes, I know the 'ipAddress' is not necessary, but my personal coding style
tends to put the extra variable in to make things clear and readable.

Since this thread has gone on way further than necessary, I don't feel I can
let it end without you finally saying "Yes, I approve your socket code". :)
I hope this does the trick:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
socket.SendTo(messageOut, endPoint);
socket.ReceiveFrom(messageIn, ref endPoint);

What do you think?

Regards,

Matt
 
P

Peter Duniho

Poster said:
[...]
Since this thread has gone on way further than necessary, I don't feel I
can let it end without you finally saying "Yes, I approve your socket
code". :)

lol...well, as long as you understand that seeking my approval is not
necessarily the most productive approach in the long run. I try my best
to be a good role model, but I've seen better. :)
I hope this does the trick:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
socket.SendTo(messageOut, endPoint);
socket.ReceiveFrom(messageIn, ref endPoint);

What do you think?

Looks fine. I won't mention naming conventions, lest we carry this on
even further. :)

Pete
 
P

Poster Matt

Peter said:
Poster said:
[...]
Since this thread has gone on way further than necessary, I don't feel
I can let it end without you finally saying "Yes, I approve your
socket code". :)

lol...well, as long as you understand that seeking my approval is not
necessarily the most productive approach in the long run. I try my best
to be a good role model, but I've seen better. :)
I hope this does the trick:

IPHostEntry ipHostEntry = Dns.GetHostEntry(server);
EndPoint endPoint = new IPEndPoint(ipHostEntry.AddressList[0], port);

Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
socket.SendTo(messageOut, endPoint);
socket.ReceiveFrom(messageIn, ref endPoint);

What do you think?

Looks fine. I won't mention naming conventions, lest we carry this on
even further. :)

Okay. :)

I'll change them as appropriate so they represent what they are and not the
type.

Thanks again for all your help Pete. I really appreciate it.
 

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

Top