.NET Forum / Languages / C# / March 2008
Unicast UDP Server
|
|
Thread rating:  |
O.B. - 28 Mar 2008 03:07 GMT I'm attempting to set up at UDP server in unicast mode, where 10.1.16.25 is the remote machine. Below is the error being thrown when binding the socket. What am I doing wrong?
System.Net.Sockets.SocketException "The requested address is not valid in its context"
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 75000000); // 75 MB
socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 1472);
EndPoint receiveEndPoint = new IPEndPoint( System.Net.IPAddress.Parse("10.1.16.25"), socketConfig.Port);
socket.Bind(receiveEndPoint);
Peter Duniho - 28 Mar 2008 04:07 GMT > I'm attempting to set up at UDP server in unicast mode, where > 10.1.16.25 is the remote machine. Below is the error being thrown > when binding the socket. What am I doing wrong? You're trying to bind your local socket to a remote address.
If you want to specify a remote address to be used as the default destination for the UDP socket, use Connect(), not Bind().
By the way...IMHO, you shouldn't be messing with the socket buffer sizes unless you have already gotten everything else working, you know exactly what you're doing, _and_ you have run into some problem that requires you to change the default buffer sizes.
Pete
O.B. - 28 Mar 2008 15:40 GMT On Mar 27, 10:07 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > I'm attempting to set up at UDP server in unicast mode, where > > 10.1.16.25 is the remote machine. Below is the error being thrown [quoted text clipped - 4 lines] > If you want to specify a remote address to be used as the default > destination for the UDP socket, use Connect(), not Bind(). But that would require the remote connection to be Bound to that port to accept connections. Here is the dilemma. We have a user that has other commercial software that is only capable of running their sockets as client UDP broadcast connections on a fixed port. On our end, the user wants the ability to open two UDP servers both on the same port, but for each one to filter based on the IP that is sending data. I was hoping that it would be possible to do this in the connection setup rather than having our asynchronous receive callback do the filtering.
With that said, what is the point of binding to a specific address if it isn't allowed?
> By the way...IMHO, you shouldn't be messing with the socket buffer sizes > unless you have already gotten everything else working, you know exactly > what you're doing, _and_ you have run into some problem that requires you > to change the default buffer sizes. We started off with defaults and lost too many packets. 75 MB ended up being a good number. Most of our machines are running with 4 GB or more of memory, so it isn't an issue.
Peter Duniho - 28 Mar 2008 18:49 GMT >> If you want to specify a remote address to be used as the default >> destination for the UDP socket, use Connect(), not Bind(). > > But that would require the remote connection to be Bound to that port > to accept connections. For you to send directly to a remote socket, it has to be bound to a known port, yes. Likewise, for you to send a broadcast to a remote socket, it has to be bound to a known port. And either case, you'd send your datagram to that known port. You can either use Connect() to specify the remote address (including the broadcast IP address and the target port), or you can explicitly provide the address each time you send.
But Bind() has nothing to do with any of that.
> Here is the dilemma. We have a user that has > other commercial software that is only capable of running their > sockets as client UDP broadcast connections on a fixed port. There's no such thing as a "UDP connection". I'll just ignore the word "connection" there.
As far as receiving broadcasts goes...if your clients are for some reason required to listen for broadcasts only on a specific port, then you'll have to broadcast on that port.
I don't really understand your statement that the software "that is only capable of running their sockets as client UDP broadcast". If a socket can receive a broadcast, it can also receive a datagram sent directly to it. So the stipulation that broadcasts must be involved seems odd to me.
Finally, I'm assuming this is all on the same network segment or segments connected such that the broadcasts are routed properly to the recipient.
> On our > end, the user wants the ability to open two UDP servers both on the > same port, but for each one to filter based on the IP that is sending > data. I was hoping that it would be possible to do this in the > connection setup rather than having our asynchronous receive callback > do the filtering. Again: with UDP there's no such thing as a connection. That said, you _can_ use the Connect() method to specify a remote address from which datagrams may be received, discarding datagrams sent by any other address. It doesn't create a connection, but it does do what you seem to want it to do.
Keeping in mind, however, that you cannot have two UDP sockets bound to the same local address. If you've got two different computers, or at least two different adapters on the same computer, this should be fine. But if you've got a single network adapter on a single computer, you can't reliably have two UDP servers bound to the same port (you can use the "reuse address" option, but when you do that, the OS provides no guarantess as to which socket will receive which datagram).
> With that said, what is the point of binding to a specific address if > it isn't allowed? If it weren't allowed, you're right...there wouldn't be any point in binding. But it is allowed. It just does something different than what you seem to think it does.
Bind() specifies the _local_ endpoint for the socket. It essentially labels the socket you're operating with, so that other sockets can communicate with it. If you specify an IP address, it must be a valid IP address for the local host. You may specify IPAddress.Any for the address, in which case the OS will choose an adapter (for client sockets) or listen on all adapters (for server sockets).
Connect(), on the other hand, specifies the _remote_ endpoint for the socket. If you specify an IP address, it must be a valid IP address for the _other_ end of your communications. This could in fact be your local host's IP address, but _only_ if you are communicating with another socket on the same network adapter/computer. In any case, it doesn't label anything...it simply sets up the link between the socket you're operating with and some remote socket. For a TCP socket an actual connection is created, but for a UDP socket all it does is set some internal state for the local socket that the OS then uses when you call Send() and Receive().
>> By the way...IMHO, you shouldn't be messing with the socket buffer sizes >> unless you have already gotten everything else working, you know exactly [quoted text clipped - 5 lines] > up being a good number. Most of our machines are running with 4 GB or > more of memory, so it isn't an issue. Okay. If you're running all of this on a LAN, over a gigabit network, in an extremely high-volume situation, that _might_ be reasonable. Otherwise, that's an awful lot of data to pile up without your servers being able to process it. If one or more of those descriptions don't apply to your situation, you could (and should IMHO) probably fix the issue in a more appropriate way (i.e. just fixing the code so that it's more responsive).
Pete
O.B. - 29 Mar 2008 23:18 GMT Wow, this is great. All this time I have been confusing bind and connect. Thank you for the detailed explanation.
For the record, I still have to bind to a _local_ IP address for UDP sockets in order for the receive data callback to be invoked. From what I gathered from your post, invoking "connect" with a remote address in a UDP socket will ensure that the receive callback is only invoked with data received from the specified remote address, correct?
Continued below.
>>> By the way...IMHO, you shouldn't be messing with the socket buffer sizes >>> unless you have already gotten everything else working, you know exactly [quoted text clipped - 12 lines] > issue in a more appropriate way (i.e. just fixing the code so that it's > more responsive). The code has been rewritten using unmanaged code to get a significant speed up over the original C++ implementation. Without the 75 MB receive buffer, we _occasionally_ find the buffer overfilling.
In running a profiler, it appears the code is processing the data as fast as the OS is invoking the asynchronous callback. As a test, I wrote a receive callback that only sucked data off the socket and put it into an internal buffer for processing. We were still encountering an overflow ... very odd. Maybe it is the actual NIC we're using? Or is it a limitation of Windows XP and Windows 2003 Server? This is a tough one to figure out.
Peter Duniho - 29 Mar 2008 23:58 GMT > Wow, this is great. All this time I have been confusing bind and > connect. Thank you for the detailed explanation. You're welcome.
> For the record, I still have to bind to a _local_ IP address for UDP > sockets in order for the receive data callback to be invoked. From what > I gathered from your post, invoking "connect" with a remote address in a > UDP socket will ensure that the receive callback is only invoked with > data received from the specified remote address, correct? It should, yes.
> [...] > The code has been rewritten using unmanaged code to get a significant > speed up over the original C++ implementation. Without the 75 MB > receive buffer, we _occasionally_ find the buffer overfilling. I don't understand the above comment. If you're using unmanaged code, why are you posting in the C# newsgroup? C# is dependent on the .NET runtime, and so is inherently tied to managed code. If the original implementation was C++, is the above statement meant to imply that you were using managed C++?
> In running a profiler, it appears the code is processing the data as > fast as the OS is invoking the asynchronous callback. As a test, I > wrote a receive callback that only sucked data off the socket and put it > into an internal buffer for processing. We were still encountering an > overflow ... very odd. How do you know you are encountering an overflow? Are you sure that you aren't losing datagrams due to some other reason?
> Maybe it is the actual NIC we're using? Or is it a limitation of > Windows XP and Windows 2003 Server? This is a tough one to figure out. The "U" in "UDP" might as well stand for "unreliable". UDP is inherently unreliable, and you _will_ lose datagrams eventually, no matter what you do.
On a LAN, you can avoid many of the problems that exist going over the Internet, but your code still must always be able to deal with lost datagrams. Data loss will be reduced significantly on a LAN as compared to the Internet, but it will never go away completely.
You haven't made any mention as to what your actual network speed and load is, but I'll reiterate my previous statement: only on the fastest networks, under the highest loads, should a buffer of 75MB be useful. Even at that size, that's on the order of 500 ms worth of constant data throughput on a 1 gigabit network. Or put another way, if a 75MB buffer is needed, that means that the layer of network transport that's supposed to be reading from that buffer is at times being delayed by longer than 500 ms.
On a modern computer, that's practically forever. In my opinion, you should never be seeing delays that long. If you are seeing delays that long, there's probably a better way to deal with them. Such as, preventing the delays rather than allowing them to happen and just letting the data pile up in a larger buffer.
As an example of that 500 ms really means: you'd have to be running roughly ten fully CPU-bound threads on a single-core/CPU computer in order for some other thread's execution to be delayed by that much time. And of course, running ten fully CPU-bound threads on a single-core computer is definitely not a good idea (heck, running just two fully CPU-bound threads on a single-core computer isn't a good idea).
If you're running on a more conventional network (e.g. 100 Mb/s...though I admit, 1 Gb/s is getting more and more common, I think 100 Mb/s is still more typical), and/or your data transmission rate does not saturate the network, then a 75MB buffer implies an even larger delay (for a 100 Mb/s, it's more like 5 seconds than 500 ms). And if a half-second delay is a sign of something wrong, you can imagine that an even larger delay means.
:) Pete
O.B. - 30 Mar 2008 04:59 GMT >> [...] >> The code has been rewritten using unmanaged code to get a significant [quoted text clipped - 6 lines] > implementation was C++, is the above statement meant to imply that you > were using managed C++? My apologies for the miscommunication. The socket code and most everything else is managed code. The part that is not managed is the code that convert the byte arrays to a class with explicit StructLayout structures (managing DIS EntityState PDUs).
>> In running a profiler, it appears the code is processing the data as >> fast as the OS is invoking the asynchronous callback. As a test, I [quoted text clipped - 4 lines] > How do you know you are encountering an overflow? Are you sure that you > aren't losing datagrams due to some other reason? Each time the receive callback is invoked, the code checks to see how full the buffer is. When it is close to 100%, we start noticing data not being received.
>> Maybe it is the actual NIC we're using? Or is it a limitation of >> Windows XP and Windows 2003 Server? This is a tough one to figure out. > > The "U" in "UDP" might as well stand for "unreliable". UDP is > inherently unreliable, and you _will_ lose datagrams eventually, no > matter what you do. Yes, it does happen. However, when more than 25% of the packets are getting lost, we start looking at the network for issues.
Thanks again for your help. I think we're good to go for now.
Peter Duniho - 30 Mar 2008 01:08 GMT > Each time the receive callback is invoked, the code checks to see how > full the buffer is. When it is close to 100%, we start noticing data > not being received. How do you check to see how full the buffer is?
> Yes, it does happen. However, when more than 25% of the packets are > getting lost, we start looking at the network for issues. 25% is high, yes. Still, that doesn't mean that it's simply a buffer size issue.
> Thanks again for your help. I think we're good to go for now. Somehow I suspect not. But if you're satisfied with the solution, I guess that's your perogative. Good luck.
Pete
O.B. - 31 Mar 2008 20:14 GMT On Mar 29, 7:08 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Each time the receive callback is invoked, the code checks to see how > > full the buffer is. When it is close to 100%, we start noticing data [quoted text clipped - 14 lines] > > Pete Well, doing a Socket.Connect() on a UDP socket causes a Socket.Bind() to throw a socket exception. So it is not possible to Bind on a local address in UDP *and* use Connect to specify a remote address at the same time. Oh well ... that's life.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|