2 // System.Net.Sockets.UdpClient.cs
5 // Gonzalo Paniagua Javier <gonzalo@ximian.com>
6 // Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright (C) Ximian, Inc. http://www.ximian.com
10 // Copyright 2011 Xamarin Inc.
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Threading.Tasks;
38 namespace System.Net.Sockets
40 public class UdpClient : IDisposable
42 private bool disposed = false;
43 private bool active = false;
44 private Socket socket;
45 private AddressFamily family = AddressFamily.InterNetwork;
46 private byte[] recvbuffer;
48 public UdpClient () : this(AddressFamily.InterNetwork)
52 public UdpClient(AddressFamily family)
54 if(family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6)
55 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
61 public UdpClient (int port)
63 if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
64 throw new ArgumentOutOfRangeException ("port");
66 this.family = AddressFamily.InterNetwork;
68 IPEndPoint localEP = new IPEndPoint (IPAddress.Any, port);
72 public UdpClient (IPEndPoint localEP)
75 throw new ArgumentNullException ("localEP");
77 this.family = localEP.AddressFamily;
82 public UdpClient (int port, AddressFamily family)
84 if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6)
85 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
87 if (port < IPEndPoint.MinPort ||
88 port > IPEndPoint.MaxPort) {
89 throw new ArgumentOutOfRangeException ("port");
96 if (family == AddressFamily.InterNetwork)
97 localEP = new IPEndPoint (IPAddress.Any, port);
99 localEP = new IPEndPoint (IPAddress.IPv6Any, port);
100 InitSocket (localEP);
103 public UdpClient (string hostname, int port)
105 if (hostname == null)
106 throw new ArgumentNullException ("hostname");
108 if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
109 throw new ArgumentOutOfRangeException ("port");
112 Connect (hostname, port);
115 private void InitSocket (EndPoint localEP)
122 socket = new Socket (family, SocketType.Dgram, ProtocolType.Udp);
125 socket.Bind (localEP);
128 public void AllowNatTraversal (bool allowed)
131 socket.SetIPProtectionLevel (IPProtectionLevel.Unrestricted);
133 socket.SetIPProtectionLevel (IPProtectionLevel.EdgeRestricted);
142 void DoConnect (IPEndPoint endPoint)
144 /* Catch EACCES and turn on SO_BROADCAST then,
145 * as UDP sockets don't have it set by default
148 socket.Connect (endPoint);
149 } catch (SocketException ex) {
150 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
151 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
153 socket.Connect (endPoint);
160 public void Connect (IPEndPoint endPoint)
163 if (endPoint == null)
164 throw new ArgumentNullException ("endPoint");
166 DoConnect (endPoint);
170 public void Connect (IPAddress addr, int port)
173 throw new ArgumentNullException ("addr");
175 if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
176 throw new ArgumentOutOfRangeException ("port");
179 Connect (new IPEndPoint (addr, port));
182 public void Connect (string hostname, int port)
184 if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
185 throw new ArgumentOutOfRangeException ("port");
187 IPAddress[] addresses = Dns.GetHostAddresses (hostname);
188 for(int i=0; i<addresses.Length; i++) {
190 this.family = addresses[i].AddressFamily;
191 Connect (new IPEndPoint (addresses[i], port));
193 } catch(Exception e) {
194 if(i == addresses.Length - 1){
199 /// This is the last entry, re-throw the exception
206 #region Multicast methods
207 public void DropMulticastGroup (IPAddress multicastAddr)
210 if (multicastAddr == null)
211 throw new ArgumentNullException ("multicastAddr");
213 if(family == AddressFamily.InterNetwork)
214 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.DropMembership,
215 new MulticastOption (multicastAddr));
217 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership,
218 new IPv6MulticastOption (multicastAddr));
221 public void DropMulticastGroup (IPAddress multicastAddr,
226 /* LAMESPEC: exceptions haven't been specified
229 if (multicastAddr == null) {
230 throw new ArgumentNullException ("multicastAddr");
233 /* Does this overload only apply to IPv6?
234 * Only the IPv6MulticastOption has an
235 * ifindex-using constructor. The MS docs
238 if (family == AddressFamily.InterNetworkV6) {
239 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership, new IPv6MulticastOption (multicastAddr, ifindex));
243 public void JoinMulticastGroup (IPAddress multicastAddr)
247 if (multicastAddr == null)
248 throw new ArgumentNullException ("multicastAddr");
250 if(family == AddressFamily.InterNetwork)
251 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership,
252 new MulticastOption (multicastAddr));
254 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership,
255 new IPv6MulticastOption (multicastAddr));
258 public void JoinMulticastGroup (int ifindex,
259 IPAddress multicastAddr)
263 if (multicastAddr == null)
264 throw new ArgumentNullException ("multicastAddr");
266 if (family == AddressFamily.InterNetworkV6)
267 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption (multicastAddr, ifindex));
269 throw new SocketException ((int) SocketError.OperationNotSupported);
272 public void JoinMulticastGroup (IPAddress multicastAddr, int timeToLive)
275 if (multicastAddr == null)
276 throw new ArgumentNullException ("multicastAddr");
277 if (timeToLive < 0 || timeToLive > 255)
278 throw new ArgumentOutOfRangeException ("timeToLive");
280 JoinMulticastGroup (multicastAddr);
281 if(family == AddressFamily.InterNetwork)
282 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive,
285 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastTimeToLive,
289 public void JoinMulticastGroup (IPAddress multicastAddr,
290 IPAddress localAddress)
294 if (family == AddressFamily.InterNetwork)
295 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption (multicastAddr, localAddress));
297 throw new SocketException ((int) SocketError.OperationNotSupported);
302 public byte [] Receive (ref IPEndPoint remoteEP)
306 byte [] recBuffer = new byte [65536]; // Max. size
307 EndPoint endPoint = (EndPoint) remoteEP;
308 int dataRead = socket.ReceiveFrom (recBuffer, ref endPoint);
309 if (dataRead < recBuffer.Length)
310 recBuffer = CutArray (recBuffer, dataRead);
312 remoteEP = (IPEndPoint) endPoint;
316 int DoSend (byte[] dgram, int bytes, IPEndPoint endPoint)
318 /* Catch EACCES and turn on SO_BROADCAST then,
319 * as UDP sockets don't have it set by default
322 if (endPoint == null) {
323 return(socket.Send (dgram, 0, bytes,
326 return(socket.SendTo (dgram, 0, bytes,
330 } catch (SocketException ex) {
331 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
332 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
333 if (endPoint == null) {
334 return(socket.Send (dgram, 0, bytes, SocketFlags.None));
336 return(socket.SendTo (dgram, 0, bytes, SocketFlags.None, endPoint));
344 public int Send (byte [] dgram, int bytes)
348 throw new ArgumentNullException ("dgram");
351 throw new InvalidOperationException ("Operation not allowed on " +
352 "non-connected sockets.");
354 return(DoSend (dgram, bytes, null));
357 public int Send (byte [] dgram, int bytes, IPEndPoint endPoint)
361 throw new ArgumentNullException ("dgram is null");
364 if (endPoint != null)
365 throw new InvalidOperationException ("Cannot send packets to an " +
366 "arbitrary host while connected.");
368 return(DoSend (dgram, bytes, null));
371 return(DoSend (dgram, bytes, endPoint));
374 public int Send (byte [] dgram, int bytes, string hostname, int port)
376 return Send (dgram, bytes,
377 new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port));
380 private byte [] CutArray (byte [] orig, int length)
382 byte [] newArray = new byte [length];
383 Buffer.BlockCopy (orig, 0, newArray, 0, length);
389 IAsyncResult DoBeginSend (byte[] datagram, int bytes,
391 AsyncCallback requestCallback,
394 /* Catch EACCES and turn on SO_BROADCAST then,
395 * as UDP sockets don't have it set by default
398 if (endPoint == null) {
399 return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
401 return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
403 } catch (SocketException ex) {
404 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
405 socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
406 if (endPoint == null) {
407 return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
409 return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
417 public IAsyncResult BeginSend (byte[] datagram, int bytes,
418 AsyncCallback requestCallback,
421 return(BeginSend (datagram, bytes, null,
422 requestCallback, state));
425 public IAsyncResult BeginSend (byte[] datagram, int bytes,
427 AsyncCallback requestCallback,
432 if (datagram == null) {
433 throw new ArgumentNullException ("datagram");
436 return(DoBeginSend (datagram, bytes, endPoint,
437 requestCallback, state));
440 public IAsyncResult BeginSend (byte[] datagram, int bytes,
441 string hostname, int port,
442 AsyncCallback requestCallback,
445 return(BeginSend (datagram, bytes, new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port), requestCallback, state));
448 public int EndSend (IAsyncResult asyncResult)
452 if (asyncResult == null) {
453 throw new ArgumentNullException ("asyncResult is a null reference");
456 return(socket.EndSend (asyncResult));
459 public IAsyncResult BeginReceive (AsyncCallback requestCallback, object state)
463 recvbuffer = new byte[8192];
467 if (family == AddressFamily.InterNetwork) {
468 ep = new IPEndPoint (IPAddress.Any, 0);
470 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
473 return(socket.BeginReceiveFrom (recvbuffer, 0, 8192,
476 requestCallback, state));
479 public byte[] EndReceive (IAsyncResult asyncResult, ref IPEndPoint remoteEP)
483 if (asyncResult == null) {
484 throw new ArgumentNullException ("asyncResult is a null reference");
489 if (family == AddressFamily.InterNetwork) {
490 ep = new IPEndPoint (IPAddress.Any, 0);
492 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
495 int bytes = socket.EndReceiveFrom (asyncResult,
497 remoteEP = (IPEndPoint)ep;
499 /* Need to copy into a new array here, because
500 * otherwise the returned array length is not
503 byte[] buf = new byte[bytes];
504 Array.Copy (recvbuffer, buf, bytes);
510 protected bool Active {
511 get { return active; }
512 set { active = value; }
515 public Socket Client {
516 get { return socket; }
517 set { socket = value; }
523 return(socket.Available);
527 public bool DontFragment
530 return(socket.DontFragment);
533 socket.DontFragment = value;
537 public bool EnableBroadcast
540 return(socket.EnableBroadcast);
543 socket.EnableBroadcast = value;
547 public bool ExclusiveAddressUse
550 return(socket.ExclusiveAddressUse);
553 socket.ExclusiveAddressUse = value;
557 public bool MulticastLoopback
560 return(socket.MulticastLoopback);
563 socket.MulticastLoopback = value;
579 public void Dispose ()
582 GC.SuppressFinalize (this);
585 protected virtual void Dispose (bool disposing)
604 private void CheckDisposed ()
607 throw new ObjectDisposedException (GetType().FullName);
612 public Task<UdpReceiveResult> ReceiveAsync ()
614 return Task<UdpReceiveResult>.Factory.FromAsync (BeginReceive, r => {
615 IPEndPoint remoteEndPoint = null;
616 return new UdpReceiveResult (EndReceive (r, ref remoteEndPoint), remoteEndPoint);
620 public Task<int> SendAsync (byte[] datagram, int bytes)
622 return Task<int>.Factory.FromAsync (BeginSend, EndSend, datagram, bytes, null);
625 public Task<int> SendAsync (byte[] datagram, int bytes, IPEndPoint endPoint)
627 return Task<int>.Factory.FromAsync (BeginSend, EndSend, datagram, bytes, endPoint, null);
630 public Task<int> SendAsync (byte[] datagram, int bytes, string hostname, int port)
632 var t = Tuple.Create (datagram, bytes, hostname, port, this);
634 return Task<int>.Factory.FromAsync ((callback, state) => {
635 var d = (Tuple<byte[], int, string, int, UdpClient>) state;
636 return d.Item5.BeginSend (d.Item1, d.Item2, d.Item3, d.Item4, callback, null);